CCL Home Page
Up Directory CCL ScianColors
/*ScianColors.c
  Color management routines for scian
  Eric Pepke
  March 15, 1990
*/

/*
  This file is patched to fix some of the problems with the way SciAn version 
  0.42 allocates colors and figures out how to deal with the bitplanes.  It 
  also enhances SciAn to recognize several environment variables:

  SCIAN_VETO_RGB
    If in the environment, causes SciAn to veto RGB mode regardless of the
    machine configuration.
  SCIAN_FORCE_RGB
    If in the environment, causes SciAn to force RGB mode regardless of the
    machine configuration.
  SCIAN_VETO_CMAP
    If in the environment, causes SciAn to veto color map mode regardless of 
    the machine configuration.
  SCIAN_FORCE_CMAP
    If in the environment, causes SciAn to force color map mode regardless of 
    the machine configuration.
  SCIAN_VETO_DOUBLE
    If in the environment, causes SciAn to veto double buffering regardless of 
    the machine configuration.
  SCIAN_FORCE_DOUBLE
    If in the environment, causes SciAn to force double buffering regardless of 
    the machine configuration.
  SCIAN_COLOR_BEG
    If in the environment with an ASCII number value, causes SciAn to use the
    given number as the first color in SciAn's portion of the color map.
  SCIAN_N_COLORS
    If in the environment with an ASCII number value, causes SciAn to use the
    given number as the number of colors allocated.
*/


#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianArrays.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianButtons.h"
#include "ScianColors.h"
#include "ScianEvents.h"
#include "ScianErrors.h"
#include "ScianScripts.h"
#include "ScianPictures.h"
#include "ScianControls.h"
#include "ScianDialogs.h"
#include "ScianSliders.h"
#include "ScianDatasets.h"
#include "ScianStyle.h"
#include "ScianTitleBoxes.h"
#include "ScianTextBoxes.h"
#include "ScianLists.h"
#include "ScianMethods.h"
#include "ScianDraw.h"
#include "ScianIcons.h"

#ifndef GL4D
#include "gamtables.h"
#endif

#ifdef GL4D
/*No gamma correction on the 4d*/
#define CVAL(k) k
#else
/*3000's need gammacorrection*/
#define CVAL(k) LinToGamma[k]
#endif

/*Simple palette functions*/
#define PF_RAMP		1		/*Interpolate a ramp*/
#define PF_REVERSE	2		/*Reverse range of colors*/
#define PF_RUFFLE	3		/*Ruffle range of colors*/
#define PF_SMOOTH	4		/*Smooth range of colors*/
#define PF_SHARPEN	5		/*Sharpen range of colors*/

/*Color models*/
#define CM_RGB		0		/*RGB color model*/
#define CM_YIQ		1
#define CM_HSV		2
#define CM_HLS		3
#define NCOLORMODELS	4

/*Palette tools*/
#define PT_FREEFORM	0		/*Free form drawing*/
#define PT_SINE		1		/*Sine function*/
#define PT_TRIANGLE	2		/*Triangle function*/
#define PT_SAWTOOTH	3		/*Sawtooth function*/
#define PT_TOOTHSAW	4		/*Toothsaw function*/
#define PT_SQUARE	5		/*Square wave function*/

char *toolNames[6] = 
    {
	"free form",
	"sine wave",
	"triangle wave",
	"sawtooth wave",
	"reverse sawtooth wave",
	"square wave"
    };

char *componentNames[NCOLORMODELS][3] =
    {
	{"red", "green", "blue"},
	{"\"Y\"", "\"I\"", "\"Q\""},
	{"hue", "saturation", "value"},
	{"hue", "lightness", "saturation"}
    };

char *shortComponentNames[NCOLORMODELS][3] =
    {
	{"R", "G", "B"},
	{"Y", "I", "Q"},
	{"H", "S", "V"},
	{"H", "L", "S"}
    };

real RGB2YIQMat[3][3] =
    {{ 0.30,  0.59,  0.11},
     { 0.60, -0.28, -0.32},
     { 0.21, -0.52,  0.31}};

real YIQ2RGBMat[3][3] = 
    {{ 1.0000,  0.9483,  0.6240},
     { 1.0000, -0.2761, -0.6398},
     { 1.0000, -1.1055,  1.7299}};

Bool hasRGB;			/*True iff has RGB*/
Bool hasCmap;			/*True iff has color map mode*/
Bool hasDouble;			/*True iff has double buffer mode*/
Bool hasZbuf;			/*True iff has z buffer mode*/
Bool hasTransparency;		/*True iff has transparency*/

int paletteSerialNum = 0;		/*Serial num for palettes*/

static int ColorToPixel(ObjPtr, int);
static int PixelToColor(ObjPtr, int);
 
typedef struct cr
    {
	struct cr *next;	/*Next color range*/
	int beg, end;		/*Beginning and end (+ 1)*/
    } ColorRange;

ColorRange *colorRanges;	/*The available color ranges*/

PPtr activePalettes = 0;	/*The active palettes*/

/*Start of UI colors*/
int uiColorBeg;

#define COLORWHEELBORDER 	6	/*Border around HS control*/
#define COLORWHEELSPOTSIZE	4	/*Size of spot in HS control*/

short uiColors[NUICOLORS][3];		/*Stuff for the UI colors*/

PPtr curPalette;			/*Current palette*/
int curNColors;				/*Current number of colors*/
real curMin, curMax;			/*Current min and max values*/
int curBeg;
real diffMult;				/*Amount to multiply to get difference*/
short3 *curColors;

ObjPtr paletteClass;			/*Class for a color palette*/
ObjPtr paletteDisplayClass;		/*Class for a palette display*/
ObjPtr colorControlClass;
ObjPtr colorWheelClass;
ObjPtr colorBarClass;
#define COLORFAILTIMEOUT	60	/*Color failure timeout in seconds*/
long colorFailTime = 0;			/*Time of last color failure*/

#define COLORWHEELHEIGHT	4	/*Height of a color wheel*/

#ifdef PROTO
static void SetColorComponent(PPtr palette, int whichColor, int comp, int cc);
static int GetColorComponent(PPtr palette, int whichColor, int comp);
#endif

#ifdef PROTO
void MapPalette(ObjPtr p)
#else
void MapPalette(p)
ObjPtr p;
#endif
/*Maps palette p*/

{
#ifdef GRAPHICS
    int k;
    if (hasCmap == false) return;
    if (((PPtr) p) -> beg < 0) return;
    for (k = 0; k < ((PPtr) p) -> nColors; ++k)
    {
	mapcolor(((PPtr) p) -> beg + k,
		 ((PPtr) p) -> colors[k][0],
		 ((PPtr) p) -> colors[k][1],
		 ((PPtr) p) -> colors[k][2]);
	TinyDelay();
    }
#endif
}

#ifdef PROTO
void MapPaletteColors(PPtr p, int start, int end)
#else
void MapPaletteColors(p, start, end)
PPtr p; int start; int end;
#endif
/*Maps color from start to end in palette p*/
{
#ifdef GRAPHICS
    int k;
    if (hasCmap == false) return;
    if (((PPtr) p) -> beg < 0) return;
    for (k = start; k < MAX(((PPtr) p) -> nColors, end + 1); ++k)
    {
	mapcolor(((PPtr) p) -> beg + k,
		 ((PPtr) p) -> colors[k][0],
		 ((PPtr) p) -> colors[k][1],
		 ((PPtr) p) -> colors[k][2]);
	TinyDelay();
    }
#endif
}

#ifdef PROTO
void CopyColorsToComponents(PPtr p, int start, int finish)
#else
void CopyColorsToComponents(p, start, finish)
PPtr p; int start; int finish;
#endif
/*Copies the colors to components based on color model*/
{
    ObjPtr var;
    int cm, k;
    short3 *colors, *components;
    real r, g, b, h, s, v, l, y, i, q;

    var = GetIntVar("CopyColorsToComponents", (ObjPtr) p, COLORMODEL);
    if (var)
    {
	cm = GetInt(var);
    }
    else
    {
	cm = CM_RGB;
    }

    colors = p -> colors;
    components = p -> components;

    for (k = start; k <= finish; ++k)
    {
	switch (cm)
	{
	    case CM_RGB:
		components[k][0] = colors[k][0];
		components[k][1] = colors[k][1];
		components[k][2] = colors[k][2];
		break;
	    case CM_HSV:
		r = ((real) colors[k][0]) / 255.0;
		g = ((real) colors[k][1]) / 255.0;
		b = ((real) colors[k][2]) / 255.0;

		RGB2HSV(&h, &s, &v, r, g, b);

		components[k][0] = h * 255.0 + 0.5;
		components[k][1] = s * 255.0 + 0.5;
		components[k][2] = v * 255.0 + 0.5;
		break;
	    case CM_HLS:
		r = ((real) colors[k][0]) / 255.0;
		g = ((real) colors[k][1]) / 255.0;
		b = ((real) colors[k][2]) / 255.0;

		RGB2HLS(&h, &l, &s, r, g, b);

		components[k][0] = h * 255.0 + 0.5;
		components[k][1] = l * 255.0 + 0.5;
		components[k][2] = s * 255.0 + 0.5;
		break;
	    case CM_YIQ:
		r = ((real) colors[k][0]) / 255.0;
		g = ((real) colors[k][1]) / 255.0;
		b = ((real) colors[k][2]) / 255.0;

		RGB2YIQ(&y, &i, &q, r, g, b);

		components[k][0] = y * 255.0 + 0.5;
		components[k][1] = i * 255.0 + 0.5;
		components[k][2] = q * 255.0 + 0.5;
		break;
	    default:
		components[k][0] = colors[k][0];
		components[k][1] = colors[k][1];
		components[k][2] = colors[k][2];
		break;
	}
    }
}

#ifdef PROTO
void CopyComponentsToColors(PPtr p, int start, int finish)
#else
void CopyComponentsToColors(p, start, finish)
PPtr p; int start; int finish;
#endif
/*Copies the colors to components based on color model*/
{
    ObjPtr var;
    int cm, k;
    short3 *colors, *components;
    real r, g, b, h, s, v, l, y, i, q; 

    var = GetIntVar("CopyComponentsToColors", (ObjPtr) p, COLORMODEL);
    if (var)
    {
	cm = GetInt(var);
    }
    else
    {
	cm = CM_RGB;
    }

    colors = p -> colors;
    components = p -> components;

    for (k = start; k <= finish; ++k)
    {
	switch (cm)
	{
	    case CM_RGB:
		colors[k][0] = components[k][0];
		colors[k][1] = components[k][1];
		colors[k][2] = components[k][2];
		break;
	    case CM_HSV:
		h = ((real) components[k][0]) / 255.0;
		s = ((real) components[k][1]) / 255.0;
		v = ((real) components[k][2]) / 255.0;

		HSV2RGB(&r, &g, &b, h, s, v);

		colors[k][0] = r * 255.0 + 0.5;
		colors[k][1] = g * 255.0 + 0.5;
		colors[k][2] = b * 255.0 + 0.5;
		break;
	    case CM_HLS:
		h = ((real) components[k][0]) / 255.0;
		l = ((real) components[k][1]) / 255.0;
		s = ((real) components[k][2]) / 255.0;

		HLS2RGB(&r, &g, &b, h, l, s);

		colors[k][0] = r * 255.0 + 0.5;
		colors[k][1] = g * 255.0 + 0.5;
		colors[k][2] = b * 255.0 + 0.5;
		break;
	    case CM_YIQ:
		y = ((real) components[k][0]) / 255.0;
		i = ((real) components[k][1]) / 255.0;
		q = ((real) components[k][2]) / 255.0;

		YIQ2RGB(&r, &g, &b, y, i, q);

		colors[k][0] = r * 255.0 + 0.5;
		colors[k][1] = g * 255.0 + 0.5;
		colors[k][2] = b * 255.0 + 0.5;
		break;
	    default:
		components[k][0] = colors[k][0];
		components[k][1] = colors[k][1];
		components[k][2] = colors[k][2];
		break;
	}
    }
}

void CopyColorsToPalette(p, colors)
ObjPtr p;
short3 *colors;
/*Copies the colors into palette p*/
{
    int k;
    for (k = 0; k < ((PPtr) p) -> nColors; ++k)
    {
	((PPtr) p) -> colors[k][0] = colors[k][0];
	((PPtr) p) -> colors[k][1] = colors[k][1];
	((PPtr) p) -> colors[k][2] = colors[k][2];
    }
    CopyColorsToComponents((PPtr) p, 0, ((PPtr) p) -> nColors - 1);
}

ObjPtr MakePaletteName(palette)
ObjPtr palette;
/*Makes a name for a palette*/
{
    if (GetVar(palette, NAME))
    {
	return ObjFalse;
    }
    else
    {
	sprintf(tempStr, "Palette %ld", ++paletteSerialNum);
	SetVar(palette, NAME, NewString(tempStr));
	return ObjTrue;
    }
}

void AlertColorFailure()
/*Alerts the user that a color allocation has failed*/
{
    WinInfoPtr errWindow;

    errWindow = AlertUser(UIRED, (WinInfoPtr) 0,
	"SciAn has run out of color table entries.  It will continue to try to find enough \
colors to run normally.  Until it does, you may see some unusual colors in \
some windows.",
        0, 0, "Oh well");
    SetVar((ObjPtr) errWindow, HELPSTRING,
        NewString("All the windows of SciAn, as well as the windows of other \
processes, share the same color table.  When there are several visualizations \
on the screen, SciAn must share the color table between windows.  It tries to \
allocate and deallocate chunks of the color table according to need, but \
sometimes the number of visualizations grows so large that there are simply not \
enough colors to go around.\n\
\n\
SciAn will continue to try to allocate colors.  You can reduce the number of \
colors you are using by a number of ways.  If your computer can do 24-bit RGB, \
you can set some of the visualization windows to Full Color by clicking on the \
Full Color button at the bottom right of the visualization window.  You can close \
visualization windows that you are not using.  You can color some of the \
visualizations white or increase visualizations colored by datasets to full \
brightness."));
}

void SetPalette(p)
ObjPtr p;
/*Sets the current palette to p*/
{
    PPtr tp;
    tp = (PPtr) p;

    curPalette = tp;
    curNColors = tp -> nColors;
    curMin = tp -> min;
    curMax = tp -> max;

    if ((tp -> beg < 0) && (false == rgbp))
    {
	/*Allocate some colors on demand*/
	tp -> beg = AllocColors(curNColors);
	if (tp -> beg >= 0)
	{
	    /*Allocation succeeded*/
	    MapPalette(p);
	    curBeg = tp -> beg + 2;
	}
	else
	{
	    /*Allocation failed*/
	    struct tms buffer;
	    long curTime;

	    curTime = times(&buffer) / HZ;

	    if (curTime > colorFailTime + COLORFAILTIMEOUT)
	    {
		colorFailTime = curTime;
		DoUniqueTask(AlertColorFailure);
	    }
	    curNColors = 7;
	    curBeg = uiColorBeg + 2;
	}
    }
    else
    {
	curBeg = tp -> beg + 2;
    }

    curColors = tp -> colors + 2;

    diffMult = ((real) curNColors - 4) / (curMax - curMin);
}

#ifdef PROTO
void SetPaletteMinMax(ObjPtr p, real min, real max)
#else
void SetPaletteMinMax(p, min, max)
ObjPtr p;
real min, max;
#endif
/*Sets the min and max of palette p to min and max*/
{
    ((PPtr) p) -> min = min;
    ((PPtr) p) -> max = max;
    SetVar(p, CHANGED, ObjTrue);
}

static void CompactColors()
/*Compacts all the colors*/
{
    ColorRange *runner;

    if (colorRanges == 0)
    {
	ReportError("CompactColors", "No color ranges");
	return;
    }

    runner = colorRanges;
    while (runner && runner -> next)
    {
	if (runner -> end >= runner -> next -> beg)
	{
	    /*Compact two adjacent colors*/
	    ColorRange *next;

	    runner -> end = runner -> next -> end;
	    next = runner -> next -> next;
	    free(runner -> next);
	    runner -> next = next;
	}
	else
	{
	    runner = runner -> next;
	}
    }
}

static int AllocColors(n)
int n;
/*Allocates n contiguous colors, returns their start or -1*/
{
    ColorRange **runner, **bestFit;
    int fit;			/*Fit of best fit so far*/

    if (colorRanges == 0)
    {
	ReportError("AllocColors", "No color ranges");
	return -1;
    }

    /*Compact the colors*/
    CompactColors();

    /*Search for best fit*/
    fit = 32767;
    bestFit = (ColorRange **) 0;
    runner = &colorRanges;

    while (*runner)
    {
	int nAvailable;
	nAvailable = (*runner) -> end - (*runner) -> beg;
	if (nAvailable >= n)
	{
	    if (nAvailable - n < fit)
	    {
		fit = nAvailable - n;
		bestFit = runner;
	    }
	}
	runner = &((*runner) -> next);
    }

    if (bestFit)
    {
	int retVal;
	retVal = (*bestFit) -> beg;

	if (fit == 0)
	{
	    /*Must delete this fit*/
	    ColorRange *thisFit;
	    thisFit = (*bestFit);
	    free(thisFit);
	    (*bestFit) = (*bestFit) -> next;
	}
	else
	{
	    /*Just need to adjust it*/
	    (*bestFit) -> beg += n;
	}
	return retVal;
    }
    else
    {
	/*Allocation failed*/
	return -1;
    }
}

static void FreeColors(start, n)
int start, n;
/*Frees n contiguous colors at start*/
{
    ColorRange **runner, *next;

    if (colorRanges == 0)
    {
	ReportError("FreeColors", "No color ranges");
	return;
    }

    /*Add a new color range at the appropriate point*/
    runner = &colorRanges;

    while (*runner && (*runner) -> beg < start + n)
    {
	runner = &((*runner) -> next);
    }
    next = *runner;
    *runner = new(ColorRange);
    (*runner) -> beg = start;
    (*runner) -> end = start + n;
    (*runner) -> next = next;
}

static void MakeUIColor(whichColor, r, g, b)
int whichColor;
short r, g, b;
/*Makes UI color whichColor to r, g, b*/
{
#ifdef GRAPHICS
    if (hasCmap)
    {
	mapcolor(uiColorBeg + whichColor, CVAL(r), CVAL(g), CVAL(b));
    }
    uiColors[whichColor][0] = CVAL(r);
    uiColors[whichColor][1] = CVAL(g);
    uiColors[whichColor][2] = CVAL(b);
    TinyDelay();
#endif
}

#ifdef PROTO
int ClosestUIColor(float clr[3])
#else
int ClosestUIColor(clr)
float clr[3];
#endif
/*Returns the closest UI color to clr*/
{
    int k;
    short r, g, b;
    short bestError;
    short best;
    r = clr[0] * 256.0;
    g = clr[1] * 256.0;
    b = clr[2] * 256.0;

    bestError = 5000;	/*Big*/
    for (k = 0; k < NUICOLORS; ++k)
    {
	short error;
	error = ABS(uiColors[k][0] - r) +
	        ABS(uiColors[k][1] - g) +
	        ABS(uiColors[k][2] - b);
	if (error < bestError)
	{
	    bestError = error;
	    best = k;
	}
    }
    return best + uiColorBeg;
}

static ObjPtr DrawColorWheel(object)
ObjPtr object;
/*Draws an HS control*/
{
#ifdef GRAPHICS
    int left, right, bottom, top;
    int cx, cy;
    int width, height, radius;
    ObjPtr backColor;			/*Color of the background*/
    FuncTyp drawContents;		/*Routine to draw the contents*/
    ObjPtr valueArray;
    real value[2];

    Get2DIntBounds(object, &left, &right, &bottom, &top);

    /*Draw the control frame*/
    DrawButtonFrame(left, right - 1, bottom + 1, top, COLORWHEELHEIGHT, UIGRAY50);

    /*Draw the color background*/
    cx = (left + right) / 2;
    cy = (bottom + top) / 2;
    width = right - left;
    height = top - bottom;
    
    if (width > height)
    {
	radius = height / 2 - COLORWHEELBORDER;
    }
    else
    {
	radius = width / 2 - COLORWHEELBORDER;
    }

    /*Draw the color wedges*/
    SetUIColor(UIRED);
    arcfi(cx, cy, radius, -300, 300);
    SetUIColor(UIYELLOW);
    arcfi(cx, cy, radius, 300, 900);
    SetUIColor(UIGREEN);
    arcfi(cx, cy, radius, 900, 1500);
    SetUIColor(UICYAN);
    arcfi(cx, cy, radius, 1500, 2100);
    SetUIColor(UIBLUE);
    arcfi(cx, cy, radius, 2100, 2700);
    SetUIColor(UIMAGENTA);
    arcfi(cx, cy, radius, 2700, 3300);
    SetUIColor(UIWHITE);
    setpattern(GREYPAT);
    circfi(cx, cy, radius * 2 / 3);
    setpattern(SOLIDPAT);
    circfi(cx, cy, radius / 3);

    /*Draw the spot*/
    valueArray = GetVar(object, VALUE);
    if (valueArray)
    { 
	Array2CArray(value, valueArray);

	SetUIColor(UIBLACK);
	cx += radius * value[1] * cos(2.0 * M_PI * value[0]);
	cy += radius * value[1] * sin(2.0 * M_PI * value[0]);
	DrawUILine(cx - COLORWHEELSPOTSIZE, cy, cx + COLORWHEELSPOTSIZE, cy, UIBLACK);
	DrawUILine(cx, cy - COLORWHEELSPOTSIZE, cx, cy + COLORWHEELSPOTSIZE, UIBLACK);
    }

    if (!GetPredicate(object, ACTIVATED))
    {
	FillUIGauzeRect(left, right, bottom, top, UIBACKGROUND);
    }
#endif
    return ObjTrue;
}

#ifdef PROTO
void RGB2YIQ(real *y, real *i, real*q, real r, real g, real b)
#else
void RGB2YIQ(y, i, q, r, g, b)
real *y, *i, *q, r, g, b;
#endif
/*Converts r, g, b, into y, i, q*/
{
    *y = RGB2YIQMat[0][0] * r +
	 RGB2YIQMat[0][1] * g +
	 RGB2YIQMat[0][2] * b; 
    *i = RGB2YIQMat[1][0] * r +
	 RGB2YIQMat[1][1] * g +
	 RGB2YIQMat[1][2] * b; 
    *q = RGB2YIQMat[2][0] * r +
	 RGB2YIQMat[2][1] * g +
	 RGB2YIQMat[2][2] * b;

    *i = (*i + 0.6) / 1.2;
    *q = (*q + 0.52) / 1.04;

    if (*y < 0.0) *y = 0.0;
    if (*y > 1.0) *y = 1.0;
    if (*i < 0.0) *i = 0.0;
    if (*i > 1.0) *i = 1.0;
    if (*q < 0.0) *q = 0.0;
    if (*q > 1.0) *q = 1.0;
}

#ifdef PROTO
void YIQ2RGB(real *r, real *g, real *b, real y, real i, real q)
#else
void YIQ2RGB(r, g, b, y, i, q)
real y, i, q, *r, *g, *b;
#endif
/*Converts y, i, q, into r, g, b*/
{
    i = i * 1.2 - 0.6;
    q = q * 1.04 - 0.52;

    *r = YIQ2RGBMat[0][0] * y +
	 YIQ2RGBMat[0][1] * i +
	 YIQ2RGBMat[0][2] * q; 
    *g = YIQ2RGBMat[1][0] * y +
	 YIQ2RGBMat[1][1] * i +
	 YIQ2RGBMat[1][2] * q; 
    *b = YIQ2RGBMat[2][0] * y +
	 YIQ2RGBMat[2][1] * i +
	 YIQ2RGBMat[2][2] * q;

    if (*r < 0.0) *r = 0.0;
    if (*r > 1.0) *r = 1.0;
    if (*g < 0.0) *g = 0.0;
    if (*g > 1.0) *g = 1.0;
    if (*b < 0.0) *b = 0.0;
    if (*b > 1.0) *b = 1.0;
}

#ifdef PROTO
void HSV2RGB(real *r, real *g, real *b, real h, real s, real v)
#else
void HSV2RGB(r, g, b, h, s, v)
real *r, *g, *b, h, s, v;
#endif
/*Converts h s v into r g b.  All within range 0..1
  Adapted from Foley & VanDam*/
{
    int i;
    real f;
    real p, q, t;

    while (h < 0.0) h += 1.0;
    while (h > 1.0) h -= 1.0;
    h *= 6;		/*h from 0 to 6*/
    i = h;
    f = h - i;		/*fractional part of i*/
    i %= 6;		/*i from 0 to 5*/
    p = v * (1.0 - s);
    q = v * (1.0 - (s * f));
    t = v * (1.0 - (s * (1.0 - f)));

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

#ifdef PROTO
real HLSValue(real n1, real n2, real h)
#else
real HLSValue(n1, n2, h)
real n1; real n2; real h;
#endif
{
    while (h < 0.0) h += 1.0;
    while (h > 1.0) h -= 1.0;

    if (h < 1.0 / 6.0)
    {
	return n1 + (n2 - n1) * h * 6.0;
    }
    else if (h < 0.5)
    {
	return n2;
    }
    else if (h < 4.0 / 6.0)
    {
	return n1 + (n2 - n1) * (4.0 / 6.0 - h) * 6.0;
    }
    else
    {
	return n1;
    }
}

#ifdef PROTO
void HLS2RGB(real *r, real *g, real *b, real h, real l, real s)
#else
void HLS2RGB(r, g, b, h, l, s)
real *r, *g, *b, h, s, l;
#endif
/*Converts h l s into r g b.  All within range 0..1
  Adapted from Foley & VanDam*/
{
    real m1, m2;
    if (l <= 0.5)
    {
	m2 = l * (1.0 + s);
    }
    else
    {
	m2 = l + s - l * s;
    }
    m1 = 2 * l - m2;

    if (s == 0)
    {
	*r = *g = *b = 1.0;
    }
    else
    {
	*r = HLSValue(m1, m2, h + 1.0 / 3.0);
	*g = HLSValue(m1, m2, h);
	*b = HLSValue(m1, m2, h - 1.0 / 3.0);
    }
}

#ifdef PROTO
void RGB2HSV(real *h, real *s, real *v, real r, real g, real b)
#else
void RGB2HSV(h, s, v, r, g, b)
real r, g, b, *h, *s, *v;
#endif
/*Converts rgb to hsv.  All numbers within range 0 to 1.*/
{
    real max, min;

    max = MAX(r, MAX(g, b));
    min = MIN(r, MIN(g, b));
    *v = max;

    if (max > 0.0)
    {
	*s = (max - min) / max;
    }
    else
    {
	*s = 0;
    }

    if (*s > 0.0)
    {
	real rc, gc, bc;

	rc = (max - r) / (max - min);
	gc = (max - g) / (max - min);
	bc = (max - b) / (max - min);
	if (r == max)
	{
	    *h = (bc - gc) / 6.0;
	}
	else if (g == max)
	{
	    *h = (2.0 + rc - bc) / 6.0;
	}
	else
	{
	    *h = (4.0 + gc - rc) / 6.0;
	}
    }
    else
    {
	*h = 0.0;
    }
    if (*h < 0.0) *h += 1.0;
}

#ifdef PROTO
void RGB2HLS(real *h, real *l, real *s, real r, real g, real b)
#else
void RGB2HLS(h, l, s, r, g, b)
real r, g, b, *h, *s, *l;
#endif
/*Converts rgb to hls.  All numbers within range 0 to 1.*/
{
    real max, min;

    max = MAX(r, MAX(g, b));
    min = MIN(r, MIN(g, b));
    *l = (max + min) * 0.5;

    if (max == min)
    {
	*s = 0.0;
	*h = 0.0;
    }
    else
    {
	real rc, gc, bc;

	if (*l <= 0.5)
	{
	    *s = (max - min) / (max + min);
	}
	else
	{
	    *s = (max - min) / (2 - max - min);
	}

	rc = (max - r) / (max - min);
	gc = (max - g) / (max - min);
	bc = (max - b) / (max - min);
	if (r == max)
	{
	    *h = (bc - gc) / 6.0;
	}
	else if (g == max)
	{
	    *h = (2.0 + rc - bc) / 6.0;
	}
	else
	{
	    *h = (4.0 + gc - rc) / 6.0;
	}
    }
}

static ObjPtr TrackColorWheel(object, mouseX, mouseY, flags)
ObjPtr object;
int mouseX, mouseY;
int flags;
/*Track a click in an HS control.  Double-click snaps to closest pure hue*/
{
    int left, right, bottom, top;
    int cx, cy;
    int width, height, radius;
    ObjPtr backColor;			/*Color of the background*/
    FuncTyp drawContents;		/*Routine to draw the contents*/
    ObjPtr valueArray;
    real value[2];
    int lastX, lastY;			/*Last X and Y mouse position*/
    int sX, sY;				/*Shifted x and y for calculation*/
    Bool dontTrack;			/*True iff don't track*/

    Get2DIntBounds(object, &left, &right, &bottom, &top);

    if (mouseX < left || mouseX > right || mouseY < bottom || mouseY > top)
    {
	return ObjFalse;
    }

    if (TOOL(flags) == T_HELP)
    {
	ContextHelp(object);
	return ObjTrue;
    }

    if (!GetPredicate(object, ACTIVATED)) return ObjTrue;

    cx = (left + right) / 2;
    cy = (bottom + top) / 2;
    width = right - left;
    height = top - bottom;
    
    if (width > height)
    {
	radius = height / 2 - COLORWHEELBORDER;
    }
    else
    {
	radius = width / 2 - COLORWHEELBORDER;
    }

    /*Get the current value of the control*/
    valueArray = GetFixedArrayVar("TrackColorWheel", object, VALUE, 1, 2L);
    if (!valueArray)
    { 
	return ObjFalse;
    }
    Array2CArray(value, valueArray);

    /*Make laxtX and lastY correspond to value*/
    lastX = cx + value[1] * cos(2.0 * M_PI * value[0]);
    lastY = cy + value[1] * sin(2.0 * M_PI * value[0]);

    if (flags & F_DOUBLECLICK)
    {
	/*Snap current value to closest pure hue*/
	if (value[1] < 0.33)
	{
	    /*White*/
	    value[1] = 0.0;
	}
	else
	{
	    int testo;
	    /*50 % or 100% saturated something*/
	    if (value[1] > 0.66)
	    {
	        value[1] = 1.0;
	    }
	    else
	    {
		value[1] = 0.5;
	    }
	    testo = value[0] * 6.0 + 0.5;
	    value[0] = ((real) testo) / 6.0;
	}
	valueArray = NewRealArray(1, 2L);
	CArray2Array(valueArray, value);
	SetVar(object, VALUE, valueArray);
	DrawMe(object);
	UpdateDrawing();
	ChangedValue(object);
	if (logging)
	{
	    LogControl(object);
	}
	return ObjTrue;
    }

    dontTrack = GetPredicate(object, TRACKNOT);

    InhibitLogging(true);
    while (Mouse(&mouseX, &mouseY))
    {
	if (mouseX != lastX || mouseY != lastY)
	{
	    real hue, saturation;
	    /*Mouse has moved.  Update.*/
	    sX = mouseX - cx;
	    sY = mouseY - cy;

	    if (sX == 0 && sY == 0)
	    {
		/*It's at the origin, so choose 0 for h and s*/
		hue = 0.0;
		saturation = 0.0;
	    }
	    else
	    {
		/*Hue is angle*/
		hue = atan2(((double) sY) / ((double) radius),
			    ((double) sX) / ((double) radius)) / (2.0 * M_PI);
		while (hue < 0.0) hue += 1.0;

		/*Saturation is radius, clipped*/
		saturation = sqrt(((double) sY) * ((double) sY) +
				  ((double) sX) * ((double) sX)) / ((double) radius);
		if (saturation > 1.0) saturation = 1.0;
		if (flags & F_SHIFTDOWN)
		{
		    if (saturation > 0.66)
		    {
		        saturation = 1.0;
		    }
		    else if (saturation > 0.33)
		    {
			saturation = 0.5;
		    }
		    else
		    {
			saturation = 0.0;
		    }
		}
	    }
	    value[0] = hue;
	    value[1] = saturation;
	    valueArray = NewRealArray(1, 2L);
	    CArray2Array(valueArray, value);
	    SetVar(object, VALUE, valueArray);
	    DrawMe(object);
	    UpdateDrawing();
	    if (!dontTrack)
	    {
		ChangedValue(object);
	    }
	}
    }
    if (dontTrack)
    {
	ChangedValue(object);
    }
    InhibitLogging(false);
    if (logging)
    {
	LogControl(object);
    }
    return ObjTrue;
}

ObjPtr SetColorWheelVal(object, value)
ObjPtr object;
ObjPtr value;
/*Sets the value of the object to value*/
{
    if (value == NULLOBJ ||
	(IsArray(value) && RANK(value) == 1 && DIMS(value)[0] == 2))
    {
	SetVar(object, VALUE, value);
	ImInvalid(object);
	ChangedValue(object);
	if (logging)
	{
	    LogControl(object);
	}
	return ObjTrue;
    }
    else 
    {
	return ObjFalse;
    }
}

static ObjPtr CleanupPalette(palette)
ObjPtr palette;
/*Cleans up the palette before deleting*/
{
    PPtr *runner;
    if (((PPtr) palette) -> beg > 0 && ((PPtr) palette) -> nColors > 0)
    {
	/*There are some colors*/
	FreeColors(((PPtr) palette) -> beg, ((PPtr) palette) -> nColors);
    }
    if (((PPtr) palette) -> colors)
    {
	free(((PPtr) palette) -> colors);
	((PPtr) palette) -> colors = 0;
    }
    if (((PPtr) palette) -> components)
    {
	free(((PPtr) palette) -> components);
	((PPtr) palette) -> components = 0;
    }
    runner = &activePalettes;
    while (*runner)
    {
	if (*runner == (PPtr) palette)
	{
	    *runner = (*runner) -> next;
	}
	else
	{
	    runner = &((*runner) -> next);
	}
    }
    return ObjTrue;
}

ObjPtr NewPalette(nColors)
int nColors;
/*Returns a new palette of nColors, initially set to a grey ramp.  Returns
  0 if it can't.*/
{
    int k, colorBeg;
    PPtr retVal;
    short3 *colors, *components;

    colors = (short3 *) malloc(sizeof(short3) * nColors);
    if (!colors)
    {
	OMErr();
	return NULLOBJ;
    }

    components = (short3 *) malloc(sizeof(short3) * nColors);
    if (!components)
    {
	free(colors);
	OMErr();
	return NULLOBJ;
    }

    retVal = (PPtr) 
	NewObject(paletteClass, 
		 sizeof(Palette) - sizeof(Thing));
    if (!retVal)
    {
	return (ObjPtr) retVal;
    }
    retVal -> next = activePalettes;
    activePalettes = retVal -> next;
    retVal -> thing . flags = PALETTE;
    retVal -> colors = colors;
    retVal -> components = components;
    retVal -> beg = -1;
    retVal -> nColors = nColors;
    retVal -> min = -1.0;
    retVal -> max = 1.0;

    for (k = 0; k < nColors; ++k)
    {
	retVal -> colors[k][0] = 
	retVal -> colors[k][1] = 
	retVal -> colors[k][2] = (k + 1) * 255 / nColors;
    }
    CopyColorsToComponents(retVal, k, nColors - 1);
    return (ObjPtr) retVal;
}

void CopyPalette(d, s)
ObjPtr d, s;
/*Copies palette s to d*/
{
    int k;

    /*Free the colors*/
    if (((PPtr) d) -> beg > 0)
    {
	FreeColors(((PPtr) d) -> beg, ((PPtr) d) -> nColors);
	((PPtr) d) -> beg = -1;
    }

    if (((PPtr) d) -> nColors != ((PPtr) s) -> nColors)
    {
	SetVar(d, CHANGED, ObjTrue);
	free(((PPtr) d) -> colors);
	free(((PPtr) d) -> components);
	((PPtr) d) -> colors = malloc(sizeof(short3) * ((PPtr) s) -> nColors);
	((PPtr) d) -> components = malloc(sizeof(short3) * ((PPtr) s) -> nColors);
	((PPtr) d) -> nColors = ((PPtr) s) -> nColors;
    }
    ((PPtr) d) -> min = ((PPtr) s) -> min;
    ((PPtr) d) -> max = ((PPtr) s) -> max;

    for (k = 0; k < ((PPtr) d) -> nColors; ++k)
    {
	((PPtr) d) -> colors[k][0] = ((PPtr) s) -> colors[k][0];
	((PPtr) d) -> colors[k][1] = ((PPtr) s) -> colors[k][1];
	((PPtr) d) -> colors[k][2] = ((PPtr) s) -> colors[k][2];
	((PPtr) d) -> components[k][0] = ((PPtr) s) -> components[k][0];
	((PPtr) d) -> components[k][1] = ((PPtr) s) -> components[k][1];
	((PPtr) d) -> components[k][2] = ((PPtr) s) -> components[k][2];
    }
    SetVar(d, COLORMODEL, GetVar(s, COLORMODEL));
    SetVar(d, JUSTCOLORCHANGE, ObjTrue);

    MapPalette(d);
}

void SetPaletteNColors(p, nColors)
ObjPtr p;
int nColors;
/*Sets palette to be a nColors palette, doing any necessary resampling.*/
{
    short3 *oldColors, *colors;
    short3 *oldComponents, *components;
    real beg, end, increment;
    int s1, s2, i, k, comp;
    int temp;

    /*Free the colors*/
    if (((PPtr) p) -> beg > 0)
    {
	FreeColors(((PPtr) p) -> beg, ((PPtr) p) -> nColors);
	((PPtr) p) -> beg = -1;
    }

    /*Make a new set of colors*/
    oldColors = ((PPtr) p) -> colors;
    colors = (short3 *) malloc(nColors * sizeof(short3));

    /*And a new set of components*/
    oldComponents = ((PPtr) p) -> components;
    components = (short3 *) malloc(nColors * sizeof(short3));

    /*Copy ov, und, missing*/
    colors[0][0] = oldColors[0][0];
    colors[0][1] = oldColors[0][1];
    colors[0][2] = oldColors[0][2];

    colors[1][0] = oldColors[1][0];
    colors[1][1] = oldColors[1][1];
    colors[1][2] = oldColors[1][2];

    colors[nColors - 1][0] = oldColors[((PPtr) p) -> nColors][0];
    colors[nColors - 1][1] = oldColors[((PPtr) p) -> nColors][1];
    colors[nColors - 1][2] = oldColors[((PPtr) p) -> nColors][2];

    increment = ((real) ((PPtr) p) -> nColors - 3) / ((real) nColors - 3);
    
    for (k = 0; k < nColors - 2; ++k)
    {
	beg = k * increment;
	end = beg + increment;
	for (comp = 0; comp < 3; ++comp)
	{
	    s1 = beg;
	    s2 = end;

	    if (s1 == s2)
	    {
		/*Chunk of just one color*/
		colors[k + 2][comp] = oldColors[s1 + 2][comp];
	    }
	    else
	    {
		/*Chunk of more than one color*/
		real cum;

		/*First one*/
		cum = (1.0 - (beg - s1)) * oldColors[s1 + 2][comp];

		/*Intermediate ones*/
		for (i = s1 + 3; i < s2 + 2; ++i)
		{
		    cum += oldColors[i][comp];
		}

		/*Last one*/
		cum += (end - s2) * oldColors[s2 + 2][comp];

		cum /= (end - beg);

		temp = cum + 0.5;
		if (temp < 0) temp = 0;
		else if (temp > 255) temp = 255;

		colors[k + 2][comp] = temp;
	    }
	}
    }

    /*Update nColors*/
    ((PPtr) p) -> nColors = nColors;
    ((PPtr) p) -> colors = colors;
    ((PPtr) p) -> components = components;

    /*Free the old colors*/
    free(oldColors);
    free(oldComponents);

    CopyColorsToComponents((PPtr) p, 0, nColors - 1);

    SetVar(p, CHANGED, ObjTrue);
}

#ifdef PROTO
void CopyAttenuatedPalette(ObjPtr d, ObjPtr s, real atten)
#else
void CopyAttenuatedPalette(d, s, atten)
ObjPtr d, s;
real atten;
#endif
/*Copies palette s to d, attenuated by d*/
{
    int k;

    /*Free the colors*/
    if (((PPtr) d) -> beg > 0)
    {
	FreeColors(((PPtr) d) -> beg, ((PPtr) d) -> nColors);
	((PPtr) d) -> beg = -1;
    }

    if (((PPtr) d) -> nColors != ((PPtr) s) -> nColors)
    {
	SetVar(d, CHANGED, ObjTrue);
	free(((PPtr) d) -> colors);
	((PPtr) d) -> colors = malloc(sizeof(short3) * ((PPtr) s) -> nColors);
	free(((PPtr) d) -> components);
	((PPtr) d) -> components = malloc(sizeof(short3) * ((PPtr) s) -> nColors);
	((PPtr) d) -> nColors = ((PPtr) s) -> nColors;
    }
    ((PPtr) d) -> min = ((PPtr) s) -> min;
    ((PPtr) d) -> max = ((PPtr) s) -> max;

    for (k = 0; k < ((PPtr) d) -> nColors; ++k)
    {
	((PPtr) d) -> colors[k][0] = ((PPtr) s) -> colors[k][0] * atten;
	((PPtr) d) -> colors[k][1] = ((PPtr) s) -> colors[k][1] * atten;
	((PPtr) d) -> colors[k][2] = ((PPtr) s) -> colors[k][2] * atten;
    }

    SetVar(d, JUSTCOLORCHANGE, ObjTrue);
    CopyColorsToComponents((PPtr) d, 0, ((PPtr) d) -> nColors - 1);
    MapPalette(d);
}

ObjPtr ClonePalette(oldPalette)
ObjPtr oldPalette;
/*Clones a new palette*/
{
    PPtr retVal;
    short3 *colors, *components;

    colors = (short3 *) malloc(sizeof(short3) * ((PPtr) oldPalette) -> nColors);
    if (!colors)
    {
	OMErr();
	return oldPalette;
    }

    components = (short3 *) malloc(sizeof(short3) * ((PPtr) oldPalette) -> nColors);
    if (!components)
    {
	OMErr();
	return oldPalette;
    }

    retVal = (PPtr) 
	NewObject(paletteClass,
		sizeof(Palette) - sizeof(Thing));
    if (!retVal)
    {
	return (ObjPtr) retVal;
    }
    retVal -> next = activePalettes;
    activePalettes = retVal -> next;
    retVal -> thing . flags = PALETTE;
    retVal -> colors = colors;
    retVal -> components = components;
    retVal -> beg = -1;
    retVal -> nColors = ((PPtr) oldPalette) -> nColors;

    CopyPalette((ObjPtr) retVal, oldPalette);
    return (ObjPtr) retVal;
}

void InterpPalette(palette, r1, g1, b1, r2, g2, b2)
ObjPtr palette;
int r1, g1, b1, r2, g2, b2;
/*Makes palette be an interpolation between r1, g1, b1, and r2, g2, b2*/
{
    int k;
    int n;

    n = ((PPtr) palette) -> nColors - 3;	/*Room for min, max, missing*/

    for (k = 0; k < n; ++k)
    {
	((PPtr) palette) -> colors[k + 2][0] =
	    (k + 1) * r2 / n + 
	    (n - k) * r1 / n;
	((PPtr) palette) -> colors[k + 2][1] = 
	    (k + 1) * g2 / n + 
	    (n - k) * g1 / n;
	((PPtr) palette) -> colors[k + 2][2] =
	    (k + 1) * b2 / n + 
	    (n - k) * b1 / n;
    }

    /*Fill in missing*/
    ((PPtr) palette) -> colors[0][0] = 64;
    ((PPtr) palette) -> colors[0][1] = 64;
    ((PPtr) palette) -> colors[0][2] = 64;

    /*Fill in min*/
    ((PPtr) palette) -> colors[1][0] = ((PPtr) palette) -> colors[2][0];
    ((PPtr) palette) -> colors[1][1] = ((PPtr) palette) -> colors[2][1];
    ((PPtr) palette) -> colors[1][2] = ((PPtr) palette) -> colors[2][2];

    /*Fill in max*/
    ((PPtr) palette) -> colors[n + 2][0] = ((PPtr) palette) -> colors[n + 1][0];
    ((PPtr) palette) -> colors[n + 2][1] = ((PPtr) palette) -> colors[n + 1][1];
    ((PPtr) palette) -> colors[n + 2][2] = ((PPtr) palette) -> colors[n + 1][2];

    CopyColorsToComponents((PPtr) palette, 0, ((PPtr) palette) -> nColors - 1);

    MapPalette(palette);
}

void BlueToRedPalette(p)
ObjPtr p;
/*Makes a blue to red palette in p*/
{
    int k;
    real hue, r, g, b;
    int n;

    n = ((PPtr) p) -> nColors - 3;	/*Room for min, max, missing*/

    for (k = 0; k < n; ++k)
    {
	real v = 1.0;
/*
	hue = (((real)(n - k))
		/ (real) n) * 1.15 - 0.2;
*/
/*
	hue = (((real)(n - k))
		/ (real) n) * 0.8;
*/
	hue = (((real)(n - k))
		/ (real) n) * 0.9 - 0.1;
	if (hue < 0.0) {v = 1.0 + (hue * 3.0); hue = 0.0;} 
	HSV2RGB(&r, &g, &b, hue, 1.0, v);
	((PPtr) p) -> colors[k + 2][0] = r * 255;
	((PPtr) p) -> colors[k + 2][1] = g * 255;
	((PPtr) p) -> colors[k + 2][2] = b * 255;
    }

    /*Fill in missing*/
    ((PPtr) p) -> colors[0][0] = 64;
    ((PPtr) p) -> colors[0][1] = 64;
    ((PPtr) p) -> colors[0][2] = 64;

    /*Fill in min*/
    ((PPtr) p) -> colors[1][0] = ((PPtr) p) -> colors[2][0];
    ((PPtr) p) -> colors[1][1] = ((PPtr) p) -> colors[2][1];
    ((PPtr) p) -> colors[1][2] = ((PPtr) p) -> colors[2][2];

    /*Fill in max*/
    ((PPtr) p) -> colors[n + 2][0] = ((PPtr) p) -> colors[n + 1][0];
    ((PPtr) p) -> colors[n + 2][1] = ((PPtr) p) -> colors[n + 1][1];
    ((PPtr) p) -> colors[n + 2][2] = ((PPtr) p) -> colors[n + 1][2];

    CopyColorsToComponents((PPtr) p, 0, ((PPtr) p) -> nColors - 1);

    MapPalette(p);
}

void GreenToRedPalette(p)
ObjPtr p;
/*Makes a green to red palette in p*/
{
    int k;
    real hue, r, g, b;
    int n;

    n = ((PPtr) p) -> nColors - 3;	/*Room for min, max, missing*/

    for (k = 0; k < n; ++k)
    {
	hue = ((real)(n - k))
		/ (real) n * 0.35;
	HSV2RGB(&r, &g, &b, hue, 1.0, 1.0);
	((PPtr) p) -> colors[k + 2][0] = r * 255;
	((PPtr) p) -> colors[k + 2][1] = g * 255;
	((PPtr) p) -> colors[k + 2][2] = b * 255;
    }

    /*Fill in missing*/
    ((PPtr) p) -> colors[0][0] = 64;
    ((PPtr) p) -> colors[0][1] = 64;
    ((PPtr) p) -> colors[0][2] = 64;

    /*Fill in min*/
    ((PPtr) p) -> colors[1][0] = ((PPtr) p) -> colors[2][0];
    ((PPtr) p) -> colors[1][1] = ((PPtr) p) -> colors[2][1];
    ((PPtr) p) -> colors[1][2] = ((PPtr) p) -> colors[2][2];

    /*Fill in max*/
    ((PPtr) p) -> colors[n + 2][0] = ((PPtr) p) -> colors[n + 1][0];
    ((PPtr) p) -> colors[n + 2][1] = ((PPtr) p) -> colors[n + 1][1];
    ((PPtr) p) -> colors[n + 2][2] = ((PPtr) p) -> colors[n + 1][2];

    CopyColorsToComponents((PPtr) p, 0, ((PPtr) p) -> nColors - 1);

    MapPalette(p);
}

void DisposePalette(palette)
ObjPtr palette;
/*Disposes of palette*/
{
    free(palette);
}

void SetUIColor(c)
int c;
/*Sets the current drawing color to user interface color c, regardless
  of whether the current window is RGB or colormap*/
{
#ifdef GRAPHICS
    if (rgbp)
    {
	/*It's an RGB window*/
	lmcolor(LMC_COLOR);
	c3s(uiColors[c]);
    }
    else
    {
	/*It's a cmap window*/
	color(uiColorBeg + c);
    }
#endif
}

ObjPtr NewColorWheel(left, right, bottom, top, name)
int left, right, bottom, top;
char *name;
/*Makes a new hue/saturation control in left, right, bottom, top*/
{
    real value[2];
    ObjPtr valueArray;
    ObjPtr retVal;

    
    value[0] = 0.0;
    value[1] = 0.0;
    valueArray = NewRealArray(1, (long) 2);
    if (valueArray)
    {
	CArray2Array(valueArray, value);

	retVal = NewObject(colorWheelClass, 0);
	Set2DIntBounds(retVal, left, right, bottom, top);
	SetVar(retVal, VALUE, valueArray);
	SetVar(retVal, NAME, NewString(name));
	return retVal;
    }
    else
    {
	return NULLOBJ;
    }
}

static int ColorToPixel(colorBar, clr)
ObjPtr colorBar;
int clr;
/*Returns the horizontal pixel for the color value in clr*/
{
    int left, right, bottom, top;
    PPtr palette;
    int nColors;

    /*Get the palette*/
    palette = (PPtr) GetPaletteVar("ColorToPixel", colorBar, REPOBJ);
    if (!palette)
    {
	return 0;
    }

    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

    /*Get the stuff from the palette*/
    nColors = palette -> nColors;

    if (clr == 0)
    {
	/*Missing*/
	return left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH / 2;
    }
    else if (clr == 1)
    {
	/*Underflow*/
	return left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + CBBOXWIDTH / 2;
    }
    else if (clr == nColors - 1)
    {
	/*Overflow*/
	return right - CBRBORDER - CBBOXWIDTH / 2;
    }
    else
    {
	/*In the center*/
	int r, l;

	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;

	return ((clr - 2) * (r - l) + (r - l) / 2) / (nColors - 3) + l;
    }
}

static Bool ColorToPixels(int *, int *, ObjPtr, int);

static Bool ColorToPixels(lPix, rPix, colorBar, clr)
ObjPtr colorBar;
int clr;
int *lPix, *rPix;
/*Returns the horizontal pixelx around the color value in clr*/
{
    int left, right, bottom, top;
    PPtr palette;
    int nColors;

    /*Get the palette*/
    palette = (PPtr) GetPaletteVar("ColorToPixel", colorBar, REPOBJ);
    if (!palette)
    {
	return false;
    }

    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

    /*Get the stuff from the palette*/
    nColors = palette -> nColors;

    if (clr == 0)
    {
	/*Missing*/
	*lPix = left + CBLBORDER + CBLTEXTSPACE + 1;
	*rPix = left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH - 1;
	return true;
    }
    else if (clr == 1)
    {
	/*Underflow*/
	*lPix = left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + 1;
	*rPix = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP - 1;
	return true;
    }
    else if (clr == nColors - 1)
    {
	/*Overflow*/
	*lPix = right - CBRBORDER - CBBOXWIDTH + 1;
	*rPix = right - CBRBORDER - 1;
	return true;
    }
    else
    {
	/*In the center*/
	int r, l;

	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;

	if (clr == 2)
	{
	    *lPix = l;
	}
	else
	{
	    *lPix = l + (clr - 2) * (r - l) / (nColors - 3) + 1;
	}
	*rPix = l + (clr - 1) * (r - l) / (nColors - 3);

	return true;
    }
}

static int PixelToColor(colorBar, pix)
ObjPtr colorBar;
int pix;
/*Returns the color for the horizontal pixel pixel*/
{
    int left, right, bottom, top;
    PPtr palette;
    int nColors;

    /*Get the palette*/
    palette = (PPtr) GetPaletteVar("ColorToPixel", colorBar, REPOBJ);
    if (!palette)
    {
	return 0;
    }

    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

    /*Get the stuff from the palette*/
    nColors = palette -> nColors;

    if (pix < left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP / 2)
    {
	/*Missing*/
	return 0;
    }
    else if (pix < left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP)
    {
	/*Underflow*/
	return 1;
    }
    else if (pix > right - CBRBORDER - CBBOXWIDTH)
    {
	/*Overflow*/
	return nColors - 1;
    }
    else
    {
	/*In the center*/
	int r, l, retVal;

	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;
	retVal = ((pix - l) * (nColors - 3) + (nColors - 3) / 2) / (r - l)
		+ 2;
	if (retVal >= nColors - 1)
	{
	    retVal = nColors - 2;
	}
	if (retVal < 2)
	{
	    retVal = 2;
	}
	return retVal;
    }
}


#undef CEDGE
#define CEDGE 3

#ifdef PROTO
static void DrawFullButton(int left, int right, int bottom, int top, Bool highlight)
#else
static void DrawFullButton(left, right, bottom, top, highlight)
int left, right, bottom, top;
Bool highlight;
#endif
{
#ifdef GRAPHICS
	Coord v[4][2];

	/* bottom */
	v[0][0] = left + CEDGE;
	v[0][1] = bottom + CEDGE;
	v[1][0] = right - CEDGE;
	v[1][1] = bottom + CEDGE;
	v[2][0] = right;
	v[2][1] = bottom;
	v[3][0] = left;
	v[3][1] = bottom;
	SetUIColor(UIBOTTOMEDGE);
	polf2(4, v);

	/* left */
	v[0][0] = left;
	v[0][1] = top;
	v[1][0] = left + CEDGE;
	v[1][1] = top - CEDGE;
	v[2][0] = left + CEDGE;
	v[2][1] = bottom + CEDGE;
	v[3][0] = left;
	v[3][1] = bottom;
	SetUIColor(UILEFTEDGE);
	polf2(4,v);

	/* right */
	v[0][0] = right - CEDGE;
	v[0][1] = top - CEDGE;
	v[1][0] = right;
	v[1][1] = top;
	v[2][0] = right;
	v[2][1] = bottom;
	v[3][0] = right - CEDGE;
	v[3][1] = bottom + CEDGE;
	SetUIColor(UIRIGHTEDGE);
	polf2(4,v);

	/* top */
	v[0][0] = left;
	v[0][1] = top;
	v[1][0] = right;
	v[1][1] = top;
	v[2][0] = right - CEDGE;
	v[2][1] = top - CEDGE;
	v[3][0] = left + CEDGE;
	v[3][1] = top - CEDGE;
	SetUIColor(UITOPEDGE);
	polf2(4,v);

	/* left face of control */
	v[0][0] = left + CEDGE;
	v[0][1] = bottom + CEDGE;
	v[1][0] = (right + left) / 2;
	v[1][1] = bottom + CEDGE;
	v[2][0] = (right + left) / 2;
	v[2][1] = top - CEDGE;
	v[3][0] = left + CEDGE;
	v[3][1] = top - CEDGE;
	SetUIColor((highlight & 1) ? UIHIBACKGROUND : UIBACKGROUND);
	polf2(4,v);

	/* right face of control */
	v[0][0] = (right + left) / 2 + 1;
	v[0][1] = bottom + CEDGE;
	v[1][0] = right - CEDGE;
	v[1][1] = bottom + CEDGE;
	v[2][0] = right - CEDGE;
	v[2][1] = top - CEDGE;
	v[3][0] = (right + left) / 2 + 1;
	v[3][1] = top - CEDGE;
	SetUIColor((highlight & 2) ? UIHIBACKGROUND : UIBACKGROUND);
	polf2(4,v);

	/*Score line*/
	DrawUILine((left + right) / 2, top - CEDGE,
		   (left + right) / 2, bottom + CEDGE,
		   UIGRAY25);
	DrawUILine((left + right) / 2 + 1, top - CEDGE,
		   (left + right) / 2 + 1, bottom + CEDGE,
		   UIWHITE);
#endif
}

#ifdef PROTO
static void DrawLeftButton(int left, int right, int bottom, int top, Bool highlight)
#else
static void DrawLeftButton(left, right, bottom, top, highlight)
int left, right, bottom, top;
Bool highlight;
#endif
{
#ifdef GRAPHICS
	Coord v[4][2];

	/* bottom */
	v[0][0] = left + CEDGE;
	v[0][1] = bottom + CEDGE;
	v[1][0] = (right + left) / 2;
	v[1][1] = bottom + CEDGE;
	v[2][0] = (right + left) / 2;
	v[2][1] = bottom;
	v[3][0] = left;
	v[3][1] = bottom;
	SetUIColor(UIBOTTOMEDGE);
	polf2(4, v);

	/* left */
	v[0][0] = left;
	v[0][1] = top;
	v[1][0] = left + CEDGE;
	v[1][1] = top - CEDGE;
	v[2][0] = left + CEDGE;
	v[2][1] = bottom + CEDGE;
	v[3][0] = left;
	v[3][1] = bottom;
	SetUIColor(UILEFTEDGE);
	polf2(4,v);

	/* top */
	v[0][0] = left;
	v[0][1] = top;
	v[1][0] = (right + left) / 2;
	v[1][1] = top;
	v[2][0] = (right + left) / 2;
	v[2][1] = top - CEDGE;
	v[3][0] = left + CEDGE;
	v[3][1] = top - CEDGE;
	SetUIColor(UITOPEDGE);
	polf2(4,v);

	/* face of control */
	v[0][0] = left + CEDGE;
	v[0][1] = bottom + CEDGE;
	v[1][0] = (right + left) / 2;
	v[1][1] = bottom + CEDGE;
	v[2][0] = (right + left) / 2;
	v[2][1] = top - CEDGE;
	v[3][0] = left + CEDGE;
	v[3][1] = top - CEDGE;
	SetUIColor(highlight ? UIHIBACKGROUND : UIBACKGROUND);
	polf2(4,v);

	/*Score line*/
	DrawUILine((left + right) / 2 + 1, top,
		   (left + right) / 2 + 1, bottom,
		   UIGRAY25);
#endif
}

#ifdef PROTO
static void DrawRightButton(int left, int right, int bottom, int top, Bool highlight)
#else
static void DrawRightButton(left, right, bottom, top, highlight)
int left, right, bottom, top;
Bool highlight;
#endif
{
#ifdef GRAPHICS
	Coord v[4][2];

	/* bottom */
	v[0][0] = (left + right) / 2;
	v[0][1] = bottom + CEDGE;
	v[1][0] = right - CEDGE;
	v[1][1] = bottom + CEDGE;
	v[2][0] = right;
	v[2][1] = bottom;
	v[3][0] = (left + right) / 2;
	v[3][1] = bottom;
	SetUIColor(UIBOTTOMEDGE);
	polf2(4, v);

	/* right */
	v[0][0] = right - CEDGE;
	v[0][1] = top - CEDGE;
	v[1][0] = right;
	v[1][1] = top;
	v[2][0] = right;
	v[2][1] = bottom;
	v[3][0] = right - CEDGE;
	v[3][1] = bottom + CEDGE;
	SetUIColor(UIRIGHTEDGE);
	polf2(4,v);

	/* top */
	v[0][0] = (left + right) / 2;
	v[0][1] = top;
	v[1][0] = right;
	v[1][1] = top;
	v[2][0] = right - CEDGE;
	v[2][1] = top - CEDGE;
	v[3][0] = (left + right) / 2;
	v[3][1] = top - CEDGE;
	SetUIColor(UITOPEDGE);
	polf2(4,v);

	/* face of control */
	v[0][0] = (left + right) / 2;
	v[0][1] = bottom + CEDGE;
	v[1][0] = right - CEDGE;
	v[1][1] = bottom + CEDGE;
	v[2][0] = right - CEDGE;
	v[2][1] = top - CEDGE;
	v[3][0] = (left + right) / 2;
	v[3][1] = top - CEDGE;
	SetUIColor(highlight ? UIHIBACKGROUND : UIBACKGROUND);
	polf2(4,v);

	/*Score line*/
	DrawUILine((left + right) / 2, top,
		   (left + right) / 2, bottom,
		   UIWHITE);
#endif
}

#ifdef PROTO
void CalcGoodSteps(double diff, int pixWidth, int minMajorPix, double *majorWidth, int *nMinorSteps)
#else
void CalcGoodSteps(diff, pixWidth, minMajorPix, majorWidth, nMinorSteps)
double diff;
int pixWidth;
int minMajorPix;
double *majorWidth;
int *nMinorSteps;
#endif
/*Calculates good steps for a 10-base scale
  diff is the difference between minimum and max
  pixWidth is the width in pixels between minimum and max
  minMajorPix is the minimum number of pixels between major tics
  *majorWidth will be the width of a major step
  *nMinorTics will be the number of minor steps
*/
{
    long deltaLog;	/*Log of the delta at minimum*/
    double minDelta;	/*Minimum delta between major tics*/

    if (diff <= 0.0)
    {
	*majorWidth = 0.0;
	nMinorSteps = 0;
    }

    minDelta = diff * minMajorPix / pixWidth;

    deltaLog = ((long) (1000.0 + log10((double) minDelta))) - 1000;
    *majorWidth = pow((double) 10.0, (double) deltaLog);
    *nMinorSteps = 0;

    while (*majorWidth < minDelta)
    {
	if (*majorWidth < minDelta)
	{
	    /*Try 2*/
	    *majorWidth *= 2.0;
	    *nMinorSteps = 2;
	}
	if (*majorWidth < minDelta)
	{
	    /*Try 5*/
	    *majorWidth *= 2.5;
	    *nMinorSteps = 5;
	}
	if (*majorWidth < minDelta)
	{
	    /*Try 10*/
	    *majorWidth *= 2.0;
	    *nMinorSteps = 10;
	}
    }
}

void ResetColorBarTools(colorBar)
ObjPtr colorBar;
/*Resets the tools associated with a color bar*/
{
    ObjPtr radio, freeFormButton;
    
    InhibitLogging(true);
    freeFormButton = GetVar(colorBar, FREEFORMBUTTON);
    radio = GetVar(colorBar, TOOLGROUP);
    if (radio)
    {
	/***UPDATE For some reason, this doesn't work properly.*/
	SetValue(radio, NewInt(freeFormButton ? 
				(GetPredicate(freeFormButton, ACTIVATED) ? PT_FREEFORM : -1)
				: PT_FREEFORM));
    }
    InhibitLogging(false);
}

ObjPtr SetColorBarVal(object, value)
ObjPtr object;
ObjPtr value;
/*Sets the value of the object to value*/
{
    if (IsArray(value) && RANK(value) == 1 && DIMS(value)[0] == 3)
    {
	ObjPtr radio;
	PPtr repObj;
	real *elements;

	elements = ELEMENTS(value);
	if (elements[0] < 0.0 || elements[0] > 3.0 ||
	    elements[1] < 0.0 || elements[2] < 0.0)
	{
	    ReportError("SetColorBarVal", "Value error");
	    return ObjFalse;
	}

	repObj = (PPtr) GetPaletteVar("SetColorBarVal", object, REPOBJ);
	if (repObj)
	{
	    if (elements[1] >= ((real) repObj -> nColors) - 0.5 ||
		elements[2] >= ((real) repObj -> nColors) - 0.5)
	    {
		ReportError("SetColorBarVal", "Value error");
		return ObjFalse;
	    }
	}

	SetVar(object, VALUE, value);
	ImInvalid(object);

	ChangedValue(object);
	if (logging)
	{
	    LogControl(object);
	}

	/*Set the edit tool to 0*/
	ResetColorBarTools(object);

	return ObjTrue;
    }
    else
    {
	ReportError("SetColorBarVal", "Bad value for color bar");
	return ObjFalse;
    }
}

static ObjPtr ReinitColorBar(colorBar)
ObjPtr colorBar;
/*Reinitializes a color bar*/
{
    return ObjTrue;
}

real GetColorValue(palette, color)
ObjPtr palette;
int color;
/*Gets a field value for center of color color.  Ignores missing*/
{
    return
	((PPtr) palette) -> min +
	(color - 2) * (((PPtr) palette) -> max - ((PPtr) palette) -> min) /
	(((PPtr) palette) -> nColors - 4);
}

static int hueColors[7] =
    {
	UIRED, UIYELLOW, UIGREEN, UICYAN, UIBLUE, UIMAGENTA, UIRED
    };

#ifdef PROTO
static void ColorModelRect(int left, int right, int bottom, int top, int cm, int k)
#else
static void ColorModelRect(left, right, bottom, top, cm, k)
int left;
int right;
int bottom;
int top;
int cm;
int k;
#endif
/*Fills a rectangle within the bounds for color model cm in field k*/
{
    switch (cm)
    {
	case CM_RGB:
	    FillUIRect(left, right, bottom, top, 
			k == 1 ? UIBLUE : k == 2 ? UIGREEN : UIRED);
	    break;
	case CM_HSV:
	case CM_HLS:
	    shademodel(GOURAUD);
	    if (k == 3)
	    {
		/*Hue*/
		int c;
		long v[2];

		bgnpolygon();
		SetUIColor(UIRED);
		v[1] = bottom;
		v[0] = left;
		v2i(v);
		v[0] = right + 1;
		v2i(v);

		for (c = 1; c < 5; ++c)
		{
		    SetUIColor(hueColors[c]);
		    v[1] = bottom + (top - bottom) * c / 6;
		    v[0] = right + 1;
		    v2i(v);
		    v[0] = left;
		    v2i(v);
		    endpolygon();

		    bgnpolygon();
		    v2i(v);
		    v[0] = right + 1;
		    v2i(v);
		}
		SetUIColor(hueColors[c]);
		v[1] = top + 1;
		v[0] = right + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	    else if ((cm == CM_HSV && k == 2) ||
		     (cm == CM_HLS && k == 1))
	    {
		/*Saturation*/
		long v[2];
		bgnpolygon();
		SetUIColor(UIWHITE);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UICYAN);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	    else if (cm == CM_HSV)
	    {
		/*Must be value*/
		long v[2];
		bgnpolygon();
		SetUIColor(UIBLACK);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UICYAN);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	    else
	    {
		/*Must be lightness*/
		long v[2];
		bgnpolygon();
		SetUIColor(UIBLACK);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UICYAN);
		v[1] = (top + bottom) / 2;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();

		bgnpolygon();
		v[0] = left;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UIWHITE);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	
	    shademodel(FLAT);
	    break;
	case CM_YIQ:
	    shademodel(GOURAUD);
	    if (k == 3)
	    {
		/*Must be y*/
		long v[2];
		bgnpolygon();
		SetUIColor(UIBLACK);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UICYAN);
		v[1] = (top + bottom) / 2;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();

		bgnpolygon();
		v[0] = left;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UIWHITE);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	    else if (k == 2)
	    {
		/*i, cyan/orange index*/
		long v[2];
		bgnpolygon();
		SetUIColor(UICYAN);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UIORANGE);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	    else
	    {
		/*q, green/magenta axis*/
		long v[2];
		bgnpolygon();
		SetUIColor(UIGREEN);
		v[0] = left;
		v[1] = bottom;
		v2i(v);
		v[0] = right + 1;
		v2i(v);
		SetUIColor(UIMAGENTA);
		v[1] = top + 1;
		v2i(v);
		v[0] = left;
		v2i(v);
		endpolygon();
	    }
	
	    shademodel(FLAT);
	    break;
    }
}

static ObjPtr DrawColorBar(colorBar)
ObjPtr colorBar;
/*Draws a color bar*/
{
    int left, right, bottom, top;
    int l, r, b, t;
    int start, diff, comp;
    PPtr palette;
    int nColors;
    short3 *colors;
    ObjPtr value;			/*Value of the control*/
    real *elements;
    ObjPtr var;
    int highlighted;
    int k;
    double majorWidth, minorWidth;	/*Width of a major step*/
    double ddiff;				/*Data difference*/
    int nTics;				/*Number of minor tics*/
    long temp;
    double halfSpace;
    int pixel;				/*Temporary pixel*/
    double curValue;			/*Current value for making tics*/
    int cm;				/*Color model in use*/

#ifdef GRAPHICS
    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

    /*Get the palette*/
    palette = (PPtr) GetPaletteVar("DrawColorBar", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    /*Get the stuff from the palette*/
    nColors = palette -> nColors;
    colors = palette -> colors;

    /*Draw the bump and plateau*/
    DrawBumpEdge(left, right, bottom, top);
    FillUIRect(left + 3, right - 3, bottom + 3, top - 3, UIBACKGROUND);

    /*Draw the legends and tic marks and stuff at the bottom*/
    SetUIColor(UIBLACK);
    SetupFont(CBTEXTFONT, CBTEXTSIZE);
    b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP;

    DrawString(	left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH / 2 - StrWidth("Missing") / 2,
		bottom + CBBORDER + CBTEXTUP, "Missing");
    DrawUILine( left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH / 2, b,
		left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH / 2, b - CBMAJORTICLEN, UIBLACK);
    DrawString(	left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + CBBOXWIDTH / 2 - StrWidth("Under") / 2,
		bottom + CBBORDER + CBTEXTUP, "Under");
    DrawUILine( left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + CBBOXWIDTH / 2, b,
		left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + CBBOXWIDTH / 2, b - CBMAJORTICLEN, UIBLACK);

    /*Draw all the tics in the middle*/
    l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
    r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;
    halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
    ddiff = palette -> max - palette -> min;

    CalcGoodSteps(ddiff,
		  r - l,
		  CBSTEPPIXELS,
		  &majorWidth, &nTics);
    minorWidth = majorWidth / nTics;

    /*Minor and major tics first*/
    temp = palette -> min / majorWidth;
    curValue = temp * majorWidth;

    while (curValue > palette -> min)
    {
	curValue -= majorWidth;
    }
    k = 0;
    while (curValue < palette -> min)
    {
	++k;
	if (k >= nTics) k = 0;

	curValue += minorWidth;
    }

    /*Now actually draw them*/
    if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
    while (curValue <= palette -> max + ddiff * 1.0E-6)
    {
	pixel = l + (curValue - palette -> min) * (r - l) / (ddiff);
	if (k == 0)
	{
	    /*Major tic*/
	    DrawUILine(pixel, b,
		       pixel, b - CBMAJORTICLEN,
		       UIBLACK);
	    sprintf(tempStr, "%lg", curValue);
	    DrawString(pixel - StrWidth(tempStr) / 2, b - CBTEXTSEP, tempStr);
	}
	else
	{
	    /*Minor tic*/
	    DrawUILine(pixel, b,
		       pixel, b - CBMAJORTICLEN / 2,
		       UIBLACK);
	}

	curValue += minorWidth;
	if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
	++k;
	if (k >= nTics) k = 0;
    }

    DrawString(	right - CBRBORDER - CBBOXWIDTH + CBBOXWIDTH / 2 - StrWidth("Over") / 2,
		bottom + CBBORDER + CBTEXTUP, "Over");
    DrawUILine( right - CBRBORDER - CBBOXWIDTH + CBBOXWIDTH / 2, b,
		right - CBRBORDER - CBBOXWIDTH + CBBOXWIDTH / 2, b - CBMAJORTICLEN, UIBLACK);

    /*Get the color model*/
    var = GetIntVar("DrawColorBar", (ObjPtr) palette, COLORMODEL);
    if (!var)
    {
	return ObjFalse;
    }
    cm = GetInt(var);

    /*Draw the boxes*/
    b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP;
    SetupFont(CBBIGTEXTFONT, CBBIGTEXTSIZE);

    for (k = 0; k < 4; ++k)
    {
	t = b + CBCOMPHEIGHT;

	/*Draw the box outlines*/

	/*Draw the missing data box*/
	FrameUIRect(left + CBLBORDER + CBLTEXTSPACE, left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH, b, t, UIBLACK);

	/*Now the underflow box*/
	FrameUIRect(left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP,
		left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP,
		b, t, UIBLACK);

	/*Now the middle bit*/
	FrameUIRect(left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP,
		right  - CBRBORDER - CBBOXWIDTH - CBHGAP,
		b, t, UIBLACK);

	/*Now the overflow box*/
	FrameUIRect(right - CBRBORDER - CBBOXWIDTH,
		right - CBRBORDER,
		b, t, UIBLACK);

	/*Draw the background of the boxex*/

	/*Draw the missing data box*/
	ColorModelRect(left + CBLBORDER + CBLTEXTSPACE + 1, left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH - 1, b + 1, t - 1, 
			cm, k);

	/*Now the underflow box*/
	ColorModelRect(left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + 1,
			left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP - 1,
			b + 1, t - 1, cm, k);

	/*Now the middle bit*/
	ColorModelRect(left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1,
			right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1,
			b + 1, t - 1, cm, k);

	/*Now the overflow box*/
	ColorModelRect(right - CBRBORDER - CBBOXWIDTH + 1,
			right - CBRBORDER - 1,
			b + 1, t - 1, cm, k);

	/*Now the component names*/
	if (k)
	{
	    SetUIColor(UIBLACK);
	    DrawString(left + CBLBORDER, (b + t) / 2 - CBCOMPTEXTDOWN,
		shortComponentNames[cm][3 - k]);
	}

	b = t + CBVGAP;
    }

    /*Now the expanded readout*/
    FrameUIRect(left + CBLBORDER,
		right  - CBRBORDER,
		b, top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP, UIBLACK);

    b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP + 1;
    t = b + CBCOMPHEIGHT - 2;

    /*Now draw the colors*/
    if (rgbp)
    {
	/*It's in RGB mode, so we don't have to set the palette*/

	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;

	/*Do missing data box*/
	c3s(colors[0]);
	rectfi( left + CBLBORDER + CBLTEXTSPACE + 1, b,
		left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH - 1, t);

	/*Do underflow data box*/
	c3s(colors[1]);
	rectfi( left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + 1, b,
		left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP - 1, t);

	/*Do the colors in the center*/
	diff = r - l;
	start = l;
	for (k = 2; k < nColors - 1; ++k)
	{
	    r = (k - 1) * diff / (nColors - 3) + start;
	    c3s(colors[k]);
	    rectfi(l, b, r, t);
	    l = r + 1;
	}

	/*Do overflow data box*/
	c3s(colors[nColors - 1]);
	rectfi( right - CBRBORDER - CBBOXWIDTH + 1, b,
		right - CBRBORDER - 1, t);
    }
    else
    {
	/*It's a color map window, have to set the colors*/
	int beg;

	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;

	SetPalette((ObjPtr) palette);

	beg = palette -> beg;

	/*Do missing data box*/
	color(beg);
	rectfi( left + CBLBORDER + CBLTEXTSPACE + 1, b,
		left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH - 1, t);

	/*Do underflow data box*/
	color(beg + 1);
	rectfi( left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + 1, b,
		left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP - 1, t);

	/*Do the colors in the center*/
	diff = r - l;
	start = l;
	for (k = 2; k < nColors - 1; ++k)
	{
	    r = (k - 1) * diff / (nColors - 3) + start;
	    color(beg + k);
	    rectfi(l, b, r, t);
	    l = r + 1;
	}

	/*Do overflow data box*/
	color(beg + nColors - 1);
	rectfi( right - CBRBORDER - CBBOXWIDTH + 1, b,
		right - CBRBORDER - 1, t);
    }


    b = t + CBVGAP + 2;
    t = b + CBCOMPHEIGHT - 1;
    /*Do 3 components*/
    for (comp = 2; comp >= 0; --comp)
    {
	int height;

	SetUIColor(UIGRAY62);

	/*Do missing data box*/
	height = GetColorComponent(palette, 0, comp) * (CBCOMPHEIGHT - 1) / 255;
	if (height < CBCOMPHEIGHT - 1)
	{
	    rectfi( left + CBLBORDER + CBLTEXTSPACE + 1, t - CBCOMPHEIGHT + height + 1,
		    left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH - 1, t - 1);
	}

	/*Do underflow data box*/
	height = GetColorComponent(palette, 1, comp) * (CBCOMPHEIGHT - 1) / 255;
	if (height < CBCOMPHEIGHT - 1)
	{
	    rectfi( left + CBLBORDER + CBLTEXTSPACE + CBBOXWIDTH + CBHGAP + 1, t - CBCOMPHEIGHT + height + 1,
		    left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + CBHGAP - 1, t - 1);
	}

	/*Do the colors in the center*/
	l = left + CBLBORDER + CBLTEXTSPACE + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
	r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;
	diff = r - l;
	start = l;
	for (k = 2; k < nColors - 1; ++k)
	{
	    r = (k - 1) * diff / (nColors - 3) + start;
	    height = GetColorComponent(palette, k, comp) * (CBCOMPHEIGHT - 1) / 255;
	    if (height < CBCOMPHEIGHT - 1)
	    {
		rectfi(l, t - CBCOMPHEIGHT + height + 1, r, t - 1);
	    }
	    l = r + 1;
	}

	/*Overflow*/
	height = GetColorComponent(palette, nColors - 1, comp) * (CBCOMPHEIGHT - 1) / 255;
	if (height < CBCOMPHEIGHT - 1)
	{
	    rectfi(right - CBRBORDER - CBBOXWIDTH + 1, t - CBCOMPHEIGHT + height + 1, 
		right - CBRBORDER - 1, t - 1);
	}
	b = t + CBVGAP + 1;
	t = b + CBCOMPHEIGHT - 1;
    }

    /*Now deal with the value*/
    value = GetValue(colorBar);
    if (!value)
    {
	/*Have to give it a value*/
	value = NewRealArray(1, 3L);
	elements = ELEMENTS(value);
	elements[0] = 0.0;
	elements[1] = 0.0;
	elements[2] = 0.0;
    }
    else
    {
	elements = ELEMENTS(value);
    }

    if (elements[0] >= 0.5)
    {
	/*Draw the selection in the magnified area*/
	int dummy;

	b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP +
		((int) (elements[0] - 0.5)) * (CBVGAP + CBCOMPHEIGHT);
	t = b + CBCOMPHEIGHT;

	ColorToPixels(&l, &dummy, colorBar, (int) (elements[1] + 0.5));
	ColorToPixels(&dummy, &r, colorBar, (int) (elements[2] + 0.5));

	FrameUIRect(l - 1, r + 3, b - 2, t, UIBLACK);
	FrameUIRect(l - 2, r + 1, b, t + 1, UIYELLOW);
	FrameUIRect(l - 3, r + 2, b - 1, t + 2, UIYELLOW);

	/*Draw the expanded range*/
	if (elements[0] == 1.0)
	{
	    int c1, c2;
	    /*Draw the full color*/
	    l = left + CBLBORDER + 1;
	    r = right  - CBRBORDER - 1;
	    t = top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP - 1;
	    b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP + 4 * CBCOMPHEIGHT
		+ 4 * CBVGAP + 1;
	    if (rgbp)
	    {
		/*It's in RGB mode, so we don't have to set the palette*/

		/*Do the colors in the center*/
		diff = r - l;
		start = l;
		c1 = elements[1];
		c2 = elements[2];
		
		for (k = c1; k <= c2; ++k)
		{
		    r = (k - c1 + 1) * diff / (c2 - c1 + 1) + start;
		    c3s(colors[k]);
		    rectfi(l, b, r, t);
		    l = r + 1;
		}
	    }
	    else
	    {
		/*It's a color map window, have to set the colors*/
		int beg;
		SetPalette((ObjPtr) palette);

		beg = palette -> beg;

		/*Do the colors in the center*/
		diff = r - l;
		start = l;
		c1 = elements[1];
		c2 = elements[2];
		for (k = c1; k <=c2; ++k)
		{
		    r = (k - c1 + 1) * diff / (c2 - c1 + 1) + start;
		    color(beg + k);
		    rectfi(l, b, r, t);
		    l = r + 1;
		}
	    }
	}
	else
	{
	    int c1, c2;
	    int height;
	    c1 = elements[1];
	    c2 = elements[2];

	    comp = 4 - ((int) elements[0]);

	    l = left + CBLBORDER + 1;
	    r = right  - CBRBORDER - 1;
	    t = top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP - 1;
	    b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP + 4 * CBCOMPHEIGHT
		+ 4 * CBVGAP + 1;

            ColorModelRect(l, r, b, t, cm, (int) elements[0] - 1);

	    SetUIColor(UIGRAY62);
	    /*Do the colors in the center*/
	    diff = r - l;
	    start = l;
	    for (k = c1; k <= c2; ++k)
	    {
		r = (k - c1 + 1) * diff / (c2 - c1 + 1) + start;
		height = GetColorComponent(palette, k, comp) * (t - b) / 255.0 + 0.5;
		if (height < t - b)
		{
		    rectfi(l, b + height, r, t);
		}
		l = r + 1;
	    }
	}
    }	

    /*Draw the tics at the top*/
    if (elements[0] > 0.0)
    {
	real start, finish, halfSpace;
	l = left + CBLBORDER + 1;
	r = right - CBRBORDER - 1;
	t = top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP - 1;
	b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP +  4 * CBCOMPHEIGHT + 4 * CBVGAP + 1;

	if  (elements[2] >= elements[1])
	{
	    /*It's a range*/

	    halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
	    start = GetColorValue((ObjPtr) palette, (int) elements[1]) - halfSpace;
	    finish = GetColorValue((ObjPtr) palette, (int) elements[2]) + halfSpace;
	    ddiff = finish - start;

	    CalcGoodSteps(ddiff,
		      r - l,
		      CBSTEPPIXELS,
		      &majorWidth, &nTics);

	    minorWidth = majorWidth / nTics;

	    /*Minor and major tics first*/
	    temp = start / majorWidth;
	    curValue = temp * majorWidth;
	    while (curValue > start)
	    {
		 curValue -= majorWidth;
	    }
	    k = 0;

	    while (curValue < start)
	    {
		++k;
		if (k >= nTics) k = 0;

		curValue += minorWidth;
	    }

	    SetupFont(CBBIGTEXTFONT, CBBIGTEXTSIZE);

	    /*Now actually draw them*/
	    if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
	    while (curValue <= finish + ddiff * 1.0E-6)
	    {
		int strOff;
		pixel = l + (curValue - start) * (r - l) / (ddiff);
		if (k == 0)
		{
		    /*Major tic*/
		    DrawUILine(pixel,
			    top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP,
			    pixel,
			    top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP + CBMAJORTICLEN,
			    UIBLACK);

		    sprintf(tempStr, "%lg", curValue);
		    strOff = StrWidth(tempStr) / 2;
		    if (pixel - strOff > l - CBLBORDER && pixel + strOff < r + CBRBORDER)
		    {
			DrawString(pixel - strOff, top - CBTBORDER - CBTEXTDOWN, tempStr);
		    }
		}
		else
		{
		    /*Minor tic*/
		    DrawUILine(pixel,
			    top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP,
			    pixel,
			    top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP + CBMAJORTICLEN / 2,
			    UIBLACK);
		}

		curValue += minorWidth;
		if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
		++k;
		if (k >= nTics) k = 0;
	    }

	    /*Draw the function box, if any*/
	    var = GetVar(colorBar, FUNCTIONBOX);
	    if (var)
	    {
		int boxL, boxR, boxB, boxT, boxX, boxY;
		real *elements;
		elements = ELEMENTS(var);
		boxL = l + (elements[0] - start) * (r - l) / (ddiff);
		boxR = l + (elements[1] - start) * (r - l) / (ddiff);
		boxB = b + elements[2] * (t - b) / 255.0;
		boxT = b + elements[3] * (t - b) / 255.0;
		boxX = (boxL + boxR) / 2;
		boxY = (boxB + boxT) / 2;

		SetClipRect(left + 3, right - 3, bottom + 3, top - 3);

		/*Now draw the box and its handles*/
		FrameUIRect(boxL + 1, boxR + 2, boxB - 2, boxT - 1, UIBLACK);
		FrameUIRect(boxL + 1 - CBHANDLESIZE, boxL + 1,
			    boxY - 1 - CBHANDLESIZE / 2, boxY - 1 + CBHANDLESIZE / 2, UIBLACK);
		FrameUIRect(boxR + 2, boxR + 2 + CBHANDLESIZE,
			    boxY - 1 - CBHANDLESIZE / 2, boxY - 1 + CBHANDLESIZE / 2, UIBLACK);
		FrameUIRect(boxX + 2 - CBHANDLESIZE / 2, boxX + 2 + CBHANDLESIZE / 2,
			    boxT - 1, boxT - 1 + CBHANDLESIZE, UIBLACK);
		FrameUIRect(boxX + 2 - CBHANDLESIZE / 2, boxX + 2 + CBHANDLESIZE / 2,
			    boxB - 2 - CBHANDLESIZE, boxB - 2, UIBLACK);

		FrameUIRect(boxL, boxR, boxB, boxT, UIYELLOW);
		FrameUIRect(boxL - 1, boxR + 1, boxB - 1, boxT + 1, UIYELLOW);

		FillUIRect(boxL - 1 - CBHANDLESIZE, boxL,
			    boxY - CBHANDLESIZE / 2, boxY + 1 + CBHANDLESIZE / 2, UIYELLOW);
		FillUIRect(boxR, boxR + 1 + CBHANDLESIZE,
			    boxY - CBHANDLESIZE / 2, boxY + 1 + CBHANDLESIZE / 2, UIYELLOW);
		FillUIRect(boxX - CBHANDLESIZE / 2, boxX + 1 + CBHANDLESIZE / 2,
			    boxT, boxT + 1 + CBHANDLESIZE, UIYELLOW);
		FillUIRect(boxX - CBHANDLESIZE / 2, boxX + 1 + CBHANDLESIZE / 2,
			    boxB - 1 - CBHANDLESIZE, boxB, UIYELLOW);
		RestoreClipRect();
	    }
	}
    }

#endif
    return ObjTrue;
}

ObjPtr SetFunctionBox(colorBar, functionBox)
ObjPtr colorBar;
ObjPtr functionBox;
/*Sets the FUNCTIONBOX of colorBar to functionBox, also logs*/
{
    real *elements;
    PPtr repObj;
    if (functionBox && (!IsArray(functionBox) || RANK(functionBox) != 1 || 
	DIMS(functionBox)[0] != 4))
    {
	ReportError("SetFunctionBox", "Bad value given");
	return ObjFalse;
    }
    repObj = (PPtr) GetPaletteVar("SetFunctionBox", colorBar, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }
    if (functionBox)
    {
	elements = ELEMENTS(functionBox);
	if (elements[2] < -0.5 || elements[2] >= 255.5 ||
		elements[3] < -0.5 || elements[3] >= 255.5 ||
		elements[2] > elements[3] ||
		elements[0] > elements[1])
	{
	    ReportError("SetFunctionBox", "Value out of range");
	    return ObjFalse;
	}
    }

    SetVar(colorBar, FUNCTIONBOX, functionBox);

    if (logging)
    {
	char cmd[256];
	char *s;

	sprintf(cmd, "set functionbox ");
	s = &(cmd[0]);
	while (*s) ++s;
	MakeObjectName(s, colorBar);
	while (*s) ++s;
	*s++ = ' ';
	PrintScriptObject(s, functionBox);
	while (*s) ++s;
	*s++ = '\n';
	*s = 0;
	Log(cmd);
    }
    return ObjTrue;
}

void LogColorChange(colorBar, colors, newColor)
ObjPtr colorBar;
short3 *colors;
int newColor;
/*Logs a change to colors[newColor] within colorBar*/
{
    if (logging)
    {
	char cmd[256];
	char *s;

	sprintf(cmd, "set color ");
	s = &(cmd[0]);
	while (*s) ++s;
	MakeObjectName(s, colorBar);
	while (*s) ++s;
	*s++ = ' ';
	sprintf(s, "%d %d %d %d\n", newColor, colors[newColor][0], 
			colors[newColor][1], colors[newColor][2]);
	Log(cmd);
    }
}

ObjPtr SetColorBarColor(colorBar, whichColor, r, g, b)
ObjPtr colorBar;
int whichColor, r, g, b;
/*Sets color whichColor from inside a color bar*/
{
    PPtr palette;
    short3 *colors;

    palette = (PPtr) GetPaletteVar("SetColorBarColor", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    colors = palette -> colors;
    colors[whichColor][0] = r;
    colors[whichColor][1] = g;
    colors[whichColor][2] = b;

    ImInvalid(colorBar);
    LogColorChange(colorBar, colors, whichColor);
    ChangedValue(colorBar);

    CopyColorsToComponents(palette, whichColor, whichColor);

#ifdef GRAPHICS
    MapPaletteColors(palette, whichColor, whichColor);
#endif
}

static ObjPtr PressColorBar(colorBar, x, y, flags)
ObjPtr colorBar;
int x, y;
int flags;
/*Does a press in a color bar beginning at x and y.  Returns
  true iff the press really was in the control.*/
{
#ifdef INTERACTIVE
    int left, right, bottom, top;
    int l, r, b, t;
    int k;
    int cm;

    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

    if (x >= left && x <= right && y >= bottom && y <= top)
    {
	real val[3];
	ObjPtr value, var;
	real *origVal;
	ObjPtr palette;
	/*Hey!  It really was a click in the time control*/

	if (TOOL(flags) == T_HELP)
	{
	    ContextHelp(colorBar);
	    return ObjTrue;
	}

	b = bottom + CBBORDER + CBTEXTUP + CBTEXTSEP;
	if (y < b)
	{
	    /*Click below*/
	    val[0] = val[1] = val[2] = 0.0;
	    value = NewRealArray(1, 3L);
	    CArray2Array(value, val);
	    SetValue(colorBar, value);
	    return ObjTrue;
	}

	/*Get current value*/
	value = GetFixedArrayVar("PressColorBar", colorBar, VALUE, 1, 3L);
	if (!value)
	{
	    return ObjFalse;
	}

	origVal = (real *) ELEMENTS(value);

	palette = (ObjPtr) GetPaletteVar("PressColorBar", colorBar, REPOBJ);
	if (!palette)
	{
	    return ObjFalse;
	}

	/*Get the color model*/
	var = GetIntVar("PressColorBar", (ObjPtr) palette, COLORMODEL);
	if (!var)
	{
	    return ObjFalse;
	}
	cm = GetInt(var);

	/*See if it's in one of the components or full color*/
	for (k = 0; k < 4; ++k)
	{
	    t = b + CBCOMPHEIGHT;
	    if (y <= t && y >= b)
	    {
		ObjPtr radio;
		int newX, newY;
		int startColor;
		int lastColor;
		int newColor;
		Bool inp;			/*True if inside*/

		/*It's a press in track k*/
		InhibitLogging(true);

		if ((flags & F_SHIFTDOWN) || TOOL(flags) == T_ROTATE)
		{
		    /*Shift click, determine start from farthest*/
		    newColor = PixelToColor(colorBar, x);
		    if (ABS(newColor - origVal[2]) < ABS(newColor - origVal[1]))
		    {
			startColor = origVal[1];
		    }
		    else
		    {
			startColor = origVal[2];
		    }
		}
		else
		{
		    startColor = PixelToColor(colorBar, x);
		}
		lastColor = -1;

		val[0] = (real) (k + 1);

		inp = true;

		/*Set the edit tool to free form*/
		ResetColorBarTools(colorBar);

		while (Mouse(&newX, &newY))
		{
		    if (newY > t + SLOP ||
			newY < b - SLOP ||
			newX < left - SLOP ||
			newX > right + SLOP)
		    {
			/*It's outside*/
			if (inp)
			{
			    /*Transition from in to out*/
			    SetVar(colorBar, VALUE, value);
			    ChangedValue(colorBar);
			    inp = false;
			    lastColor = -1;
			    DrawMe(colorBar);
			}
		    }
		    else
		    {
			/*It's inside*/
			if (!inp)
			{
			    /*Transition from out to in*/
			    inp = true;
			}
			newColor = PixelToColor(colorBar, newX);
			if (newColor != lastColor)
			{
			    ObjPtr var;

			    /*It's a new color, set it*/
			    if (newColor >= startColor)
			    {
				val[1] = (real) startColor;
				val[2] = (real) newColor;
			    }
			    else
			    {
				val[2] = (real) startColor;
				val[1] = (real) newColor;
			    }
			     var = NewRealArray(1, 3L);
			    CArray2Array(var, val);
			    SetVar(colorBar, VALUE, var);
			    ChangedValue(colorBar);
			    DrawMe(colorBar);
			    lastColor = newColor;
			}
		    }
		}
		InhibitLogging(false);
		LogControl(colorBar);
		return ObjTrue;
	    }
	    b = t + CBVGAP;
	}

	/*It wasn't in a component, maybe it's in the readout*/
	t = top - CBTBORDER - CBTEXTDOWN - CBTEXTDOWNSEP - 1;
	l = left + CBLBORDER + 1;
	r = right - CBRBORDER - 1;
	if (origVal[0] > 1.0 &&
	    y >= b - SLOP && y <= t + SLOP && x >= l - SLOP && x <= r + SLOP)
	{
	    /*It is!  Do stuff based on edit mode*/
	    int editMode;
	    int lastColor, lastAmp;
	    PPtr palette;
	    int index1, index2;
	    int k;
	    int comp;
	    short3 *colors;
	    int newX, newY, newColor, newAmp;
	    ObjPtr panel, button;

	    palette = (PPtr) GetPaletteVar("PressColorBar", colorBar, REPOBJ);
	    if (!palette)
	    {
		return ObjFalse;
	    }

	    colors = palette -> colors;

	    comp = 4 - origVal[0];

	    var = GetIntVar("PressColorBar", colorBar, EDITMODE);
	    if (!var)
	    {
		return ObjFalse;
	    }
	    editMode = GetInt(var);

	    index1 = origVal[1] + 0.5;
	    index2 = origVal[2] + 0.5;

	    panel = GetVar(colorBar, PARENT);
	    if (panel)
	    {
		button = GetVar(panel, KEEPBUTTON);
		if (button)
		{
		    ActivateButton(button, true);
		}
		button = GetVar(panel, REVERTBUTTON);
		if (button)
		{
		    ActivateButton(button, true);
		}
	    }

	    if (editMode == PT_FREEFORM)
	    {
		int firstAltered, lastAltered;
		Bool constrainX, constrainY;
		/*Free form*/
		lastColor = lastAmp = -1;

		firstAltered = index2 + 1;
		lastAltered = index1 - 1;

		constrainX = constrainY = false;

		while (Mouse(&newX, &newY))
		{
		    if (newX >= l - SLOP && newX <= r + SLOP && newY >= b - SLOP && newY <= t + SLOP)
		    {
			
			if (flags & F_SHIFTDOWN)
			{
			    if (!constrainX && !constrainY)
			    {
				int xDiff, yDiff;

				/*Must make some constraints*/
				xDiff = ABS(newX - x);
				yDiff = ABS(newY - y);
				if (xDiff >= MINCONSTRAINT &&
				    yDiff >= MINCONSTRAINT)
				{
				    if (yDiff > xDiff)
				    {
					constrainX = true;
				    }
				    else
				    {
					constrainY = true;
				    }
				}
			    }
			    if (constrainX) newX = x;
			    if (constrainY) newY = y;
			}

			/*Calculate newAmp*/
			newAmp = ((newY - b) + 0.5) * 255.0 / (real) (t - b - 1);
			if (newAmp < 0) newAmp = 0;
			else if (newAmp > 255) newAmp = 255;

			/*Calculate newColor*/
			newColor = index1 + ((real) newX - l) * ((real) index2 - index1 + 1) / ((real) (r - l - 1));	    	
			if (newColor < index1) newColor = index1;
			else if (newColor > index2) newColor = index2;

			if (newColor < firstAltered)
			{
			    firstAltered = newColor;
			}
			if (newColor > lastAltered)
			{
			    lastAltered = newColor;
			}

			if (newAmp != lastAmp || newColor != lastColor)
			{
			    /*Set the new color to be correct*/
			    
			    if (lastColor <= -1 || lastColor == newColor)
			    {
				/*New in this range, just set this color*/
				SetColorComponent(palette, newColor, comp, newAmp);
#ifdef GRAPHICS
				MapPaletteColors(palette, newColor, newColor);
#endif
			    }
			    else if (newColor > lastColor)
			    {
				int nc;
				/*Linearly interpolate up to this color*/
				for (k = lastColor + 1; k <= newColor; ++k)
				{
				    nc = ((newColor - k) * lastAmp +
					  (k - lastColor) * newAmp) / 
						(newColor - lastColor);
				    if (nc < 0) nc = 0;
				    else if (nc > 255) nc = 255;

				    SetColorComponent(palette, k, comp, nc);

				}
#ifdef GRAPHICS
				MapPaletteColors(palette, lastColor, newColor);
#endif
			    }
			    else if (newColor < lastColor)
			    {
				int nc;
				/*Linearly interpolate down to this color*/
				for (k = lastColor - 1; k >= newColor; --k)
				{
				    nc = ((k - newColor) * lastAmp +
					  (lastColor - k) * newAmp) / 
						(lastColor - newColor);
				    if (nc < 0) nc = 0;
				    else if (nc > 255) nc = 255;

				    SetColorComponent(palette, k, comp, nc);
				}
#ifdef GRAPHICS
				MapPaletteColors(palette, lastColor, newColor);
#endif
			    }
			    ChangedValue(colorBar);
			    DrawMe(colorBar);
 			    lastAmp = newAmp;
			    lastColor = newColor;
			}
		    }
		    else
		    {
			lastColor = lastAmp = -1;
		    }
		}
		for (k = firstAltered; k <= lastAltered; ++k)
		{
		    LogColorChange(colorBar, colors, k);
		}
		SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
		ForAllVisWindows(ImInvalid);
	    }
	    else
	    {
		/*It's a tool*/
		ObjPtr toolBounds;
		int boxL, boxR, boxB, boxT;
		int xOffset, yOffset;
		double start, finish, ddiff, halfSpace;
		real *elements;
		real newElements[4];
		int whichMove = 0;
		real lastSide, nextSide;
		double majorWidth, minorWidth;
		int nTics;
		long temp;

#define MOVEBOXTOP 	1
#define MOVEBOXBOTTOM	2
#define MOVEBOXLEFT	3
#define MOVEBOXRIGHT	4
#define MOVEBOXLR	5

		halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
		start = GetColorValue((ObjPtr) palette, index1) - halfSpace;
		finish = GetColorValue((ObjPtr) palette, index2) + halfSpace;
		ddiff = finish - start;

		toolBounds = GetFixedArrayVar("PressColorBar", colorBar, FUNCTIONBOX, 1, 4L);
		if (!toolBounds)
		{
		    return ObjFalse;
		}
		elements = ELEMENTS(toolBounds);
	
		boxL = l + (elements[0] - start) * (r - l) / (ddiff);
		boxR = l + (elements[1] - start) * (r - l) / (ddiff);
		boxB = b + elements[2] * (t - b) / 255.0;
		boxT = b + elements[3] * (t - b) / 255.0;

		/*Figure out which side is clicked on*/

		whichMove = 0;
		if (y >= boxB - (CBHANDLESIZE + 2) && y <= boxT + (CBHANDLESIZE + 2))
		{
		    if (x >= boxL - (CBHANDLESIZE + 2) && x <= boxL)
		    {
			whichMove = MOVEBOXLEFT;
			xOffset = x - boxL;
		    }
		    else if (x >= boxR && x <= boxR + (CBHANDLESIZE + 2))
		    {
			whichMove = MOVEBOXRIGHT;
			xOffset = x - boxR;
		    }
		}
		if (x >= boxL - (CBHANDLESIZE + 2) && x <= boxR + (CBHANDLESIZE + 2))
		{
		    if (y >= boxB - (CBHANDLESIZE + 2) && y <= boxB)
		    {
			whichMove = MOVEBOXBOTTOM;
			yOffset = y - boxB;
		    }
		    else if (y >= boxT && y <= boxT + (CBHANDLESIZE + 2))
		    {
			whichMove = MOVEBOXTOP;
			yOffset = y - boxT;
		    }
		} 
		if (!whichMove)
		{
		    whichMove = MOVEBOXLR;
		    xOffset = x - boxL;
		    yOffset = y - (boxT + boxB) / 2;
		}

		newElements[0] = elements[0];
		newElements[1] = elements[1];
		newElements[2] = elements[2];
		newElements[3] = elements[3];

		if (flags & F_SHIFTDOWN)
		{
		    /*Constrained motion, have to calc constraint stuff*/
		    halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
		    start = GetColorValue((ObjPtr) palette, index1) - halfSpace;
		    finish = GetColorValue((ObjPtr) palette, index2) + halfSpace;
		    ddiff = finish - start;
	
		    CalcGoodSteps(ddiff,
			      r - l,
			      CBSTEPPIXELS,
			      &majorWidth, &nTics);
		    minorWidth = majorWidth / nTics;
		}

		/*Now do it*/
		if (whichMove == MOVEBOXLEFT || whichMove == MOVEBOXRIGHT)
		{
		    lastSide = newElements[whichMove == MOVEBOXLEFT ? 0 : 1];
		    while (Mouse(&newX, &newY))
		    {		
			nextSide = start +
				(newX - xOffset - l) * (finish - start) / (r - l);
			if (flags & F_SHIFTDOWN)
			{
			    /*Constrain*/
			    temp = nextSide / minorWidth + 0.5;
			    nextSide = temp * minorWidth;
			}

			if (nextSide != lastSide)
			{
			    /*Change it*/

			    newElements[whichMove == MOVEBOXLEFT ? 0 : 1] = nextSide;
			    if (newElements[1] > newElements[0])
			    {
				toolBounds = NewRealArray(1, 4L);
				CArray2Array(toolBounds, newElements);
				SetFunctionBox(colorBar, toolBounds);
				InhibitLogging(true);
				ImposeColorFunction(colorBar);
				InhibitLogging(false);
				DrawMe(colorBar);
				lastSide = nextSide;
			    }
			}
		    }
		}
		else if (whichMove == MOVEBOXTOP || whichMove == MOVEBOXBOTTOM)
		{
		    lastAmp = newElements[whichMove == MOVEBOXBOTTOM ? 2 : 3] + 0.5;
		    while (Mouse(&newX, &newY))
		    {		
			newAmp = ((newY - yOffset - b) + 0.5) * 255.0 / (real) (t - b - 1);
			if (newAmp < 0) newAmp = 0;
			else if (newAmp > 255) newAmp = 255;
			if (newAmp != lastAmp)
			{
			    /*Change it*/

			    newElements[whichMove == MOVEBOXBOTTOM ? 2 : 3] = newAmp;
			    if (newElements[3] > newElements[2])
			    {
				toolBounds = NewRealArray(1, 4L);
				CArray2Array(toolBounds, newElements);
				SetFunctionBox(colorBar, toolBounds);
				InhibitLogging(true);
				ImposeColorFunction(colorBar);
				InhibitLogging(false);
				DrawMe(colorBar);
				lastAmp = newAmp;
			    }
			}
		    }
		}
		if (whichMove == MOVEBOXLR)
		{
		    lastSide = newElements[0];
		    while (Mouse(&newX, &newY))
		    {		
			nextSide = start +
				(newX - xOffset - l) * (finish - start) / (r - l);
			if (flags & F_SHIFTDOWN)
			{
			    /*Constrain*/
			    temp = nextSide / minorWidth + 0.5;
			    nextSide = temp * minorWidth;
			}
			if (nextSide != lastSide)
			{
			    /*Change it*/

			    newElements[0] = nextSide;
			    newElements[1] = nextSide + elements[1] - elements[0];
			    if (newElements[1] > newElements[0])
			    {
				toolBounds = NewRealArray(1, 4L);
				CArray2Array(toolBounds, newElements);
				SetFunctionBox(colorBar, toolBounds);
				InhibitLogging(true);
				ImposeColorFunction(colorBar);
				InhibitLogging(false);
				DrawMe(colorBar);
				lastSide = nextSide;
			    }
			}
		    }
		}
		SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
		ForAllVisWindows(ImInvalid);
	    }
	}

	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
#else
    return ObjFalse;
#endif
}


ObjPtr NewColorBar(left, right, bottom, top, name)
int left, right, bottom, top;
char *name;
/*Makes a new hue/saturation control in left, right, bottom, top*/
{
    ObjPtr retVal;
    ObjPtr value;
    
    retVal = NewObject(colorBarClass, 0);
    Set2DIntBounds(retVal, left, right, bottom, top);
    SetVar(retVal, NAME, NewString(name));
    SetVar(retVal, TYPESTRING, NewString("color bar control"));
    value = NewRealArray(1, 3L);
    ((real *) ELEMENTS(value))[0] = 1.0;
    ((real *) ELEMENTS(value))[1] = 0.0;
    ((real *) ELEMENTS(value))[1] = 0.0;
    SetVar(retVal, VALUE, value);
    return retVal;
}

#ifdef PROTO
static void ChoosePaletteSliderValue(ObjPtr, PPtr, int, int, int);
#endif 

static void ChoosePaletteSliderValue(slider, palette, component, index1, index2)
ObjPtr slider;
PPtr palette;
int index1, index2, component;
/*Chooses a value for the slider based on colors index1 through index2 of
  palette.  Component is 0 for rgb, 1 for r, 2 for g, and 3 for b, */
{
    real min, max, testVal;
    short3 *colors;
    real hsv[3];
    int k;

    colors = palette -> colors;
    min = 1.0;
    max = 0.0;
    if (component)
    {
	for (k = index1; k <= index2; ++k)
	{
	    testVal = ((real) GetColorComponent(palette, k, component - 1)) / 255.0;
	    if (testVal < min)
	    {
		min = testVal;
	    }
	    if (testVal > max)
	    {
		max = testVal;
	    }
	}	
    }
    else
    {
	for (k = index1; k <= index2; ++k)
	{
	    RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]),
		((real) colors[k][0]) / 255.0, 
		((real) colors[k][1]) / 255.0, 
		((real) colors[k][2]) / 255.0);
	    testVal = hsv[2];
	    if (testVal < min)
	    {
		min = testVal;
	    }
	    if (testVal > max)
	    {
		max = testVal;
	    }
	}
    }
    testVal = (min + max) * 0.5;
    if (testVal < 0.0) testVal = 0.0;
    if (testVal > 1.0) testVal = 1.0;
    SetSliderValue(slider, testVal);
    SetVar(slider, INITVALUE, NewReal(testVal));
    SetVar(slider, TEMPPALETTE, ClonePalette((ObjPtr) palette));
}

static ObjPtr ChangePaletteBar(colorBar)
ObjPtr colorBar;
/*Changed value for a color bar that controls a palette*/
{
    ObjPtr colorWheel, slider, button, buttons;
    ThingListPtr buttonList;
    PPtr palette;
    ObjPtr value, newValue;
    FuncTyp changedValue;
    real hsv[3];
    short3 *colors;
    int index;
    real *elements;

    value = GetFixedArrayVar("ChangePaletteBar", colorBar, VALUE, 1, 3L);
    colorWheel = GetObjectVar("ChangePaletteBar", colorBar, COLORWHEEL);
    palette = (PPtr) GetPaletteVar("ChangePaletteBar", colorBar, REPOBJ);
    slider = GetObjectVar("ChangePaletteBar", colorBar, SLIDER);

    if (!colorWheel || !palette || !value || !slider)
    {
	return ObjFalse;
    }

    elements = ELEMENTS(value);
    if (elements[0] == 1.0)
    {
	/*Active color wheel*/
	ActivateColorWheel(colorWheel, true);
	if (elements[1] == elements[2])
	{
	    /*Give it a value*/
	    colors = palette -> colors;

	    /*Update the left slider and color wheel*/
	    index = elements[1];

	    RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]),
		    ((real) colors[index][0]) / 255.0, 
		    ((real) colors[index][1]) / 255.0, 
		    ((real) colors[index][2]) / 255.0);
	    if (hsv[2] < 0.0) hsv[2] = 0.0;
	    else if (hsv[2] > 1.0) hsv[2] = 1.0;
	}
	else
	{
	    /*No value*/
	    newValue = NULLOBJ;
	}
	changedValue = GetMethod(colorWheel, CHANGEDVALUE);
	SetMethod(colorWheel, CHANGEDVALUE, 0);
	newValue = NewRealArray(1, 2L);
	((real *) ELEMENTS(newValue))[0] = hsv[0];
	((real *) ELEMENTS(newValue))[1] = hsv[1];
	SetValue(colorWheel, newValue);
	SetMethod(colorWheel, CHANGEDVALUE, changedValue);
    }
    else
    {
	ActivateColorWheel(colorWheel, false);
    }

    buttons = GetVar(colorBar, FULLCOMPBUTTONS);
    if (buttons)
    {
	buttonList = LISTOF(buttons);
    }
    else
    {
	buttonList = 0;
    }

    if (elements[0] > 0.0)
    {
	/*Active Slider and buttons*/
	int index1, index2;
	int comp;

	changedValue = GetMethod(slider, CHANGEDVALUE);
	SetMethod(slider, CHANGEDVALUE, 0);
	ActivateSlider(slider, true);

	/*Get a value for the slider*/
	index1 = elements[1];
	index2 = elements[2];
	if (elements[0] == 1.0)
	{
	    comp = 0;
	}
	else
	{
	    comp = 5 - (int) elements[0];
	}
	ChoosePaletteSliderValue(slider, palette, comp,
		index1, index2);
	SetMethod(slider, CHANGEDVALUE, changedValue);

	/*Activate buttons*/
	while (buttonList)
	{
	    ActivateButton(buttonList -> thing, true);
	    buttonList = buttonList -> next;
	}
    }
    else
    {
	/*Inactive Slider*/
	ActivateSlider(slider, false);

	/*Deactivate buttons*/
	while (buttonList)
	{
	    ActivateButton(buttonList -> thing, false);
	    buttonList = buttonList -> next;
	}
    }
    buttons = GetVar(colorBar, COMPBUTTONS);
    if (buttons)
    {
	buttonList = LISTOF(buttons);
    }
    else
    {
	buttonList = 0;
    }
    if (elements[0] > 1.0 && elements[2] > elements[1])
    {
	/*Activate buttons*/
	while (buttonList)
	{
	    ActivateButton(buttonList -> thing, true);
	    buttonList = buttonList -> next;
	}
    }
    else
    {
	/*Deactivate buttons*/
	while (buttonList)
	{
	    ActivateButton(buttonList -> thing, false);
	    buttonList = buttonList -> next;
	}
    }

    button = GetVar(colorBar, FREEFORMBUTTON);
    if (elements[0] > 1.0)
    {
 	if (button)
	{
	    ActivateButton(button, true);
	}
    }
    else
    {
 	if (button)
	{
	    ActivateButton(button, false);
	}
    }

    ImInvalid(colorBar);
    return ObjTrue;
}

static ObjPtr ChangePaletteColorWheel(colorWheel)
ObjPtr colorWheel;
/*Changes a color wheel*/
{
    ObjPtr slider, colorBar, panel, button; 
    PPtr palette;
    ObjPtr var;
    real h, s, v;
    real r, g, b;
    int rs, gs, bs;
    short3 *colors;
    int index1, index2, k;
    real *elements;

    slider = GetObjectVar("ChangePaletteColorWheel", colorWheel, SLIDER);
    colorBar = GetObjectVar("ChangePaletteColorWheel", colorWheel, COLORBAR);

    if (!slider || !colorBar)
    {
	return ObjFalse;
    }

    palette = (PPtr) GetPaletteVar("ChangePaletteColorWheel", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    var = GetValue(colorBar);
    if (!var)
    {
	return ObjFalse;
    }
    if (((real *) ELEMENTS(var))[0] != 1.0)
    {
	return ObjFalse;
    }
    index1 = ((real *) ELEMENTS(var))[1];
    index2 = ((real *) ELEMENTS(var))[2];

    var = GetValue(colorWheel);
    if (!var)
    {
	return ObjFalse;
    }

    h = ((real *) ELEMENTS(var))[0];
    s = ((real *) ELEMENTS(var))[1];
    var = GetValue(slider);
    if (!var)
    {
	return ObjFalse;
    }
    v = GetReal(var);

    HSV2RGB(&r, &g, &b, h, s, v);

    /*Change the palette.  Tricky.*/
    rs = r * 255.0;
    gs = g * 255.0;
    bs = b * 255.0;
    if (rs > 255) rs = 255;
    else if (rs < 0) rs = 0;
    if (gs > 255) gs = 255;
    else if (gs < 0) gs = 0;
    if (bs > 255) rs = 255;
    else if (bs < 0) bs = 0;

    colors = palette -> colors;
    for (k = index1; k <= index2; ++k)
    {
	colors[k][0] = rs;
	colors[k][1] = gs;
	colors[k][2] = bs;
    }

    CopyColorsToComponents(palette, index1, index2);

#ifdef GRAPHICS
    MapPaletteColors(palette, index1, index2);
#endif


    SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
    panel = GetVar(colorWheel, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

    SetVar(slider, TEMPPALETTE, ClonePalette((ObjPtr) palette));
/*    interactiveMoving = false;*/
    ForAllVisWindows(ImInvalid);
    DrawMe(colorBar);
    return ObjTrue;
}

static ObjPtr ChangePaletteSlider(slider)
ObjPtr slider;
/*Changes a palette's slider*/
{
    ObjPtr colorBar, panel, button; 
    PPtr palette, tempPalette;
    ObjPtr var;
    short rs, gs, bs;
    short3 *colors, *tempColors;
    int index1, index2, k, comp;
    real sliderValue, initValue;
    real change;
    ObjPtr radio;
    ObjPtr colorWheel, hsObj;

    colorBar = GetObjectVar("ChangePaletteSlider", slider, COLORBAR);
    tempPalette = (PPtr) GetPaletteVar("ChangePaletteSlider", slider, TEMPPALETTE);

    if (!colorBar || !tempPalette)
    {
	return ObjFalse;
    }

    palette = (PPtr) GetPaletteVar("ChangePaletteSlider", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    colorWheel = GetObjectVar("ChangePaletteSlider", colorBar, COLORWHEEL);
    if (!colorWheel)
    {
	return ObjFalse;
    }

    var = GetRealVar("ChangePaletteSlider", slider, INITVALUE);
    if (var)
    {
	initValue = GetReal(var);
    }
    else
    {
	return ObjFalse;
    }

    var = GetValue(colorBar);
    if (!var)
    {
	return ObjFalse;
    }
    
    comp = 5 - ((real *) ELEMENTS(var))[0];
    if (comp == 4) comp = 0;
    if (comp < 0)
    {
	return ObjFalse;
    }
    index1 = ((real *) ELEMENTS(var))[1];
    index2 = ((real *) ELEMENTS(var))[2];

    var = GetValue(slider);
    if (!var)
    {
	return ObjFalse;
    }
    sliderValue = GetReal(var);

    colors = palette -> colors;
    tempColors = tempPalette -> colors;

    if (sliderValue < initValue)
    {
	/*Attenuate value*/
	change = sliderValue / initValue;

	if (comp == 0)
	{
	    /*Full color*/
	    real h, s, v, dummy;
	    real r, g, b;

	    for (k = index1; k <= index2; ++k)
	    {
		r = tempColors[k][0] / 255.0;
		g = tempColors[k][1] / 255.0;
		b = tempColors[k][2] / 255.0;

		RGB2HSV(&h, &s, &v, r, g, b);
		if (index1 == index2)
		{
		    hsObj = GetValue(colorWheel);
		    if (!hsObj || !IsArray(hsObj) || RANK(hsObj) != 1 || DIMS(hsObj)[0] != 2)
		    {
			return ObjFalse;
		    }
		    /*Single color, refresh value from wheel*/
		    h = ((real *) ELEMENTS(hsObj))[0];
		    s = ((real *) ELEMENTS(hsObj))[1];
		    v = sliderValue;
		}
		else
		{
		    v *= change;
		}
		if (v > 1.0) v = 1.0;
		if (v < 0.0) v = 0.0;
		HSV2RGB(&r, &g, &b, h, s, v);

		rs = r * 255.0 + 0.5;
		gs = g * 255.0 + 0.5;
		bs = b * 255.0 + 0.5;
		colors[k][0] = rs;
		colors[k][1] = gs;
		colors[k][2] = bs;
	    }
	    CopyColorsToComponents(palette, index1, index2);
	}
	else
	{
	    real component;

	    for (k = index1; k <= index2; ++k)
	    {
		component = ((real) GetColorComponent(tempPalette, k, comp - 1)) / 255.0;
		component *= change;
		if (component > 1.0) component = 1.0;
		else if (component < 0.0) component = 0.0;
	    	SetColorComponent(palette, k, comp - 1, (int) (component * 255.0 + 0.5));
	    }
	    CopyComponentsToColors(palette, index1, index2);
	}
#ifdef GRAPHICS
	MapPaletteColors(palette, index1, index2);
#endif
    }
    else
    {
	/*Brighten value*/
	change = (1.0 - sliderValue) / (1.0 - initValue);
	if (comp == 0)
	{
	    /*Full color*/
	    real h, s, v, dummy;
	    real r, g, b;

	    for (k = index1; k <= index2; ++k)
	    {
		r = tempColors[k][0] / 255.0;
		g = tempColors[k][1] / 255.0;
		b = tempColors[k][2] / 255.0;

		RGB2HSV(&h, &s, &v, r, g, b);
		if (index1 == index2)
		{
		    /*Single color, refresh value from wheel*/
		    hsObj = GetValue(colorWheel);
		    if (!hsObj || !IsArray(hsObj) || RANK(hsObj) != 1 || DIMS(hsObj)[0] != 2)
		    {
			return ObjFalse;
		    }
		    h = ((real *) ELEMENTS(hsObj))[0];
		    s = ((real *) ELEMENTS(hsObj))[1];
		    v = sliderValue;
		}
		else
		{
		    v = 1.0 - (1.0 - v) * change;
		}
		if (v > 1.0) v = 1.0;
		if (v < 0.0) v = 0.0;
		HSV2RGB(&r, &g, &b, h, s, v);

		rs = r * 255.0 + 0.5;
		gs = g * 255.0 + 0.5;
		bs = b * 255.0 + 0.5;
		colors[k][0] = rs;
		colors[k][1] = gs;
		colors[k][2] = bs;
	    }
	    CopyColorsToComponents(palette, index1, index2);
	}
	else
	{
	    real component;

	    for (k = index1; k <= index2; ++k)
	    {
		component = ((real) GetColorComponent(tempPalette, k, comp - 1)) / 255.0;
		component = 1.0 - (1.0 - component) * change;
		if (component > 1.0) component = 1.0;
		else if (component < 0.0) component = 0.0;
	    	SetColorComponent(palette, k, comp - 1, (int) (component * 255.0 + 0.5));
	    }
	    CopyComponentsToColors(palette, index1, index2);
	}
#ifdef GRAPHICS
	MapPaletteColors(palette, index1, index2);
#endif
    }

    ResetColorBarTools(colorBar);

    SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
    panel = GetVar(slider, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

/*    interactiveMoving = false;*/
    ForAllVisWindows(ImInvalid);
    DrawMe(colorBar);
    return ObjTrue;
}

static ObjPtr RevertButton(button)
ObjPtr button;
/*Reverts to last palette saved*/
{
    ObjPtr panel, repObj, keptPalette, colorBar, textBox, radio;

    repObj = GetPaletteVar("RevertButton", button, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }
    keptPalette = GetPaletteVar("RevertButton", repObj, KEPTPALETTE);
    if (!keptPalette)
    {
	return ObjFalse;
    }
    CopyPalette(repObj, keptPalette);
    SetVar(repObj, CHANGED, ObjTrue);
    ActivateButton(button, false);
    if (panel = GetObjectVar("RevertButton", button, PARENT))
    {
	if (button = GetObjectVar("RevertButton", panel, KEEPBUTTON))
	{
	    ActivateButton(button, false);
	}
    }

    if (colorBar = GetObjectVar("RevertButton", panel, COLORBAR))
    {
	ObjPtr var;
	real newValue[3];

	InhibitLogging(true);
	newValue[0] = newValue[1] = newValue[2] = 0.0;
	var = NewRealArray(1, 3L);
	CArray2Array(var, newValue);
	SetValue(colorBar, var);
	InhibitLogging(false);
    }

    /*Change the text boxes*/
    if (textBox = GetObjectVar("RevertButton", panel, NCOLORBOX))
    {
	sprintf(tempStr, "%d", ((PPtr) repObj) -> nColors);
	SetValue(textBox, NewString(tempStr));
    }
    if (textBox = GetObjectVar("RevertButton", panel, MINBOX))
    {
	sprintf(tempStr, "%g", ((PPtr) repObj) -> min);
	SetValue(textBox, NewString(tempStr));
    }
    if (textBox = GetObjectVar("RevertButton", panel, MAXBOX))
    {
	sprintf(tempStr, "%g", ((PPtr) repObj) -> max);
	SetValue(textBox, NewString(tempStr));
    }
    if (radio = GetObjectVar("RevertButton", panel, CMODELRADIO))
    {
	FuncTyp method;
	method = GetMethod(radio, CHANGEDVALUE);
	SetMethod(radio, CHANGEDVALUE, (FuncTyp) 0);
	SetValue(radio, GetVar(repObj, COLORMODEL));
	SetMethod(radio, CHANGEDVALUE, method);
    }

    ForAllVisWindows(ImInvalid);
    return ObjTrue;
}

static ObjPtr KeepButton(button)
ObjPtr button;
/*Reverts to last palette saved*/
{
    ObjPtr panel, repObj, keptPalette, colorBar;

    repObj = GetPaletteVar("KeepButton", button, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }
    keptPalette = GetPaletteVar("KeepButton", repObj, KEPTPALETTE);
    if (!keptPalette)
    {
	return ObjFalse;
    }
    CopyPalette(keptPalette, repObj);
    ActivateButton(button, false);
    if (panel = GetObjectVar("KeepButton", button, PARENT))
    {
	if (button = GetObjectVar("KeepButton", panel, REVERTBUTTON))
	{
	    ActivateButton(button, false);
	}
    }

    return ObjTrue;
}

static void SimplePaletteFunction(whichFunc, palette, index1, index2, comp, colorBar)
int whichFunc;
PPtr palette;
int index1, index2, comp;
ObjPtr colorBar;
/*Does a simple function on palette*/
{
    int k;
    real r1, g1, b1, r2, g2, b2, r3, g3, b3, r, g, b, c;
    int rs, gs, bs, cs;

    switch (whichFunc)
    {
	case PF_RAMP:
	    /*Change the palette to a ramp*/
	    for (k = index1 + 1; k < index2; ++k)
	    {
		if (comp == 0 || comp == 1)
		{
		    c = ((k - index1) * (GetColorComponent(palette, index2, 0) / 255.0)
		       + (index2 - k) * (GetColorComponent(palette, index1, 0) / 255.0)) / (index2 - index1);
	
		    cs = c * 255.0;

		    SetColorComponent(palette, k, 0, cs);
		}
		if (comp == 0 || comp == 2)
		{
		    c = ((k - index1) * (GetColorComponent(palette, index2, 1) / 255.0)
		       + (index2 - k) * (GetColorComponent(palette, index1, 1) / 255.0)) / (index2 - index1);
	
		    cs = c * 255.0;

		    SetColorComponent(palette, k, 1, cs);
		}
		if (comp == 0 || comp == 3)
		{
		    c = ((k - index1) * (GetColorComponent(palette, index2, 2) / 255.0)
		       + (index2 - k) * (GetColorComponent(palette, index1, 2) / 255.0)) / (index2 - index1);
	
		    cs = c * 255.0;

		    SetColorComponent(palette, k, 2, cs);
		}
	    }
	    break;
	case PF_REVERSE:
	    for (k = 0; k < (index2 - index1 + 1) / 2; ++k)
	    {
		int tempColor;

		if (comp == 0 || comp == 1)
		{
		    /*First component*/
		    tempColor = GetColorComponent(palette, index2 - k, 0);
		    SetColorComponent(palette, index2 - k, 0, 
			GetColorComponent(palette, index1 + k, 0));
		    SetColorComponent(palette, index1 + k, 0, tempColor);
		}

		if (comp == 0 || comp == 2)
		{
		    /*Secpmd component*/
		    tempColor = GetColorComponent(palette, index2 - k, 1);
		    SetColorComponent(palette, index2 - k, 1, 
			GetColorComponent(palette, index1 + k, 1));
		    SetColorComponent(palette, index1 + k, 1, tempColor);
		}

		if (comp == 0 || comp == 3)
		{
		    /*Third component*/
		    tempColor = GetColorComponent(palette, index2 - k, 2);
		    SetColorComponent(palette, index2 - k, 2, 
			GetColorComponent(palette, index1 + k, 2));
		    SetColorComponent(palette, index1 + k, 2, tempColor);
		}
	    }
	    break;
	case PF_RUFFLE:
	    {
		int left, right, bottom, top, l, r;
		long oldQuotient, newQuotient;
		double ddiff, majorWidth, minorWidth, halfSpace;
		real val;
		ObjPtr var;
		int nTics;

		Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

		l = left + CBLBORDER + 2 * CBBOXWIDTH + 2 * CBHGAP + 1;
		r = right  - CBRBORDER - CBBOXWIDTH - CBHGAP - 1;
		halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
		ddiff = palette -> max - palette -> min + 2 * halfSpace;
		CalcGoodSteps(ddiff,
			r - l,
			CBSTEPPIXELS,
			&majorWidth, &nTics);

		minorWidth = majorWidth / nTics;

		l = index1;
		val = (l - 2) * ddiff / (palette -> nColors - 3);
		oldQuotient = val / minorWidth;
		for (r = l + 1; r <= index2; ++r)
		{
		    val = (r - 2) * ddiff / (palette -> nColors - 3);
		    newQuotient = val / minorWidth;
		    if (newQuotient != oldQuotient ||
			r == index2)
		    {
			/*Time for a reversal*/
			if (r == index2) ++r;
			oldQuotient = newQuotient;
		        for (k = 0; k < (r - l) / 2; ++k)
			{
			    int tempColor;

			    if (comp == 0 || comp == 1)
			    {
				/*Component*/
				tempColor = GetColorComponent(palette, r - 1 - k, 0);
				SetColorComponent(palette, r - 1 - k, 0,
					GetColorComponent(palette, l + k, 0));
				SetColorComponent(palette, l + k, 0, tempColor);
			    }

			    if (comp == 0 || comp == 2)
			    {
				/*Component*/
				tempColor = GetColorComponent(palette, r - 1 - k, 1);
				SetColorComponent(palette, r - 1 - k, 1,
					GetColorComponent(palette, l + k, 1));
				SetColorComponent(palette, l + k, 1, tempColor);
			    }

			    if (comp == 0 || comp == 3)
			    {
				/*Component*/
				tempColor = GetColorComponent(palette, r - 1 - k, 2);
				SetColorComponent(palette, r - 1 - k, 2,
					GetColorComponent(palette, l + k, 2));
				SetColorComponent(palette, l + k, 2, tempColor);
			    }
			}
			l = r;
		    } 
		}
	    }
	    break;
	case PF_SMOOTH:
	    if (index2 <= index1) break;
	    r1 = (GetColorComponent(palette, index1, 0) + 0.5) / 255.0;
	    g1 = (GetColorComponent(palette, index1, 1) + 0.5) / 255.0;
	    b1 = (GetColorComponent(palette, index1, 2) + 0.5) / 255.0;
	    r2 = (GetColorComponent(palette, index1 + 1, 0) + 0.5) / 255.0;
	    g2 = (GetColorComponent(palette, index1 + 1, 1) + 0.5) / 255.0;
	    b2 = (GetColorComponent(palette, index1 + 1, 2) + 0.5) / 255.0;
	    for (k = index1 + 2; k < index2; ++k)
	    {
		r3 = (GetColorComponent(palette, k + 1, 0) + 0.5) / 255.0;
		g3 = (GetColorComponent(palette, k + 1, 1) + 0.5) / 255.0;
		b3 = (GetColorComponent(palette, k + 1, 2) + 0.5) / 255.0;

		r = ((r1 + r3) * 0.5 + r2) * 0.5;
		g = ((g1 + g3) * 0.5 + g2) * 0.5;
		b = ((b1 + b3) * 0.5 + b2) * 0.5;

		rs = r * 255.0;
		gs = g * 255.0;
		bs = b * 255.0;

		if (comp == 0 || comp == 1)
		{
		    SetColorComponent(palette, k, 0, rs);
		}
		if (comp == 0 || comp == 2)
		{
		    SetColorComponent(palette, k, 1, gs);
		}
		if (comp == 0 || comp == 3)
		{
		    SetColorComponent(palette, k, 2, bs);
		}
		r1 = r2; r2 = r3;
		g1 = g2; g2 = g3;
		b1 = b2; b2 = b3;
	    }
	    break;
	case PF_SHARPEN:
	    if (index2 <= index1) break;
	    r1 = (GetColorComponent(palette, index1, 0) + 0.5) / 255.0;
	    g1 = (GetColorComponent(palette, index1, 1) + 0.5) / 255.0;
	    b1 = (GetColorComponent(palette, index1, 2) + 0.5) / 255.0;
	    r2 = (GetColorComponent(palette, index1 + 1, 0) + 0.5) / 255.0;
	    g2 = (GetColorComponent(palette, index1 + 1, 1) + 0.5) / 255.0;
	    b2 = (GetColorComponent(palette, index1 + 1, 2) + 0.5) / 255.0;
	    for (k = index1 + 2; k < index2; ++k)
	    {
		r3 = (GetColorComponent(palette, k + 1, 0) + 0.5) / 255.0;
		g3 = (GetColorComponent(palette, k + 1, 1) + 0.5) / 255.0;
		b3 = (GetColorComponent(palette, k + 1, 2) + 0.5) / 255.0;

		r = r2 + (r2 - (r1 + r3) * 0.5) * 2.0;
		if (r > 1.0) r = 1.0; else if (r < 0.0) r = 0.0;
		g = g2 + (g2 - (g1 + g3) * 0.5) * 2.0;
		if (g > 1.0) g = 1.0; else if (g < 0.0) g = 0.0;
		b = b2 + (b2 - (b1 + b3) * 0.5) * 2.0;
		if (b > 1.0) b = 1.0; else if (b < 0.0) b = 0.0;

		rs = r * 255.0;
		gs = g * 255.0;
		bs = b * 255.0;

		if (comp == 0 || comp == 1)
		{
		    SetColorComponent(palette, k, 0, rs);
		}
		if (comp == 0 || comp == 2)
		{
		    SetColorComponent(palette, k, 1, gs);
		}
		if (comp == 0 || comp == 3)
		{
		    SetColorComponent(palette, k, 2, bs);
		}
		r1 = r2; r2 = r3;
		g1 = g2; g2 = g3;
		b1 = b2; b2 = b3;
	    }
	    break;
    }
    CopyComponentsToColors(palette, index1, index2);
}

static ObjPtr SimpleFuncButton(button)
ObjPtr button;
/*Do a simple palette function*/
{
    ObjPtr colorBar, panel; 
    PPtr palette;
    ObjPtr var;
    int whichFunc;
    int index1, index2, comp, k;

    colorBar = GetObjectVar("SimpleFuncButton", button, COLORBAR);
    if (!colorBar)
    {
	return ObjFalse;
    }

    var = GetIntVar("SimpleFuncButton", button, PALETTEFUNC);
    if (!var)
    {
	return ObjFalse;
    }
    whichFunc = GetInt(var);

    palette = (PPtr) GetPaletteVar("SimpleFuncButton", colorBar, REPOBJ);
    if (!palette)
    {
	return false;
    }

    var = GetValue(colorBar);
    if (!var)
    {
	return ObjFalse;
    }
    if (((real *) ELEMENTS(var))[0] < 1.0)
    {
	return ObjFalse;
    }
    index1 = ((real *) ELEMENTS(var))[1];
    index2 = ((real *) ELEMENTS(var))[2];

    if (((real *) ELEMENTS(var))[0] == 1.0)
    {
	comp = 0;
    }
    else
    {
	comp = 5 - (int) ((real *) ELEMENTS(var))[0];
    }

    SimplePaletteFunction(whichFunc, palette, index1, index2, comp, colorBar);

#ifdef GRAPHICS
    MapPaletteColors(palette, index1, index2);
#endif

    SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
    panel = GetVar(button, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

/*   interactiveMoving = false;*/
    InhibitLogging(true);
    ChangedValue(colorBar);
    InhibitLogging(false);
    ForAllVisWindows(ImInvalid);
    DrawMe(colorBar);
    return ObjTrue;
}

#ifdef PROTO
void ActivateColorWheel(ObjPtr wheel, Bool whether)
#else
void ActivateColorWheel(wheel, whether)
ObjPtr wheel;
Bool whether;
#endif
/*Activates a color wheel*/
{
    SetVar(wheel, ACTIVATED, whether ? ObjTrue : ObjFalse);
    ImInvalid(wheel);
}

#ifdef PROTO
static int GetColorComponent(PPtr palette, int whichColor, int comp)
#else
static int GetColorComponent(palette, whichColor, comp)
PPtr palette;
int whichColor;
int comp;
#endif
/*Gets the color component comp from whichColor in palette*/
{
    return palette -> components[whichColor][comp];
}

#ifdef PROTO
static void SetColorComponent(PPtr palette, int whichColor, int comp, int cc)
#else
static void SetColorComponent(palette, whichColor, comp, cc)
PPtr palette, int whichColor; int comp; int cc;
#endif
/*Changes component comp in whichColor of palette to cc*/
{
    real rgb[3], hsv[3], hls[3], yiq[3];

    palette -> components[whichColor][comp] = cc;

    CopyComponentsToColors(palette, whichColor, whichColor);
}

ObjPtr ImposeColorFunction(colorBar)
ObjPtr colorBar;
/*Imposes a color function on colorBar.
  Function is given by EDITMODE
  Box is given by FUNCTIONBOX
*/
{
    ObjPtr value, var, palette, panel, button;
    real *elements;
    int comp;
    int index1, index2, k, cc;
    real beg, end, fieldVal;
    int min, max;
    int tool;
    int cm;

    value = GetValue(colorBar);
    if (!value)
    {
	return ObjFalse;
    }

    palette = GetPaletteVar("ImposeColorFunction", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    elements = ELEMENTS(value);
    if (elements[1] < 1.5)
    {
	/*Not on a component*/
	return ObjFalse;
    }

    var = GetIntVar("ImposeColorFunction", colorBar, EDITMODE);
    if (!var)
    {
	return ObjFalse;
    }
    tool = GetInt(var);

    comp = 4 - elements[0];

    index1 = elements[1] + 0.5;
    index2 = elements[2] + 0.5;

    var = GetVar(colorBar, FUNCTIONBOX);
    if (!var)
    {
	/*No box around function!*/
	return ObjFalse;
    }

    elements = ELEMENTS(var);
    beg = elements[0];
    end = elements[1];
    min = elements[2] + 0.5;
    max = elements[3] + 0.5;

    var = GetVar(palette, COLORMODEL);
    if (var)
    {
	cm = GetInt(var);
    }
    else
    {
	cm = CM_RGB;
    }

    for (k = index1; k <= index2; ++k)
    {
	fieldVal = GetColorValue(palette, k);
	while (fieldVal < beg)
	{
	    fieldVal += end - beg;
	}
	while (fieldVal > end)
	{
	    fieldVal -= end - beg;
	}
	fieldVal -= beg;
	fieldVal /= (end - beg);
	/*Now fieldVal is in [0, 1) */

	switch (tool)
	{
	    case PT_SINE:		/*Sine function*/
		cc = min + (max - min) * ((1.0 + rsin(fieldVal * 2.0 * M_PI)) * 0.5);
		break;
	    case PT_TRIANGLE:		/*Triangle function*/
		if (fieldVal < 0.25)
		{
		    cc = (max + min) / 2 + fieldVal * 2.0 * (max - min);
		}
		else if (fieldVal > 0.75)
		{
		    cc = min + (fieldVal - 0.75) * 2.0 * (max - min);
		}
		else
		{
		    cc = min + (0.75 - fieldVal) * 2.0 * (max - min);
		}
		break;
	    case PT_SAWTOOTH:		/*Sawtooth function*/
		cc = min + fieldVal * (max - min);
		break;
	    case PT_TOOTHSAW:		/*Toothsaw function*/
		cc = max - fieldVal * (max - min);
		break;
	    case PT_SQUARE:		/*Square wave function*/
		if (fieldVal < 0.5)
		{
		    cc = max;
		}
		else
		{
		    cc = min;
		}
		break;
	    default:
		cc = 0;
	}
	SetColorComponent((PPtr) palette, k, comp, cc);
    }
    CopyComponentsToColors((PPtr) palette, index1, index2);

    MapPaletteColors((PPtr) palette, index1, index2);

    panel = GetVar(colorBar, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }
    SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
    ChangedValue(colorBar);

    return ObjTrue;
}

static ObjPtr ChangeColorTool(group)
ObjPtr group;
/*Changes a color tool in response to a radio button group*/
{
    ObjPtr colorBar;
    PPtr palette;
    ObjPtr value;
    ObjPtr var;
    int tool;
    ObjPtr selection;
    real *elements;

    colorBar = GetObjectVar("ChangeColorTool", group, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    selection = GetValue(colorBar);
    elements = ELEMENTS(selection);

    palette = (PPtr) GetPaletteVar("ChangeColorTool", colorBar, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }

    value = GetValue(group);
    if (!value)
    {
	return false;
    }
    tool = GetInt(value);

    var = GetVar(colorBar, EDITMODE);
    if (var && GetInt(var) == tool)
    {
	/*Don't need to do anything*/
	return ObjFalse;
    }

    SetVar(colorBar, EDITMODE, NewInt(tool));

    if (tool > 0)
    {
	/*It's a function.*/
	var = GetVar(colorBar, FUNCTIONBOX);
	if (!var)
	{
	    /*Calculate initial function box*/
	    int left, right, bottom, top, l, r, b, t;
	    double majorWidth;
	    double halfSpace;
	    double start, finish, middle, ddiff;
	    int nTics;
	    long temp;

	    Get2DIntBounds(colorBar, &left, &right, &bottom, &top);

	    l = left + CBLBORDER + 1;
	    r = right - CBRBORDER - 1;

	    halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
	    start = GetColorValue((ObjPtr) palette, (int) elements[1]) - halfSpace;
	    finish = GetColorValue((ObjPtr) palette, (int) elements[2]) + halfSpace;
	    middle = (start + finish) * 0.5;
	    ddiff = finish - start;

	    CalcGoodSteps(ddiff,
		      r - l,
		      CBSTEPPIXELS,
		      &majorWidth, &nTics);


	    /*Minor and major tics first*/
	    temp = middle / majorWidth;
	    start = temp * majorWidth;
	    finish = start + majorWidth;

	    var = NewRealArray(1, 4L);
	    ((real *) ELEMENTS(var))[0] = start - majorWidth;
	    ((real *) ELEMENTS(var))[1] = finish;
	    ((real *) ELEMENTS(var))[2] = 0.0;
	    ((real *) ELEMENTS(var))[3] = 255.0;

	    InhibitLogging(true);
	    SetFunctionBox(colorBar, var);
	    InhibitLogging(false);
	}
	InhibitLogging(true);
	ImposeColorFunction(colorBar);
	SetVar((ObjPtr) palette, JUSTCOLORCHANGE, ObjTrue);
	ForAllVisWindows(ImInvalid);
	InhibitLogging(false);
    }
    else
    {
	/*No function box*/
	InhibitLogging(true);
	SetFunctionBox(colorBar, NULLOBJ);
	InhibitLogging(false);
    }

    ImInvalid(colorBar);
    return ObjTrue;
}

static ObjPtr MakePaletteBarHelp(colorBar, class)
ObjPtr colorBar;
ObjPtr class;
/*Makes help for a color bar*/
{
    ObjPtr help, temp;
    ObjPtr var;
    int editMode;
    real *elements;

    help = NewString("This control shows a color palette.  The color bar at \
the bottom shows the range of colors in the palette associated with the field \
values, plus colors for missing data, overflow, and underflow values.  \
Above the color bar are three bars showing the magnitude of the three components \
of each color, plus a magnified readout at the top.\n\
\n\
You can select a range of colors to edit by clicking in the color bar or one of the \
three component bars and dragging through the colors you want to edit.  When you \
select a range, controls in the window that can edit the color will become active.  \
Also, the selected range will be expanded to fill the magnified readout at the top of the control.  \
When a color component is selected, you can edit it using the Edit Component \
buttons to the left.");

    var = GetValue(colorBar);
    if (var)
    {
	elements = ELEMENTS(var);

	if (elements[0] > 1.5)
	{
	    /*A component is selected*/
	    var = GetIntVar("MakePaletteBarHelp", colorBar, EDITMODE);
	    if (var)
	    {
		ObjPtr palette;
		int cm;
		editMode = GetInt(var);

		palette = GetVar(colorBar, REPOBJ);
		if (!palette)
		{
		    return ObjFalse;
		}

		var = GetVar(palette, COLORMODEL);
		if (var)
		{
		    cm = GetInt(var);
		}
		else
		{
		    cm = CM_RGB;
		}

		sprintf(tempStr, "\n\nThe %s components of a range of colors are \
currently selected.  The Intensity slider on the left lets you change the brightness \
of this component of the range.  The buttons at the top of the window let you \
perform some simple functions over the entire range of colors.  Use Help In \
Context to find out what each of these buttons does.\n\n",
		componentNames[cm][4 - (int) (elements[0] + 0.5)]);
		temp = NewString(tempStr);
		help = ConcatStrings(help, temp);

		if (editMode == PT_FREEFORM)
		{
		    /*Free form tool*/
		    temp = NewString("The free form tool is selected.  \
You can modify the waveform shown at the top of the control by clicking and drawing \
within the box.  The Shift key will constrain motion in just the vertical or \
horizontal direction.\n");
		    help = ConcatStrings(help, temp);
		}
		else
		{
		    /*Waveform tool*/
		    sprintf(tempStr, "The %s tool is selected.  Notice \
that there is a yellow box within the magnified readout at \
the top of the control.  This box encloses one period of a %s which is used \
to fill the entire selected range of the component.  ",
		    toolNames[editMode], toolNames[editMode]);
		    temp = NewString(tempStr);
		    help = ConcatStrings(help, temp);
		    temp = NewString("Click and drag the square \
handles on the top or bottom to change the minimum and maximum values of the wave.  Click \
Click and drag the handles on the left or right to change the beginning and end \
of the wave as well as its wavelength.  The Shift key will constrain motion to \
the nearest tic mark.  Click and drag within the box to move the entire waveform \
left or right.");
		    help = ConcatStrings(help, temp);
		}
	    }
	}
	else if (elements[0] > 0.5)
	{
	    temp = NewString("\n\nA range of colors is \
currently selected.  The Intensity slider on the left lets you change the brightness \
of this component of the range.  The Hue/Saturation color wheel lets you change \
the hue and saturation of the entire range.  When you click on this \
control, the entire range will become a single color.  \
The buttons at the top of the window let you \
perform some simple functions over the entire range of colors.  Use Help In \
Context to find out what each of these buttons does.");
	    help = ConcatStrings(help, temp);
	}
    }

    SetVar(class, HELPSTRING, help);
}

void DoNumberError()
/*Whines at the user about a numeric error*/
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "There is a syntax error in the number.", 0, 0, "");
    SetVar((ObjPtr) errWindow, HELPSTRING,
	NewString("There is a syntax error in the number you have just entered.  \
Please enter the number once more."));
}

void DoRangeError()
/*Whines at the user about a range error*/
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "The maximum value must be greater than the minimum.", 0, 0, "");
    SetVar((ObjPtr) errWindow, HELPSTRING,
	NewString("The number you have just entered would cause the maximum value \
to be less than the minimum.  \
Please enter the number once more."));
}

void DoWrongNColorsError()
/*Whines at the user about the wrong number of colors*/
{
    WinInfoPtr errWindow;
    errWindow = AlertUser(UIRED, (WinInfoPtr) 0, "The number of colors must be between 5 and 2048.", 0, 0, "");
    SetVar((ObjPtr) errWindow, HELPSTRING,
	NewString("The number you have just entered is an invalid number of colors \
for a color table.  \
Please enter a number between 5 and 2048."));
}

static ObjPtr EnterMin(box)
ObjPtr box;
/*Enters the minimum value from box*/
{
    ObjPtr value, colorBar, panel, button;
    PPtr palette;
    float min;
    char *s;
    FuncTyp method;

    value = GetValue(box);
    if (!value)
    {
	return ObjFalse;
    }

    s = GetString(value);
    if (1 != sscanf(s, " %g", &min))
    {
	DoUniqueTask(DoNumberError);
	return ObjFalse;
    }

    colorBar = GetObjectVar("EnterMin", box, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    palette = (PPtr) GetVar(colorBar, REPOBJ);
    if (!palette)
    {
	return ObjTrue;
    }

    if (min >= palette -> max)
    {
	DoUniqueTask(DoRangeError);
	return ObjFalse;
    }

    palette -> min = min;
    ReinitColorBar(colorBar);
    ImInvalid(colorBar);
    SetVar((ObjPtr) palette, CHANGED, ObjTrue);
    ForAllVisWindows(ImInvalid);

    panel = GetVar(box, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

    return ObjTrue;
}

static ObjPtr EnterMax(box)
ObjPtr box;
/*Enters the max value from box*/
{
    ObjPtr value, colorBar, panel, button;
    PPtr palette;
    float max;
    FuncTyp method;

    value = GetValue(box);
    if (!value)
    {
	return ObjFalse;
    }

    if (1 != sscanf(GetString(value), " %g", &max))
    {
	DoUniqueTask(DoNumberError);
	return ObjFalse;
    }

    colorBar = GetObjectVar("EnterMin", box, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    palette = (PPtr) GetVar(colorBar, REPOBJ);
    if (!palette)
    {
	return ObjTrue;
    }

    if (max <= palette -> min)
    {
	DoUniqueTask(DoRangeError);
	return ObjFalse;
    }

    palette -> max = max;
    SetVar((ObjPtr) palette, CHANGED, ObjTrue);
    ForAllVisWindows(ImInvalid);
    ReinitColorBar(colorBar);
    ImInvalid(colorBar);

    panel = GetVar(box, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

    return ObjTrue;
}

static ObjPtr EnterNColors(box)
ObjPtr box;
/*Enters the number of colors from box*/
{
    ObjPtr value, colorBar, panel, button;
    PPtr palette;
    int nColors;
    real *elements;
    int oldNColors; 

    value = GetValue(box);
    if (!value)
    {
	return ObjFalse;
    }

    if (1 != sscanf(GetString(value), " %d", &nColors))
    {
	DoUniqueTask(DoNumberError);
	return ObjFalse;
    }

    colorBar = GetObjectVar("EnterNColors", box, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    palette = (PPtr) GetVar(colorBar, REPOBJ);
    if (!palette)
    {
	return ObjTrue;
    }

    if (nColors < 5 || nColors > 2048)
    {
	DoUniqueTask(DoWrongNColorsError);
	return ObjFalse;
    }

    oldNColors = palette -> nColors;
    SetPaletteNColors((ObjPtr) palette, nColors);
    SetVar((ObjPtr) palette, CHANGED, ObjTrue);
    ForAllVisWindows(ImInvalid);
    panel = GetVar(box, PARENT);
    if (panel)
    {
	button = GetVar(panel, KEEPBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
	button = GetVar(panel, REVERTBUTTON);
	if (button)
	{
	    ActivateButton(button, true);
	}
    }

    value = GetValue(colorBar);
    if (value)
    {
	real newValue[4];
	elements = ELEMENTS(value);
	newValue[0] = elements[0];
	newValue[1] = elements[1];
	newValue[2] = elements[2];
	newValue[3] = elements[3];
	if (elements[0] > 0.5)
	{
	    int k;
	    for (k = 1; k <=2; ++k)
	    {
		if (((int) elements[k] + 0.5) >= oldNColors - 1)
		{
		    newValue[k] = nColors - 1;
		}
		else if (((int) elements[k] + 0.5) > 1)
		{
		    newValue[k] = 2.0 + 
			(elements[k] - 2.0) * (nColors - 3) / (oldNColors - 3);
		    if (((int) newValue[k] + 0.5) >= nColors - 1)
		    {
			newValue[k] = nColors - 2.0;
		    }
		    if (((int) newValue[k] + 0.5) < 2)
		    {
			newValue[k] = 2.0;
		    }
		}
	    }
	    if (newValue[3] <= newValue[2]) newValue[3] += 1.0;
	}
	value = NewRealArray(1, 3L);
	CArray2Array(value, newValue);
	InhibitLogging(true);
	SetValue(colorBar, value);
	InhibitLogging(false);
    }
    return ObjTrue;
}

ObjPtr ChangeTextColorWheel(wheel)
ObjPtr wheel;
/*Changes text colors according to a color wheel*/
{
    ObjPtr repObj, slider;
    ObjPtr value;
    real hsv[3], rgb[3];

    repObj = GetObjectVar("ChangeTextColorWheel", wheel, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    slider = GetObjectVar("ChangeTextColorWheel", wheel, SLIDER);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(wheel);
    if (!value)
    {
	return ObjFalse;
    }
    Array2CArray(hsv, value);

    value = GetValue(slider);
    if (!value)
    {
	return ObjFalse;
    }
    hsv[2] = GetReal(value);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), hsv[0], hsv[1], hsv[2]);

    value = NewRealArray(1, 3L);
    CArray2Array(value, rgb);
    SetVar(repObj, COLOR, value);
    ImInvalid(repObj);

    return ObjTrue;
}

ObjPtr ChangeTextColorSlider(slider)
ObjPtr slider;
/*Changes text color according to a color slider*/
{
    ObjPtr repObj, wheel;
    ObjPtr value;
    real hsv[3], rgb[3];

    repObj = GetObjectVar("ChangeTextColorWheel", slider, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    wheel = GetObjectVar("ChangeTextColorWheel", slider, COLORWHEEL);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(wheel);
    if (!value)
    {
	return ObjFalse;
    }
    Array2CArray(hsv, value);

    value = GetValue(slider);
    if (!value)
    {
	return ObjFalse;
    }
    hsv[2] = GetReal(value);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), hsv[0], hsv[1], hsv[2]);

    value = NewRealArray(1, 3L);
    CArray2Array(value, rgb);
    SetVar(repObj, COLOR, value);
    ImInvalid(repObj);

    return ObjTrue;
}

ObjPtr ChangeBackgroundColorWheel(wheel)
ObjPtr wheel;
/*Changes text colors according to a color wheel*/
{
    ObjPtr repObj, slider, checkBox;
    ObjPtr value;
    real hsv[3], rgb[3];

    repObj = GetObjectVar("ChangeBackgroundColorWheel", wheel, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    slider = GetObjectVar("ChangeBackgroundColorWheel", wheel, SLIDER);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(wheel);
    if (!value)
    {
	return ObjFalse;
    }
    Array2CArray(hsv, value);

    value = GetValue(slider);
    if (!value)
    {
	return ObjFalse;
    }
    hsv[2] = GetReal(value);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), hsv[0], hsv[1], hsv[2]);

    value = NewRealArray(1, 3L);
    CArray2Array(value, rgb);
    SetVar(repObj, BACKGROUND, value);
    ImInvalid(repObj);

    checkBox = GetObjectVar("ChangeBackgroundColorSlider", wheel, CHECKBOX);
    if (checkBox)
    {
	FuncTyp method;
	method = GetMethod(checkBox, CHANGEDVALUE);
	SetMethod(checkBox, CHANGEDVALUE, 0);
	InhibitLogging(true);
	SetValue(checkBox, NewInt(0));
	InhibitLogging(false);
	SetMethod(checkBox, CHANGEDVALUE, method);
    }
    return ObjTrue;
}

ObjPtr ChangeBackgroundColorSlider(slider)
ObjPtr slider;
/*Changes background color according to a color slider*/
{
    ObjPtr repObj, wheel, checkBox;
    ObjPtr value;
    real hsv[3], rgb[3];

    repObj = GetObjectVar("ChangeBackgroundColorSlider", slider, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    wheel = GetObjectVar("ChangeBackgroundColorSlider", slider, COLORWHEEL);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(wheel);
    if (!value)
    {
	return ObjFalse;
    }
    Array2CArray(hsv, value);

    value = GetValue(slider);
    if (!value)
    {
	return ObjFalse;
    }
    hsv[2] = GetReal(value);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), hsv[0], hsv[1], hsv[2]);
    value = NewRealArray(1, 3L);
    CArray2Array(value, rgb);
    SetVar(repObj, BACKGROUND, value);
    ImInvalid(repObj);

    checkBox = GetObjectVar("ChangeBackgroundColorSlider", slider, CHECKBOX);
    if (checkBox)
    {
	FuncTyp method;
	method = GetMethod(checkBox, CHANGEDVALUE);
	SetMethod(checkBox, CHANGEDVALUE, 0);
	InhibitLogging(true);
	SetValue(checkBox, NewInt(0));
	InhibitLogging(false);
	SetMethod(checkBox, CHANGEDVALUE, method);
    }

    return ObjTrue;
}

static ObjPtr ChangeColorByPalette(checkBox)
ObjPtr checkBox;
/*Changes an object's COLORBYFIELD according to checkBox*/
{
    ObjPtr repObj, value;

    repObj = GetObjectVar("ChangeColorByPalette", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(checkBox);
    if (!value)
    {
	return ObjFalse;
    }

    SetVar(repObj, COLORBYFIELD, value);
    ImInvalid(repObj);

    return ObjFalse;
}

ObjPtr ChangeNoBackground(checkBox)
ObjPtr checkBox;
/*Changes background color according to a check box*/
{
    ObjPtr repObj, wheel, slider;
    ObjPtr value;
    real hsv[3], rgb[3];

    repObj = GetObjectVar("ChangeBackgroundColorSlider", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(checkBox);
    if (!value)
    {
	return ObjFalse;
    }

    if (GetInt(value))
    {
	SetVar(repObj, BACKGROUND, NULLOBJ);
	ImInvalid(repObj);
	return ObjTrue;
    }

    wheel = GetObjectVar("ChangeBackgroundColorSlider", checkBox, COLORWHEEL);
    if (!repObj)
    {
	return ObjFalse;
    }

    slider = GetObjectVar("ChangeBackgroundColorSlider", checkBox, SLIDER);
    if (!repObj)
    {
	return ObjFalse;
    }

    value = GetValue(wheel);
    if (!value)
    {
	return ObjFalse;
    }
    Array2CArray(hsv, value);

    value = GetValue(slider);
    if (!value)
    {
	return ObjFalse;
    }
    hsv[2] = GetReal(value);

    HSV2RGB(&(rgb[0]), &(rgb[1]), &(rgb[2]), hsv[0], hsv[1], hsv[2]);

    value = NewRealArray(1, 3L);
    CArray2Array(value, rgb);
    SetVar(repObj, BACKGROUND, value);
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeShowMissing(checkBox)
ObjPtr checkBox;
/*Changes the SHOWMISSING according to a checkbox*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeShowMissing", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, SHOWMISSING, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeShowOverUnder(checkBox)
ObjPtr checkBox;
/*Changes the SHOWOVERUNDER according to a checkbox*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeShowOverUnder", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, SHOWOVERUNDER, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeShowMinorTics(checkBox)
ObjPtr checkBox;
/*Changes the SHOWMINORTICS according to a checkbox*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeShowMinorTics", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, SHOWMINORTICS, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ChangeNumbersOnly(checkBox)
ObjPtr checkBox;
/*Changes the NUMBERSONLY according to a checkbox*/
{
    ObjPtr repObj;

    repObj = GetObjectVar("ChangeNumbersOnly", checkBox, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    SetVar(repObj, NUMBERSONLY, GetValue(checkBox));
    ImInvalid(repObj);

    return ObjTrue;
}

static ObjPtr ShowPaletteDisplayControls(display, ownerWindow, windowName)
ObjPtr display;
WinInfoPtr ownerWindow;
char *windowName;
/*Makes a new control window to control a palette display*/
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr corral;
    ObjPtr contents;
    real rgb[3], hsv[3];
    WinInfoPtr dialogExists;
    Bool hasBackground;

    dialogExists = DialogExists((WinInfoPtr) 0, display);
    controlWindow = GetDialog((WinInfoPtr) 0, display, windowName, 
	DSPPALWINWIDTH, DSPPALWINHEIGHT, DSPPALWINWIDTH,
	DSPPALWINHEIGHT, WINDBUF + WINRGB + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	long info;
	ObjPtr value;
	
	ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
	ObjPtr colorWheel, slider, radioGroup;
	int left, right, bottom, top;

	SetVar((ObjPtr) controlWindow, REPOBJ, display);

	/*Set help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING, NewString("This window \
shows controls for a color palette legend.  For information about any of the controls \
in the window, use Help In Context on the control.\n"));

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, DSPPALWINWIDTH, 0, DSPPALWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);

	contents = GetVar(panel, CONTENTS);

	/*Add in the group of controls for text color*/
	left = MAJORBORDER;
	top = DSPPALWINHEIGHT - MAJORBORDER;
	right = left + 4 * MAJORBORDER + COLORWHEELWIDTH + SLIDERWIDTH;
	
	titleBox = NewTitleBox(left, right,
			top - TITLEBOXTOP - MAJORBORDER - 2 * MINORBORDER - COLORWHEELWIDTH - CHECKBOXHEIGHT - TEXTBOXHEIGHT - TEXTBOXSEP,
			top, "Text and Lines");
	SetVar(titleBox, PARENT, panel);
	PrefixList(contents, titleBox);
	left += MAJORBORDER;
	right -= MAJORBORDER;
	top -= TITLEBOXTOP + MAJORBORDER;

	/*Get the color for priming the controls*/
	var = GetVar(display, COLOR);
	if (IsArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	{
	    Array2CArray(rgb, var);
	}
	else if (IsInt(var))
	{
	    rgb[0] = uiColors[GetInt(var)][0] / 255.0;
	    rgb[1] = uiColors[GetInt(var)][1] / 255.0;
	    rgb[2] = uiColors[GetInt(var)][2] / 255.0;
	}
	else
	{
	    ReportError("ShowPaletteDisplayControls", "Bad color");
	    rgb[0] = rgb[1] = rgb[2] = 1.0;
	}
	RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]), (rgb[0]), (rgb[1]), (rgb[2]));

	/*Make the color wheel*/
	colorWheel = NewColorWheel(left, left + COLORWHEELWIDTH,
			top - COLORWHEELWIDTH, top, "Text Color");
	var = NewRealArray(1, 2L);
	CArray2Array(var, hsv);
	SetValue(colorWheel, var);
	SetVar(colorWheel, PARENT, panel);
	SetVar(colorWheel, REPOBJ, display);
	PrefixList(contents, colorWheel);
	SetMethod(colorWheel, CHANGEDVALUE, ChangeTextColorWheel);
	SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
hue and saturation of the color used to draw the text and lines in the palette legend.  \
The final color is a combination of this hue and saturation and the value, or brightness, \
given by the Value slider."));
	
	/*Make the text box below*/
	textBox = NewTextBox(left, left + COLORWHEELWIDTH,
			top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
			top - COLORWHEELWIDTH - TEXTBOXSEP,
			PLAIN, "Text Color Label", "Color");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);

	/*Make the brightness slider*/
	slider = NewSlider(right - SLIDERWIDTH, right, 
		       top - COLORWHEELWIDTH, top,
		       PLAIN, "Text Color Value");
	SetVar(slider, PARENT, panel);
	PrefixList(contents, slider);
	SetSliderRange(slider, 1.0, 0.0, 0.0);
	SetSliderValue(slider, hsv[2]);
	SetVar(slider, REPOBJ, display);
	SetMethod(slider, CHANGEDVALUE, ChangeTextColorSlider);
	SetVar(slider, HELPSTRING, NewString("This slider controls the \
value, or brightness, of the color used to draw the text and lines in the palette legend.  \
The final color is a combination of this value and the hue and saturation \
given by the Color color wheel."));

	/*Make the text box below*/
	textBox = NewTextBox(right - SLIDERWIDTH - MINORBORDER, right + MINORBORDER,
			top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
			top - COLORWHEELWIDTH - TEXTBOXSEP,
			PLAIN, "Text Value Label", "Value");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);

	/*Cross link the slider and color wheel*/
	SetVar(colorWheel, SLIDER, slider);
	SetVar(slider, COLORWHEEL, colorWheel);

	left -= MINORBORDER;	
	right += MINORBORDER;
	/*Make the check box*/
	top -= COLORWHEELWIDTH + TEXTBOXSEP + TEXTBOXHEIGHT + MINORBORDER;
	checkBox = NewCheckBox(left, right, 
		top - CHECKBOXHEIGHT, top,
		"Color text by palette", GetPredicate(display, COLORBYFIELD));
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeColorByPalette);
	SetVar(colorWheel, HELPSTRING, NewString("This check box controls \
how the numbers in the palette legend are colored.  If the box is checked, the \
numbers are colored according to the values they represent.  If the box is not \
checked, the numbers are colored with the same color used for the lines in the \
legend."));
	
	/*Make the background controls*/
	top = DSPPALWINHEIGHT - MAJORBORDER;
	right = DSPPALWINWIDTH - MAJORBORDER;
	left = right - (4 * MAJORBORDER + COLORWHEELWIDTH + SLIDERWIDTH);
	
	titleBox = NewTitleBox(left, right,
			top - TITLEBOXTOP - MAJORBORDER - 2 * MINORBORDER - COLORWHEELWIDTH - CHECKBOXHEIGHT - TEXTBOXHEIGHT - TEXTBOXSEP,
			top, "Background");
	SetVar(titleBox, PARENT, panel);
	PrefixList(contents, titleBox);
	left += MAJORBORDER;
	right -= MAJORBORDER;
	top -= TITLEBOXTOP + MAJORBORDER;

	/*Get the color for priming the controls*/
	var = GetVar(display, BACKGROUND);
	if (var && IsArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	{
	    Array2CArray(rgb, var);
	    hasBackground = true;
	}
	else if (var && IsInt(var))
	{
	    rgb[0] = uiColors[GetInt(var)][0] / 255.0;
	    rgb[1] = uiColors[GetInt(var)][1] / 255.0;
	    rgb[2] = uiColors[GetInt(var)][2] / 255.0;
	    hasBackground = true;
	}
	else
	{
	    rgb[0] = rgb[1] = rgb[2] = 0.5;
	    hasBackground = false;
	}
	RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]), (rgb[0]), (rgb[1]), (rgb[2]));

	/*Make the color wheel*/
	colorWheel = NewColorWheel(left, left + COLORWHEELWIDTH,
			top - COLORWHEELWIDTH, top, "Background Color");
	var = NewRealArray(1, 2L);
	CArray2Array(var, hsv);
	SetValue(colorWheel, var);
	SetVar(colorWheel, PARENT, panel);
	SetVar(colorWheel, REPOBJ, display);
	PrefixList(contents, colorWheel);
	SetMethod(colorWheel, CHANGEDVALUE, ChangeBackgroundColorWheel);
	SetVar(colorWheel, HELPSTRING, NewString("This color wheel controls the \
hue and saturation of the color used to draw the background of the palette legend.  \
The final color is a combination of this hue and saturation and the value, or brightness, \
given by the Value slider."));
	
	/*Make the text box below*/
	textBox = NewTextBox(left, left + COLORWHEELWIDTH,
			top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
			top - COLORWHEELWIDTH - TEXTBOXSEP,
			PLAIN, "Background Color Label", "Color");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);

	/*Make the brightness slider*/
	slider = NewSlider(right - SLIDERWIDTH, right, 
		       top - COLORWHEELWIDTH, top,
		       PLAIN, "Background Value");
	SetVar(slider, PARENT, panel);
	PrefixList(contents, slider);
	SetSliderRange(slider, 1.0, 0.0, 0.0);
	SetSliderValue(slider, hsv[2]);
	SetVar(slider, REPOBJ, display);
	SetMethod(slider, CHANGEDVALUE, ChangeBackgroundColorSlider);
	SetVar(slider, HELPSTRING, NewString("This slider controls the \
value, or brightness, of the color used to draw the background of the palette legend.  \
The final color is a combination of this value and the hue and saturation \
given by the Color color wheel."));

	/*Make the text box below*/
	textBox = NewTextBox(right - SLIDERWIDTH - MINORBORDER, right + MINORBORDER,
			top - COLORWHEELWIDTH - TEXTBOXSEP - TEXTBOXHEIGHT,
			top - COLORWHEELWIDTH - TEXTBOXSEP,
			PLAIN, "Background Value Label", "Value");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);

	/*Cross link the slider and color wheel*/
	SetVar(colorWheel, SLIDER, slider);
	SetVar(slider, COLORWHEEL, colorWheel);

	left -= MINORBORDER;	
	right += MINORBORDER;
	/*Make the check box*/
	top -= COLORWHEELWIDTH + TEXTBOXSEP + TEXTBOXHEIGHT + MINORBORDER;
	checkBox = NewCheckBox(left, right, 
		top - CHECKBOXHEIGHT, top,
		"No background", hasBackground ? false : true);
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	SetVar(checkBox, SLIDER, slider);
	SetVar(checkBox, COLORWHEEL, colorWheel);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeNoBackground);
	SetVar(checkBox, HELPSTRING, NewString("This checkbox controls whether \
a background is shown.  If it is selected, no background is shown, and the \
objects behind can be seen."));

	/*Link the checkbox to the slider and color wheel*/
	SetVar(colorWheel, CHECKBOX, checkBox);
	SetVar(slider, CHECKBOX, checkBox);

	top -= CHECKBOXHEIGHT + MINORBORDER + MINORBORDER;
	left = MAJORBORDER;
	right = DSPPALWINWIDTH - MAJORBORDER;

	/*Make checkbox for show missing*/
	checkBox = NewCheckBox(left, (left + right) / 2, 
		top - CHECKBOXHEIGHT, top,
		"Show Missing", GetPredicate(display, SHOWMISSING));
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeShowMissing);
	SetVar(colorWheel, HELPSTRING, NewString("This check box controls \
whether an entry for Missing data is shown in the palette legend."));

	/*Make checkbox for show Over and Under*/
	checkBox = NewCheckBox((left + right) / 2, right, 
		top - CHECKBOXHEIGHT, top,
		"Show Over and Under", GetPredicate(display, SHOWOVERUNDER));
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeShowOverUnder);
	SetVar(colorWheel, HELPSTRING, NewString("This check box controls \
whether entries for Over and Under data are shown in the palette legend."));

	top -= CHECKBOXHEIGHT + MINORBORDER;

	/*Make checkbox for show minor tics*/
	checkBox = NewCheckBox(left, (left + right) / 2, 
		top - CHECKBOXHEIGHT, top,
		"Show Minor Tics", GetPredicate(display, SHOWMINORTICS));
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeShowMinorTics);
	SetVar(colorWheel, HELPSTRING, NewString("This check box controls \
whether minor tic marks are shown in the palette legend in addition to major \
tic marks and numbers."));

	/*Make checkbox for numbers only*/
	checkBox = NewCheckBox((left + right) / 2, right,
		top - CHECKBOXHEIGHT, top,
		"Numbers Only", GetPredicate(display, NUMBERSONLY));
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, display);
	PrefixList(contents, checkBox);
	SetMethod(checkBox, CHANGEDVALUE, ChangeNumbersOnly);
	SetVar(colorWheel, HELPSTRING, NewString("When this check box is down, \
only the numbers are shown in the palette legend.  This is useful in conjunction \
with the \"Color text by palette\" check box."));
    }
    return ObjTrue;
}

static ObjPtr ChangeColorModel(radio)
ObjPtr radio;
/*Changes the color model based on the value of a radio group*/
{
    ObjPtr colorBar, palette;
    ObjPtr value;

    colorBar = GetObjectVar("ChangeColorModel", radio, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    palette = GetVar(colorBar, REPOBJ);
    if (!colorBar)
    {
	return ObjFalse;
    }

    value = GetValue(radio);
    if (!value || !IsInt(value))
    {
	return ObjFalse;
    }

    SetVar(palette, COLORMODEL, value);

    CopyColorsToComponents((PPtr) palette, 0, ((PPtr) palette) -> nColors - 1);

    ChangedValue(colorBar);
    ImInvalid(colorBar);

    /*Set the edit tool to free form*/
    ResetColorBarTools(colorBar);

    return ObjTrue;
}

static ObjPtr ShowPaletteControls(palette, ownerWindow, windowName)
ObjPtr palette;
WinInfoPtr ownerWindow;
char *windowName;
/*Makes a new control window to control a palette*/
{
    WinInfoPtr controlWindow;
    ObjPtr var;
    ObjPtr panel;
    ObjPtr corral;
    ObjPtr contents;
    WinInfoPtr dialogExists;

    dialogExists = DialogExists((WinInfoPtr) 0, palette);
    controlWindow = GetDialog((WinInfoPtr) 0, palette, windowName, 
	PCWINWIDTH, PCWINHEIGHT, PCWINWIDTH, PCWINHEIGHT, WINDBUF + WINRGB + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	long info;
	ObjPtr value;
	
	ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
	ObjPtr colorWheel, slider, fullCompList, compList, radioGroup;
	char numBuf[200];
	int left, right, bottom, top;
	char *s;

	SetVar((ObjPtr) controlWindow, REPOBJ, palette);

	/*Set help string*/
	SetVar((ObjPtr) controlWindow, HELPSTRING, NewString("This window \
shows controls for a color palette.  For information about any of the controls \
in the window, use Help In Context on the control.\n"));

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, PCWINWIDTH, 0, PCWINHEIGHT);
	if (!panel)
	{
	    return ObjFalse;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);

	contents = GetVar(panel, CONTENTS);

	/*Create a color bar to add in later*/
	left = 2 * MAJORBORDER + PCWINLSIDE;
	right = PCWINWIDTH - MAJORBORDER;
	bottom = MAJORBORDER + PCBARUP;
	top = bottom + PCBARHEIGHT;
	colorBar = NewColorBar(left, right, bottom, top, "Palette Colors");
	fullCompList = NewList();	/*List of buttons active on full or component*/
	compList = NewList();		/*List of buttons active on component*/
	SetVar(colorBar, FULLCOMPBUTTONS, fullCompList);
	SetVar(colorBar, COMPBUTTONS, compList);
	SetVar(colorBar, EDITMODE, NewInt(PT_FREEFORM));		/*Freeform editor*/

	/*Put in the min and max text boxes below*/
	sprintf(numBuf, "%g", ((PPtr) palette) -> min);
	textBox = NewTextBox(left + CBLBORDER + CBLTEXTSPACE + 3 * CBHGAP + 2 * CBBOXWIDTH - PCTEXTBOXLEN / 2,
			     left + CBLBORDER + CBLTEXTSPACE + 3 * CBHGAP + 2 * CBBOXWIDTH + PCTEXTBOXLEN / 2,
			     MAJORBORDER, MAJORBORDER + EDITBOXHEIGHT,
			     DEFERCHANGE + EDITABLE + WITH_PIT + ONE_LINE, "Field Min", numBuf);
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);
	SetVar(textBox, REPOBJ, colorBar);
	SetVar(panel, MINBOX, textBox);
	SetVar(textBox, HELPSTRING, NewString("This text box shows the maximum \
field value represented by the color table.  Any value above this will use the \
overflow color.  To change this value, enter the new number and press the Enter \
key."));
	SetMethod(textBox, CHANGEDVALUE, EnterMin);

	/*Min legend*/
	textBox = NewTextBox(left,
			     left + CBLBORDER + CBLTEXTSPACE + 3 * CBHGAP + 2 * CBBOXWIDTH - PCTEXTBOXLEN / 2 - MINORBORDER,
			     MAJORBORDER, MAJORBORDER + TEXTBOXHEIGHT + 3,
			     0, "Field Min Legend", "Minimum:");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, RIGHTALIGN);

	/*Max text box*/
	sprintf(numBuf, "%g", ((PPtr) palette) -> max);
	textBox = NewTextBox(right - CBRBORDER - CBHGAP - CBBOXWIDTH - PCTEXTBOXLEN / 2,
			     right - CBRBORDER - CBHGAP - CBBOXWIDTH + PCTEXTBOXLEN / 2,
			     MAJORBORDER, MAJORBORDER + EDITBOXHEIGHT,
			     DEFERCHANGE + EDITABLE + WITH_PIT + ONE_LINE, "Field Max", numBuf);
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);
	SetVar(textBox, REPOBJ, colorBar);
	SetVar(panel, MAXBOX, textBox);
	SetVar(textBox, HELPSTRING, NewString("This text box shows the minimum \
field value represented by the color table.  Any value below this will use the \
underflow color.  To change this value, enter the new number and press the Enter \
key."));
	SetMethod(textBox, CHANGEDVALUE, EnterMax);

	/*Max legend*/
	textBox = NewTextBox(right - CBRBORDER - CBHGAP - CBBOXWIDTH - MINORBORDER - PCTEXTBOXLEN / 2 - PCTEXTBOXLEN,
			     right - CBRBORDER - CBHGAP - CBBOXWIDTH - MINORBORDER - PCTEXTBOXLEN / 2,
			     MAJORBORDER, MAJORBORDER + TEXTBOXHEIGHT + 3,
			     0, "Field Max Legend", "Maximum:");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, RIGHTALIGN);

	/*nColors text box*/
	sprintf(numBuf, "%d", ((PPtr) palette) -> nColors);
	left = MAJORBORDER;
	textBox = NewTextBox(left, left + PCTEXTBOXLEN,
			     MAJORBORDER, MAJORBORDER + EDITBOXHEIGHT,
			     DEFERCHANGE + EDITABLE + WITH_PIT + ONE_LINE, "N Colors", numBuf);
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);
	SetTextAlign(textBox, CENTERALIGN);
	SetVar(textBox, REPOBJ, colorBar);
	SetVar(panel, NCOLORBOX, textBox);
	SetVar(textBox, HELPSTRING, NewString("This text box shows the number of colors \
in the palette, including the missing data, underflow, and overflow entries.  \
To change the number of colors, enter the new number and press the Enter \
key.  The palette will be resampled with the new number of colors.  When increasing \
the number of colors, it is sometimes useful to do a Smooth operation afterward \
to smooth out the changes."));
	SetMethod(textBox, CHANGEDVALUE, EnterNColors);

	/*nColors legend*/
	textBox = NewTextBox(left + PCTEXTBOXLEN + MINORBORDER, left + 2 * PCTEXTBOXLEN,
			     MAJORBORDER, MAJORBORDER + TEXTBOXHEIGHT + 4,
			     0, "NColors Legend", "Colors");
	SetVar(textBox, PARENT, panel);
	PrefixList(contents, textBox);

	/*Add in the left color control group*/
	left = MAJORBORDER;
	right = left + PCWINLSIDE;
	top = PCWINHEIGHT - MAJORBORDER;

	/*Revert to saved*/
	bottom = top - BUTTONHEIGHT;
	button = NewButton(left, right, bottom, top, "Revert");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetVar(panel, REVERTBUTTON, button);
	SetVar(button, HELPSTRING,
		NewString("The palette window saves the original palette.  If you have \
made changes that you do not want to keep, press the Revert button to make the \
palette revert back to the way it was.  If you have made changes that you are sure \
you want to keep, press the Keep Changes button to keep them.  From then on, the \
Revert button will make the palette revert to the palette including the \
changes you have kept."));
	SetVar(button, HALTHELP, ObjTrue);
	ActivateButton(button, false);
	SetMethod(button, CHANGEDVALUE, RevertButton);
	top = bottom - MINORBORDER;
	
	/*Keep changes*/
	bottom = top - BUTTONHEIGHT;
	button = NewButton(left, right, bottom, top, "Keep Changes");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetVar(panel, KEEPBUTTON, button);
	SetVar(button, HELPSTRING,
		NewString("The palette window saves the original palette.  If you have \
made changes that you do not want to keep, press the Revert button to make the \
palette revert back to the way it was.  If you have made changes that you are sure \
you want to keep, press the Keep Changes button to keep them.  From then on, the \
Revert button will make the palette revert to the palette including the \
changes you have kept."));
	SetVar(button, HALTHELP, ObjTrue);
	ActivateButton(button, false);
	SetMethod(button, CHANGEDVALUE, KeepButton);
	top = bottom - MAJORBORDER;

	SetVar(palette, KEPTPALETTE, ClonePalette(palette));

	bottom = MAJORBORDER + PCBARUP;
	top = bottom + COLORWHEELWIDTH + TEXTBOXHEIGHT + TEXTBOXSEP;	
	/*Color wheel*/
	colorWheel = NewColorWheel(left + MINORBORDER,
				left + MINORBORDER + COLORWHEELWIDTH,
				   bottom + TEXTBOXHEIGHT + TEXTBOXSEP,
				   top, "Hue/Saturation");
	SetVar(colorWheel, PARENT, panel);
	PrefixList(contents, colorWheel);
	SetVar(colorWheel, REPOBJ, palette);
	ActivateColorWheel(colorWheel, false);
	SetVar(colorWheel, VALUE, false);
SetVar(colorWheel, HELPSTRING,
	NewString("This color wheel controls the hue and saturation of the \
entire range of selected colors.  When you use this control, the entire range \
will be set to the new color."));
	SetVar(colorWheel, HALTHELP, ObjTrue);

	/*Color wheel text box*/
	textBox = NewTextBox(	left + MINORBORDER - MAJORBORDER ,
				left + MAJORBORDER + MINORBORDER + COLORWHEELWIDTH, 
				bottom, bottom + TEXTBOXHEIGHT,
				0, "HS Text", "Hue/Saturation");
	PrefixList(contents, textBox);
	SetVar(textBox, PARENT, panel);
	SetTextAlign(textBox, CENTERALIGN);

	/*Slider*/
	slider = NewSlider(right - MINORBORDER - SLIDERWIDTH,
			   right - MINORBORDER,
			   bottom + TEXTBOXHEIGHT + TEXTBOXSEP,
			   top, PLAIN, "Intensity");
	PrefixList(contents, slider);
	SetVar(slider, PARENT, panel);
	SetSliderRange(slider, 1.0, 0.0, 0.0);
	SetSliderValue(slider, 1.0);
	ActivateSlider(slider, false);
	SetVar(slider, HELPSTRING, NewString("This slider controls the intensity of \
the colors selected in the color bar control to the right.  When a range of colors is selected, this slider controls the value \
of the colors represented in the Hue/Saturation/Value color model.  \
When a range of a single color component is selected, it controls the intensity \
of that component."));
	SetVar(slider, HALTHELP, ObjTrue);

	/*Slider text box*/
	textBox = NewTextBox(right - MINORBORDER - MAJORBORDER - SLIDERWIDTH,
			   right - MINORBORDER + MAJORBORDER,
			   bottom, bottom + TEXTBOXHEIGHT,
			 0, "Intensity Text", "Intensity");
	PrefixList(contents, textBox);
	SetVar(textBox, PARENT, panel);
	SetTextAlign(textBox, CENTERALIGN);

	/*Link slider and color wheel*/
	SetVar(slider, COLORWHEEL, colorWheel);
	SetVar(colorWheel, SLIDER, slider);

	/*Link the color wheels to the color bar*/
	SetVar(slider, COLORBAR, colorBar);
	SetVar(colorWheel, COLORBAR, colorBar);

	/*Waveform tools*/
	top = MAJORBORDER + PCBARHEIGHT + PCBARUP;
	bottom = top - (TITLEBOXTOP + 3 * MINORBORDER + 2 * ICONBUTTONSIZE);
	titleBox = NewTitleBox(left, right, bottom, top, "Edit Component");
	PrefixList(contents, titleBox);
	SetVar(titleBox, PARENT, panel);
	left += MINORBORDER;
	right -= MINORBORDER;
	bottom += MINORBORDER;
	top -= TITLEBOXTOP + MINORBORDER;
	
	/*Icons*/
	radioGroup = NewRadioButtonGroup("Waveform Tools");
	SetVar(colorBar, TOOLGROUP, radioGroup);
	SetVar(radioGroup, HELPSTRING,
		NewString("This is a group of tools that allow you to edit the \
currently selected color component.  \
To use one of these tools, \
first select a range in any one of the color components by dragging through the range \
and then press the button of the tool \
you want to use."));
	SetVar(radioGroup, PARENT, panel);
	PrefixList(contents, radioGroup);
	SetVar(radioGroup, HALTHELP, ObjTrue);
	
	/*Freeform*/
	button = NewIconButton(left, left + ICONBUTTONSIZE,
			top - ICONBUTTONSIZE, top,
			ICONFREEFORM, UIYELLOW, "Free form");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	SetVar(colorBar, FREEFORMBUTTON, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
free form tool, which allows you to edit the waveform simply by clicking \
and drawing in the magnified readout at the top of the color bar to the right.  \
Hold down the Shift key to constrain motion to just the horizontal or \
vertical direction."));

	/*Sine*/
	button = NewIconButton((right + left - ICONBUTTONSIZE) / 2, 
			(right + left + ICONBUTTONSIZE) / 2,
			top - ICONBUTTONSIZE, top,
			ICONSINE, UIYELLOW, "Sine");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	PrefixList(compList, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
sine wave tool, which fills the entire selected range with a sine wave.  \
When this tool is selected, a yellow box in the magnified readout lets you \
change the period, phase, and position of the wave."));

	/*Triangle*/
	button = NewIconButton(right - ICONBUTTONSIZE, right,
			top - ICONBUTTONSIZE, top,
			ICONTRIANGLE, UIYELLOW, "Triangle");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	PrefixList(compList, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
triangle wave tool, which fills the entire selected range with a triangle wave.  \
When this tool is selected, a yellow box in the magnified readout lets you \
change the period, phase, and position of the wave."));

	/*Sawtooth*/
	button = NewIconButton(left, left + ICONBUTTONSIZE,
			top - 2 * ICONBUTTONSIZE - MINORBORDER,
			top - ICONBUTTONSIZE - MINORBORDER,
			ICONSAWTOOTH, UIYELLOW, "Sawtooth");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	PrefixList(compList, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
sawtooth wave tool, which fills the entire selected range with a sawtooth wave.  \
When this tool is selected, a yellow box in the magnified readout lets you \
change the period, phase, and position of the wave."));

	/*Toothsaw*/
	button = NewIconButton((right + left - ICONBUTTONSIZE) / 2, 
			(right + left + ICONBUTTONSIZE) / 2,
			top - 2 * ICONBUTTONSIZE - MINORBORDER,
			top - ICONBUTTONSIZE - MINORBORDER,
			ICONTOOTHSAW, UIYELLOW, "Toothsaw");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	PrefixList(compList, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
reverse sawtooth wave tool, which fills the entire selected range with a reverse sawtooth wave.  \
When this tool is selected, a yellow box in the magnified readout lets you \
change the period, phase, and position of the wave."));

	/*Square*/
	button = NewIconButton(right - ICONBUTTONSIZE, right,
			top - 2 * ICONBUTTONSIZE - MINORBORDER,
			top - ICONBUTTONSIZE - MINORBORDER,
			ICONSQUARE, UIYELLOW, "Square");
	AddRadioButton(radioGroup, button);
	ActivateButton(button, false);
	PrefixList(compList, button);
	SetVar(button, HELPSTRING, NewString("This button selects the \
square wave tool, which fills the entire selected range with a square wave.  \
When this tool is selected, a yellow box in the magnified readout lets you \
change the period, phase, and position of the wave."));

	SetValue(radioGroup, NewInt(PT_FREEFORM));
	SetVar(radioGroup, REPOBJ, colorBar);
	SetMethod(radioGroup, CHANGEDVALUE, ChangeColorTool);
	
	/*Color model controls*/
	left -= MINORBORDER;
	right += MINORBORDER;
	top = top - 2 * ICONBUTTONSIZE - 3 * MINORBORDER;
	bottom = top - TITLEBOXTOP - 2 * MINORBORDER - 2 * CHECKBOXHEIGHT - CHECKBOXSPACING;
	titleBox = NewTitleBox(left, right, bottom, top, "Color Model");
	PrefixList(contents, titleBox);
	SetVar(titleBox, PARENT, panel);

	/*Create the radio button group*/
	radioGroup = NewRadioButtonGroup("Color Models");
	SetVar(radioGroup, PARENT, panel);
	SetVar(panel, CMODELRADIO, radioGroup);
	PrefixList(contents, radioGroup);
	SetVar(radioGroup, REPOBJ, colorBar);
	SetVar(radioGroup, HELPSTRING,
	    NewString("This group controls which color model the palette editor \
uses.  It will affect the information shown in the three color component bars \
in the color bar.  SciAn always uses the RGB color model internally and converts \
to the selected color model for editing."));

	/*And the individual radio buttons*/
	left += MINORBORDER;
	right -= MINORBORDER;
	top -= TITLEBOXTOP + MINORBORDER;
	bottom += MINORBORDER;

	button = NewRadioButton(left, left + (right - left) / 2,
				top - CHECKBOXHEIGHT, top,
				"RGB");
	AddRadioButton(radioGroup, button);
	SetVar(button, HELPSTRING,
	    NewString("This button selects the RGB color model.  In this model, \
color is composed of red, green, and blue components, which are added together \
to produce the final color."));

	button = NewRadioButton(left, left + (right - left) / 2,
				bottom, bottom + CHECKBOXHEIGHT,
				"YIQ");
	AddRadioButton(radioGroup, button);
	SetVar(button, HELPSTRING,
	    NewString("This button selects the YIQ color model.  This color \
model is exactly the same model used to encode  NTSC composite television \
signals.  This color model uses properties of the visual system to optimize \
the amount of information that can be transmitted in the fixed bandwidth of \
the NTSC composite signal.\n\nWARNING: Not all possible YIQ combinations encode \
into valid RGB combinations.  This will only be apparent after you have edited the \
color components in the YIQ model, switch to another color model, and switch back."));

	button = NewRadioButton(left + (right - left) / 2, right,
				top - CHECKBOXHEIGHT, top,
				"HSV");
	AddRadioButton(radioGroup, button);
	SetVar(button, HELPSTRING,
	    NewString("This button selects the HSV color model.  In this model, \
color is composed of hue, saturation, and value.  The hue and saturation control \
the shade of the color, and the value controls its intensity.  This color model \
is used by the color wheels and intensity sliders in SciAn."));

	button = NewRadioButton(left + (right - left) / 2, right,
				bottom, bottom + CHECKBOXHEIGHT,
				"HLS");
	AddRadioButton(radioGroup, button);
	SetVar(button, HELPSTRING,
	    NewString("This button selects the HLS color model.  In this model, \
color is composed of hue, lightness, and saturation.  Hue and saturation control the \
basic shade of the color.  Lightness controls how much black or white is mixed in \
with the color."));

	var = GetIntVar("ShowPaletteControls", palette, COLORMODEL);
	if (var)
	{
	    SetValue(radioGroup, var);
	}

	SetMethod(radioGroup, CHANGEDVALUE, ChangeColorModel);

	/*Center controls*/
	left = PCWINLSIDE + MAJORBORDER * 2;
	right = PCWINLSIDE + MAJORBORDER * 2 +
		(PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	top = PCWINHEIGHT - MAJORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Ramp button*/
 	button = NewButton(left, right,
		bottom, top, "Ramp");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_RAMP));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button interpolates between the selected \
colors to make a smooth color ramp.  When a range in the full color bar is \
selected, all components will be interpolated.  When a range of a single component \
is selected, just that component will be interpolated.\n"));
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Reverse button*/
 	button = NewButton(left, right,
		bottom, top, "Reverse");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_REVERSE));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button reverses the range of selected colors.  \
When a range in the full color bar is \
selected, all components will be reversed.  When a range of a single component \
is selected, just that component will be reversed.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Next column of center controls*/
	left = PCWINLSIDE + MAJORBORDER * 2 +
		MINORBORDER + (PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	right = PCWINLSIDE + MAJORBORDER * 2 +
		MINORBORDER + 2 * (PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	top = PCWINHEIGHT - MAJORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Smooth button*/
 	button = NewButton(left, right,
		bottom, top, "Smooth");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_SMOOTH));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button smooths the selected colors.  \
This will reduce the abrupt changes which can worsen artifacts such as Mach bands.  \
You can smooth a range more by pressing the button repeatedly.  \
When a range in the full color bar is \
selected, all components will be smoothed.  When a range of a single component \
is selected, just that component will be smoothed.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Sharpen button*/
 	button = NewButton(left, right,
		bottom, top, "Sharpen");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_SHARPEN));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button sharpens color distinction within the selected colors.  \
This may increase artifacts such as Mach bands.  Sharpen and Smooth are not \
quite inverse operations, but they have roughly opposite effects.  Sharpen will \
amplify noise and variations in the colors, which will become evident after the \
button is pressed three or four times.  \
When a range in the full color bar is \
selected, all components will be sharpened.  When a range of a single component \
is selected, just that component will be sharpened.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Next column of center controls*/
	left = PCWINLSIDE + MAJORBORDER * 2 +
		2 * MINORBORDER + 2 * (PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	right = PCWINLSIDE + MAJORBORDER * 2 +
		2 * MINORBORDER + 3 * (PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	top = PCWINHEIGHT - MAJORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Ruffle button*/
 	button = NewButton(left, right,
		bottom, top, "Ruffle");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_RUFFLE));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button ruffles the selected colors, which means \
that it reverses short ranges of colors through the selected colors.  This produces \
an effect similar to contours while maintaining a range of colors for the entire \
field.  It is especially successful in windows set to Color Map mode.  \
When a range in the full color bar is \
selected, all components will be ruffled.  When a range of a single component \
is selected, just that component will be ruffled.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

#if 0
	/*Extra button*/
 	button = NewButton(left, right,
		bottom, top, "Extra");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_RUFFLE));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button performs the whatever function.  \
When a range in the full color bar is \
selected, all components will be whatevered.  When a range of a single component \
is selected, just that component will be whatevered.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Last column of center controls*/
	left = PCWINLSIDE + MAJORBORDER * 2 +
		3 * MINORBORDER + 3 * (PCWINWIDTH - 3 * MAJORBORDER - PCWINLSIDE - 3 * MINORBORDER) / 4;
	right = PCWINWIDTH - MAJORBORDER;
	top = PCWINHEIGHT - MAJORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Extra button*/
 	button = NewButton(left, right,
		bottom, top, "Extra");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_RUFFLE));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button performs the whatever function.  \
When a range in the full color bar is \
selected, all components will be whatevered.  When a range of a single component \
is selected, just that component will be whatevered.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;

	/*Extra button*/
 	button = NewButton(left, right,
		bottom, top, "Extra");
	PrefixList(contents, button);
	SetVar(button, PARENT, panel);
	SetVar(button, REPOBJ, palette);
	SetVar(button, COLORBAR, colorBar);
	SetMethod(button, CHANGEDVALUE, SimpleFuncButton);
	SetVar(button, PALETTEFUNC, NewInt(PF_RUFFLE));
	ActivateButton(button, false);
	PrefixList(fullCompList, button);
	SetVar(button, HELPSTRING,
		NewString("This button performs the whatever function.  \
When a range in the full color bar is \
selected, all components will be whatevered.  When a range of a single component \
is selected, just that component will be whatevered.\n")); 
	SetVar(button, HALTHELP, ObjTrue); 
	top = bottom - MINORBORDER;
	bottom = top - BUTTONHEIGHT;
#endif

	/*Now add in the color bar*/
	SetMethod(colorBar, MAKE1HELPSTRING, MakePaletteBarHelp);
	SetVar(colorBar, HALTHELP, ObjTrue);
	PrefixList(contents, colorBar);
	SetVar(colorBar, PARENT, (ObjPtr) panel);
	SetVar(colorBar, REPOBJ, palette);
        SetVar(colorBar, SLIDER, slider);
	SetVar(colorBar, COLORWHEEL, colorWheel);
	SetVar(panel, COLORBAR, colorBar);
	value = NewRealArray(1, 3L);
	((real *) ELEMENTS(value))[0] = 0.0;
	((real *) ELEMENTS(value))[1] = 0.0;
	((real *) ELEMENTS(value))[2] = 0.0;
	SetMethod(colorBar, CHANGEDVALUE, ChangePaletteBar);
	SetValue(colorBar, value);
	ReinitColorBar(colorBar);

	/*Give the color wheels and sliders changedValue routines*/
	SetMethod(colorWheel, CHANGEDVALUE, ChangePaletteColorWheel);
	SetMethod(slider, CHANGEDVALUE, ChangePaletteSlider);
    }
}

void SetObjectColor(col)
ObjPtr col;
/*If col is an int, sets its ui color.
  If col is a real 3-array, sets its value
*/
{
#ifdef GRAPHICS
    if (IsInt(col))
    {
	SetUIColor(GetInt(col));
    }
    else if (IsArray(col) && RANK(col) == 1 && DIMS(col)[0] == 3)
    {
	float clr[3];
	real *elements;
	elements = ELEMENTS(col);
	clr[0] = elements[0];
	clr[1] = elements[1];
	clr[2] = elements[2];
	if (rgbp)
	{
	    c3f(clr);
	}
	else
	{
	    color(ClosestUIColor(clr));
	}
    }
    else
    {
	ReportError("SetObjectColor", "Bad color value");
    }
#endif
}

#define PALDISPLAYBORDER	20
#define PALDISPLAYTEXTWIDTH	0.6
#define PALDISPLAYCOLORWIDTH	0.3
#define PALDISPLAYBOXSIZE	0.08

ObjPtr DrawPaletteDisplay(display)
ObjPtr display;
/*Draws a display*/
{
#ifdef GRAPHICS
    int left, right, bottom, top;
    int l, r, b, t;
    int intLeft, intRight, intBottom, intTop;	/*Interior dims*/
    double halfSpace, ddiff, majorWidth, minorWidth, curValue;
    int pixel;
    long temp;
    int nTics;
    int k, diff, start;
    int alignment;
    Bool drawOverUnder;
    Bool drawMissing;
    Bool numbersOnly;
    Bool drawMinorTics;
    PPtr palette;
    ObjPtr var;
    char *textFont;
    short3 *colors;
    int beg;
    int nColors;
    int textSize;
    ObjPtr textColor;
    int minMajorStep;
    int x, y;
    Bool colorByPalette;

    Get2DIntBounds(display, &left, &right, &bottom, &top);

    intLeft = left + PALDISPLAYBORDER;
    intRight = right - PALDISPLAYBORDER;
    intBottom = bottom + PALDISPLAYBORDER;
    intTop = top - PALDISPLAYBORDER;

    textColor = GetVar(display, COLOR);

    palette = (PPtr) GetPaletteVar("DrawPaletteDisplay", display, REPOBJ);
    if (!palette)
    {
	return ObjFalse;
    }
    colors = ((PPtr) palette) -> colors;
    beg = ((PPtr) palette) -> beg;
    nColors = ((PPtr) palette) -> nColors;

    /*Get colorByPalette predicate*/
    colorByPalette = GetPredicate(display, COLORBYFIELD);

    /*Get draw portions predicates*/
    drawOverUnder = GetPredicate(display, SHOWOVERUNDER);
    drawMissing = GetPredicate(display, SHOWMISSING);
    numbersOnly = GetPredicate(display, NUMBERSONLY);
    drawMinorTics = GetPredicate(display, SHOWMINORTICS);

    /*Set the color palette*/
    SetPalette((ObjPtr) palette);

    /*Draw the background*/
    var = GetVar(display, BACKGROUND);
    if (var)
    {
	SetObjectColor(var);
	FillRect(left, right, bottom, top);
    }

    /*Draw the info on the palette itself*/
    SetObjectColor(textColor);

    var = GetIntVar("DrawPaletteDisplay", display, MINMAJORSTEP);
    if (var)
    {
	minMajorStep = GetInt(var);
    }
    else
    {
	minMajorStep = 30;
    }

    var = GetStringVar("DrawPaletteDisplay", display, TEXTFONT);
    if (var)
    {
	textFont = GetString(var);
    }
    else
    {
	textFont = "Helvetica";
    }

    var = GetIntVar("DrawPaletteDisplay", display, TEXTSIZE);
    if (var)
    {
	textSize = GetInt(var);
    }
    else
    {
	textSize = 18;
    }

    var = GetIntVar("DrawPaletteDisplay", display, ALIGNMENT);
    if (var)
    {
	alignment = GetInt(var);
    }
    else
    {
	alignment = CENTERALIGN;
    }

    SetupFont(textFont, textSize);

    if (1 || right - left < top - bottom)
    {
	/*It's vertical*/
	int boxWidth, boxHeight, stringWidth;
	int rangeBot, rangeTop;

	b = intBottom;
	t = intTop;

	boxWidth = (intRight - intLeft) * PALDISPLAYCOLORWIDTH;
	boxHeight = (intTop - intBottom) * PALDISPLAYBOXSIZE;
	stringWidth = (intRight - intLeft) * PALDISPLAYTEXTWIDTH;

	if (drawMissing)
	{
	    /*Draw the missing data box*/
	    t = b + boxHeight;
	    if (numbersOnly)
	    {
		l = intLeft;
		r = intRight;
	    }
	    else
	    {
	    r = intRight;
	    l = r - boxWidth;
	    FrameRect(l, r, b, t);
	    FrameRect(l - 1, r + 1, b - 1, t + 1);
	    if (rgbp)
	    {
		c3s(colors[0]);
	    }
	    else
	    {
		color(beg);
	    }
	    FillRect(l + 1, r - 1, b + 1, t - 1);
	    SetObjectColor(textColor);

	    r = intLeft + stringWidth;
	    DrawLine(l, (b + t) / 2, r + DSPPALETTESTL, (b + t) / 2);
	    DrawLine(l, (b + t) / 2 + 1, r + DSPPALETTESTL, (b + t) / 2 + 1);
	    l = intLeft;
	    }
	    strcpy(tempStr, "Missing");
	    switch(alignment)
	    {
		case LEFTALIGN:
		    x = l;
		    break;
		case CENTERALIGN:
		    x = (l + r - StrWidth(tempStr)) / 2;
		    break;
		case RIGHTALIGN:
		    x = r - StrWidth(tempStr);
		    break;
	
	    }
	    if (colorByPalette)
	    {
		SetRealColor(missingData);
	    }
	    DrawString(x, (b + t - textSize) / 2, tempStr);
	    if (colorByPalette)
	    {
		SetObjectColor(textColor);
	    }

	    b = t + MINORBORDER;
	    t = intTop;
	}

	if (drawOverUnder)
	{
	    /*Draw the underflow data box*/
	    t = b + boxHeight;
	    if (numbersOnly)
	    {
		l = intLeft;
		r = intRight;
	    }
	    else
	    {
	    r = intRight;
	    l = r - boxWidth;
	    FrameRect(l, r, b, t);
	    FrameRect(l - 1, r + 1, b - 1, t + 1);
	    if (rgbp)
	    {
		c3s(colors[1]);
	    }
	    else
	    {
		color(beg + 1);
	    }
	    FillRect(l + 1, r - 1, b + 1, t - 1);
	    SetObjectColor(textColor);

	    r = intLeft + stringWidth;
	    DrawLine(l, (b + t) / 2, r + DSPPALETTESTL, (b + t) / 2);
	    DrawLine(l, (b + t) / 2 + 1, r + DSPPALETTESTL, (b + t) / 2 + 1);
	    l = intLeft;
	    }
	    strcpy(tempStr, "Under");
	    switch(alignment)
	    {
		case LEFTALIGN:
		    x = l;
		    break;
		case CENTERALIGN:
		    x = (l + r - StrWidth(tempStr)) / 2;
		    break;
		case RIGHTALIGN:
		    x = r - StrWidth(tempStr);
		    break;
	
	    }
	    if (colorByPalette)
	    {
		SetRealColor(MINUSINF);
	    }
	    DrawString(x, (b + t - textSize) / 2, tempStr);
	    if (colorByPalette)
	    {
		SetObjectColor(textColor);
	    }

	    b = t + MINORBORDER;

	    /*Draw the overflow data box*/
	    t = intTop;
	    if (numbersOnly)
	    {
		l = intLeft;
		r = intRight;
	    }
	    else
	    {
	    r = intRight;
	    l = r - boxWidth;
	    FrameRect(l, r, t - boxHeight, t);
	    FrameRect(l - 1, r + 1, t - boxHeight - 1, t + 1);
	    if (rgbp)
	    {
		c3s(colors[nColors - 1]);
	    }
	    else
	    {
		color(beg + nColors - 1);
	    }
	    FillRect(l + 1, r - 1, t - boxHeight + 1, t - 1);
	    SetObjectColor(textColor);

	    r = intLeft + stringWidth;
	    DrawLine(l, t - boxHeight / 2, r + DSPPALETTESTL, t - boxHeight / 2);
	    DrawLine(l, t - boxHeight / 2 + 1, r + DSPPALETTESTL, t - boxHeight / 2 + 1);
	    l = intLeft;
	    }
	    strcpy(tempStr, "Over");
	    switch(alignment)
	    {
		case LEFTALIGN:
		    x = l;
		    break;
		case CENTERALIGN:
		    x = (l + r - StrWidth(tempStr)) / 2;
		    break;
		case RIGHTALIGN:
		    x = r - StrWidth(tempStr);
		    break;
	
	    }
	    if (colorByPalette)
	    {
		SetRealColor(PLUSINF);
	    }
	    DrawString(x, t - boxHeight / 2 - textSize / 2, tempStr);
	    if (colorByPalette)
	    {
		SetObjectColor(textColor);
	    }

	    t = intTop - boxHeight - MINORBORDER;
	}

	/*Draw the main section of the display*/
	r = intRight;
	l = r - boxWidth;
	if (!numbersOnly)
	{
	    FrameRect(l, r, b, t);
	    FrameRect(l - 1, r + 1, b - 1, t + 1);
	}

	/*Do the colors in the center*/
	
	rangeBot = b + 1;
	rangeTop = t - 1;
	diff = t - b - 2;
	if (diff > 0)
	{
	    b = start = rangeBot;
	    if (!numbersOnly)
	    {
	    for (k = 2; k < nColors - 1; ++k)
	    {
		t = (k - 1) * diff / (nColors - 3) + start;
		if (rgbp)
		{
		    c3s(colors[k]);
		}
		else
		{
		    color(beg + k);
		}
		FillRect(l + 1, r - 1, b, t);
		b = t + 1;
	    }
	    }
	    SetObjectColor(textColor);

	    r = l - 1;
	    l = intLeft + stringWidth;

	    /*Draw all the tics in the middle*/
	    halfSpace = (palette -> max - palette -> min) / (palette -> nColors - 4) * 0.5;
	    ddiff = palette -> max - palette -> min;

	    CalcGoodSteps(ddiff,
		      rangeTop - rangeBot,
		      minMajorStep,
		      &majorWidth, &nTics);
	    minorWidth = majorWidth / nTics;

	    /*Minor and major tics first*/
	    temp = palette -> min / majorWidth;
	    curValue = temp * majorWidth;

	    while (curValue > palette -> min)
	    {
		curValue -= majorWidth;
	    }
	    k = 0;
	    while (curValue < palette -> min)
	    {
		++k;
		if (k >= nTics) k = 0;

		curValue += minorWidth;
	    }

	    /*Now actually draw them*/
	    if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
	    while (curValue <= palette -> max + ddiff * 1.0E-6)
	    {
		pixel = rangeBot + (curValue - palette -> min) * (rangeTop - rangeBot) / (ddiff);
		if (k == 0)
		{
		    /*Major tic*/
		    if (numbersOnly)
		    {
			sprintf(tempStr, "%lg", curValue);
			switch(alignment)
			{
			    case LEFTALIGN:
				x = intLeft;
				break;
			    case CENTERALIGN:
			    x = (intLeft + intRight - StrWidth(tempStr)) / 2;
				break;
			    case RIGHTALIGN:
				x = intRight - StrWidth(tempStr);
				break;
			}
		    }
		    else
		    {
			DrawLine(l + DSPPALETTESTL, pixel, r, pixel);
			DrawLine(l + DSPPALETTESTL, pixel + 1, r, pixel + 1);
			sprintf(tempStr, "%lg", curValue);
			switch(alignment)
			{
			    case LEFTALIGN:
				x = intLeft;
				break;
			    case CENTERALIGN:
				x = (intLeft + l - StrWidth(tempStr)) / 2;
				break;
			    case RIGHTALIGN:
				x = l - StrWidth(tempStr);
				break;
			}
		    }
		    if (colorByPalette)
		    {
			SetRealColor(curValue);
		    }
		    DrawString(x, pixel - textSize / 2, tempStr);
		    if (colorByPalette)
		    {
			SetObjectColor(textColor);
		    }
		}
		else if ((!numbersOnly) && drawMinorTics)
		{
		    /*Minor tic*/
		    DrawLine((l + r) / 2, pixel, r, pixel);
		    DrawLine((l + r) / 2, pixel + 1, r, pixel + 1);
		}

		curValue += minorWidth;
		if (ABS(curValue) < ddiff * 1.0E-6) curValue = 0.0;
		++k;
		if (k >= nTics) k = 0;
	    }
	}
    }

    if (AmICurrent(display))
    {
	/* Draw incredibly fancy frame for moving and resizing palette display */

	int horCent = (left + right)/2;
	int vertCent = (bottom + top)/2;

	DrawFrameRect(left+INSET, right-INSET,
			bottom+INSET, top-INSET,
				OUTSIDEFRAMECOLOR, OUTSIDEFRAMEWEIGHT);
	DrawFrameRect(left+INSET+OUTSIDEFRAMEWEIGHT,
			right-INSET-OUTSIDEFRAMEWEIGHT,
			bottom+INSET+OUTSIDEFRAMEWEIGHT,
			top-INSET-OUTSIDEFRAMEWEIGHT,
			INSIDEFRAMECOLOR, INSIDEFRAMEWEIGHT);
	DrawFrameRect(left+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
			right-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
			bottom+INSET+OUTSIDEFRAMEWEIGHT+INSIDEFRAMEWEIGHT,
			top-INSET-OUTSIDEFRAMEWEIGHT-INSIDEFRAMEWEIGHT,
			OUTSIDEFRAMECOLOR, OUTSIDEFRAMEWEIGHT);

	/* Now draw the handles */
	/* center of sides */
	FillUIRect(left, left+HANDLESIZE,
			vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
	FillUIRect(left+OUTSIDEFRAMEWEIGHT,
			left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
			vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
			vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

	FillUIRect(right-HANDLESIZE, right,
			vertCent-HANDLESIZE/2, vertCent+HANDLESIZE/2, OUTSIDEFRAMECOLOR);
	FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			right-OUTSIDEFRAMEWEIGHT,
			vertCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
			vertCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

	/* top edge */
	FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
			top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
	FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
			horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
			top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

	FillUIRect(left, left+HANDLESIZE,
			top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
	FillUIRect(left+OUTSIDEFRAMEWEIGHT,
			left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
			top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
		
	FillUIRect(right-HANDLESIZE, right,
			top-HANDLESIZE, top, OUTSIDEFRAMECOLOR);
	FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			right-OUTSIDEFRAMEWEIGHT,
			top-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			top-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

	/* bottom edge */
	FillUIRect(horCent-HANDLESIZE/2, horCent+HANDLESIZE/2,
			bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
	FillUIRect(horCent-HANDLESIZE/2+OUTSIDEFRAMEWEIGHT,
			horCent+HANDLESIZE/2-OUTSIDEFRAMEWEIGHT,
			bottom+OUTSIDEFRAMEWEIGHT,
			bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);

	FillUIRect(left, left+HANDLESIZE,
			bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
	FillUIRect(left+OUTSIDEFRAMEWEIGHT,
			left+HANDLESIZE-OUTSIDEFRAMEWEIGHT,
			bottom+OUTSIDEFRAMEWEIGHT,
			bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
		
	FillUIRect(right-HANDLESIZE, right,
			bottom, bottom+HANDLESIZE, OUTSIDEFRAMECOLOR);
	FillUIRect(right-HANDLESIZE+OUTSIDEFRAMEWEIGHT,
			right-OUTSIDEFRAMEWEIGHT,
			bottom+OUTSIDEFRAMEWEIGHT,
			bottom+HANDLESIZE-OUTSIDEFRAMEWEIGHT, INSIDEFRAMECOLOR);
    }
#endif
    return ObjTrue;
}

ObjPtr SelectPaletteDisplay(object, selectp)
ObjPtr object;
Bool selectp;
/*Selects an icon*/
{
    Bool alreadySelected;

    if (selectp == alreadySelected)
    {
	return ObjTrue;
    }

    if (selectp)
    {
	MakeMeCurrent(object);
    }

    if (logging)
    {
	char logLine[256];
	MakeObjectName(tempStr, object);
	if (tempStr[0])
	{
	    sprintf(logLine, "%s %s\n", selectp ? "select" : "deselect", tempStr);
	    Log(logLine);
	}
    }
    SetVar(object, SELECTED, NewInt(selectp));
    ImInvalid(object);
    return ObjTrue;
}

ObjPtr PaletteDisplayNotCurrent(object)
ObjPtr object;
/*Makes a palette display not current*/
{
    Select(object, false);
}

#ifdef PROTO
static ObjPtr PressPaletteDisplay(ObjPtr display, int mouseX, int mouseY, long flags)
#else
static ObjPtr PressPaletteDisplay(textBox, mouseX, mouseY, flags)
ObjPtr display;
int mouseX, mouseY;
long flags;
#endif
{
#ifdef INTERACTIVE
    int left, right, bottom, top, hCent, vCent;
    Bool ml, mr, mb, mt;
    int mX, mY;
    ObjPtr var, palette;

    Get2DIntBounds(display, &left, &right, &bottom, &top);

    /* return if mouse outside text box */
    if (mouseX < left || mouseX > right || mouseY < bottom 
	|| mouseY > top) return ObjFalse;

    if (TOOL(flags) == T_HELP) /* help mode? */
    {
	ContextHelp(display);
	return ObjTrue;
    }

    Select(display, true); /* make text box current for editing or adjusting */
    DrawMe(display);
    UpdateDrawing();

    hCent = (left + right)/2;
    vCent = (bottom + top)/2;

    ml = mr = mb = mt = FALSE;

    if (mouseX < left + HANDLESIZE) /* on left side */
    {
	if (mouseY > top - HANDLESIZE) /* top-left handle */
		mt = ml = TRUE;
	else if (mouseY < bottom + HANDLESIZE) /* bottom-left handle */
		mb = ml = TRUE;
	else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
		ml = TRUE; /* bottom middle handle */
	else ml = mr = mb = mt = TRUE; /* in frame */
    }
    else if (mouseX > right - HANDLESIZE) /* on right side */
    {
	if (mouseY > top - HANDLESIZE) /* top-right handle */
		mt = mr = TRUE;
	else if (mouseY < bottom + HANDLESIZE) /* bottom-right handle */
		mb = mr = TRUE;
	else if (mouseY > vCent - HANDLESIZE/2 && mouseY < vCent + HANDLESIZE/2)
		mr = TRUE;
	else ml = mr = mb = mt = TRUE; /* in frame */
    }
    else if (mouseY < bottom + HANDLESIZE) /* on bottom */
    {
	/* already handled (heh heh) corners */
	if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
		mb = TRUE; /* bottom middle handle */
	else ml = mr = mb = mt = TRUE; /* in frame */
    }
    else if (mouseY > top - HANDLESIZE) /* on top */
    {
	/* already handled (heh heh) corners */
	if (mouseX > hCent - HANDLESIZE/2 && mouseX < hCent + HANDLESIZE/2)
		mt = TRUE; /* middle top handle */
	else ml = mr = mb = mt = TRUE; /* in frame */
    }

    if (mr || ml || mb || mt) /* drag the incredibly fancy frame around */
    {
	/* I am greatly indebted to my friend and colleague, Eric Pepke,
	   for the following code. Any errors or obfuscations are his. */
	/*Oh yeah?  Well, I stole it back!*/
	int initX = mouseX, initY = mouseY;
	int newLeft, newRight, newBottom, newTop;
	int oldNewLeft, oldNewRight, oldNewBottom, oldNewTop;

	newLeft = oldNewLeft = left;
	newRight = oldNewRight = right;
	newBottom = oldNewBottom = bottom;
	newTop = oldNewTop = top;

	while (Mouse(&mX, &mY))
	{
	    if (ml) newLeft = left + mX - initX;
	    if (mr) newRight = right + mX - initX;
	    if (mb) newBottom = bottom + mY - initY;
	    if (mt) newTop = top + mY - initY;

	    if (flags & F_OPTIONDOWN)
	    {
		/*Grid drag*/
		if (ml && mr && mb && mt)
		{
		    /*Special case--whole object gridded
			Only grid top left*/
		    int width, height;
		    width = newRight - newLeft;
		    height = newTop - newBottom;
		    newLeft = GRIDX(newLeft);
		    newRight = newLeft + width;
		    newTop = top - (GRIDY(top - newTop));
		    newBottom = newTop - height;
		}
		else
		{
		    /*Normal case*/
		    if (ml) newLeft = GRIDX(newLeft);
		    if (mr) newRight = right - GRIDX(right - newRight);
		    if (mb) newBottom = GRIDY(newBottom);
		    if (mt) newTop = top - GRIDY(top - newTop);
		}
	    }
	    if (ml && newLeft + 3 * HANDLESIZE > newRight)
		newLeft = newRight - 3 * HANDLESIZE;
	    if (mr && newLeft + 3 * HANDLESIZE > newRight)
		newRight = newLeft + 3 * HANDLESIZE;
	    if (mb && newBottom + 3 * HANDLESIZE > newTop)
		newBottom = newTop - 3 * HANDLESIZE;
	    if (mt && newBottom + 3 * HANDLESIZE > newTop)
		newTop = newBottom + 3 * HANDLESIZE;
	    if ((newLeft != oldNewLeft ||
		 newRight != oldNewRight ||
		 newBottom != oldNewBottom ||
		 newTop != oldNewTop) &&
		 newLeft < newRight &&
		 newBottom < newTop)
	    {
		Set2DIntBounds(display, newLeft, newRight, newBottom, newTop);
		oldNewLeft = newLeft;
		oldNewRight = newRight;
		oldNewBottom = newBottom;
		oldNewTop = newTop;
		DrawMe(display);
	    }
	}
	if (logging)
	{
	    char cmd[256];
	    MakeObjectName(tempStr, display);
	    sprintf(cmd, "set bounds %s [%d %d %d %d]\n",
		    tempStr, newLeft, newRight,
		    newBottom, newTop);
	    Log(cmd);
	}
    }
    return ObjTrue;
#else
    return ObjFalse;
#endif
}


ObjPtr NewPaletteDisplay(left, right, bottom, top, name, palette)
int left, right, bottom, top;
char *name;
ObjPtr palette;
/*Makes a new palette display*/
{
    ObjPtr retVal;
    retVal = NewObject(paletteDisplayClass, 0);
    if (!retVal)
    {
	return NULLOBJ;
    }

    Set2DIntBounds(retVal, left, right, bottom, top);
    SetVar(retVal, NAME, NewString(name));
    SetVar(retVal, REPOBJ, palette);

    return retVal;
}

void InitColors()
/*Initialize the color system*/
{
    int k;
    int colorBeg, colorEnd;
    int cmapBitPlanes;
    int rgbTest;
    Bool showConfig;			/*True iff want to show configuration*/
    ObjPtr var;

    if (getenv("SCIAN_SHOW_CONFIG"))
    {
	showConfig = true;
    }
    else
    {
	showConfig = false;
    }

    /*Make a color palette class*/
    paletteClass = NewObject(NULLOBJ, sizeof(Palette) - sizeof(Thing));
    AddToReferenceList(paletteClass);
    paletteClass -> flags = PALETTE;
    ((PPtr) paletteClass) -> colors = 0;
    ((PPtr) paletteClass) -> components = 0;
    ((PPtr) paletteClass) -> beg = 0;
    ((PPtr) paletteClass) -> nColors = 0;
    SetVar(paletteClass, DEFAULTICON, iconColorPalette);
    SetVar(paletteClass, COLORMODEL, NewInt(CM_RGB));
    SetMethod(paletteClass, NAME, MakePaletteName);
    SetMethod(paletteClass, CLONE, ClonePalette);
    SetMethod(paletteClass, CLEANUP, CleanupPalette);
    SetMethod(paletteClass, NEWCTLWINDOW, ShowPaletteControls);

    if (showConfig)
    {
	printf("Calculating machine parameters\n");
    }

#ifdef GRAPHICS
    /*Shoot for RGB double buffer*/
    hasRGB = true;
    hasDouble = true;
#ifdef GD_BITS_NORM_DBL_RGB
    rgbTest = getgdesc(GD_BITS_NORM_DBL_RGB);
#else
#ifdef GD_BITS_NORM_DBL_RED
    rgbTest = getgdesc(GD_BITS_NORM_DBL_RED) &&
		   getgdesc(GD_BITS_NORM_DBL_GREEN) && 
		   getgdesc(GD_BITS_NORM_DBL_BLUE);
#else
!! No double-buffer RGB bits constant is defined
#endif
#endif

    /*If not enough bit planes, shoot for single buffer*/
    if (!rgbTest)
    {
	if (showConfig)
	{
	    printf("Not enough bit planes for double buffer RGB.  Going to single buffer.\n");
	}
	hasDouble = false;
#ifdef GD_BITS_NORM_SNG_RGB
	rgbTest = getgdesc(GD_BITS_NORM_SNG_RGB);
#else
#ifdef GD_BITS_NORM_SNG_RED
	rgbTest = getgdesc(GD_BITS_NORM_SNG_RED) &
		   getgdesc(GD_BITS_NORM_SNG_GREEN) &
		   getgdesc(GD_BITS_NORM_SNG_BLUE);
#else
!! No single-buffer RGB bits constant is defined
#endif
#endif
	if (rgbTest)
	{
	    if (showConfig)
	    {
		printf("Not enough bit planes for single buffer.  Giving up on RGB mode.\n");
	    }
	    hasRGB = false;
	    hasDouble = true;
	}
	else
	{
	    if (showConfig)
	    {
		printf("Single buffer RGB OK.\n");
	    }
	}
    }
    else
    {
	if (showConfig)
	{
	    printf("Double buffer RGB OK.\n");
	}
    }

    if (getenv("SCIAN_VETO_RGB"))
    {
	if (showConfig)
	{
	    printf("RGB mode vetoed.\n");
	}
	hasRGB = false;
    }

    if (getenv("SCIAN_FORCE_RGB"))
    {
	if (showConfig)
	{
	    printf("RGB mode forced.\n");
	}
	hasRGB = true;
    }

    /*Now check color map mode*/
    hasCmap = true;
    cmapBitPlanes = getgdesc(hasDouble ? GD_BITS_NORM_DBL_CMODE : GD_BITS_NORM_SNG_CMODE);

    if (showConfig)
    {
	printf("%d double buffer color map bitplanes.\n", cmapBitPlanes);
    }

#if MACHINE == RS6000
    cmapBitPlanes = 8;
#endif

    if (cmapBitPlanes < 8)
    {
	/*Not enough bit planes for cmap*/
	if (hasRGB)
	{
	    if (showConfig)
	    {
		printf("Not enough bit planes for color map mode.  Giving up on color map mode.\n");
	    }
	    hasCmap = false;
	}
	else
	{
	    /*Try single buffer*/
	    if (showConfig)
	    {
		printf("Not enough bit planes for double buffer color map mode.  Trying single.\n");
	    }
	    hasDouble = false;
	    cmapBitPlanes = getgdesc(GD_BITS_NORM_SNG_CMODE);
	    if (showConfig)
	    {
		printf("%d double buffer color map bitplanes.\n", cmapBitPlanes);
	    }
	    if (cmapBitPlanes < 8)
	    {
		printf("Not enough bit planes to do anything useful.  Giving up entirely.\n");
		exit(-1);
	    }
	}
    }
    else
    {
	if (showConfig)
	{
	    printf("Double buffer color map mode OK.\n");
	}
    }

    if (getenv("SCIAN_VETO_CMAP"))
    {
	if (showConfig)
	{
	    printf("Color map mode vetoed.\n");
	}
	hasCmap = false;
    }

    if (getenv("SCIAN_FORCE_CMAP"))
    {
	if (showConfig)
	{
	    printf("Color map mode forced.\n");
	}
	hasCmap = true;
    }

    if (getenv("SCIAN_VETO_DOUBLE"))
    {
	if (showConfig)
	{
	    printf("Double buffer mode vetoed.\n");
	}
	hasDouble = false;
    }

    if (getenv("SCIAN_FORCE_DOUBLE"))
    {
	if (showConfig)
	{
	    printf("Double buffer mode forced.\n");
	}
	hasDouble = true;
    }

#ifdef GD_BLEND
    if (getgdesc(GD_BLEND))
    {
	hasTransparency = true;
	if (showConfig)
	{
	    printf("Blending transparency OK.\n");
	}
    }
    else
#endif
    {
	hasTransparency = false;
	if (showConfig)
	{
	    printf("There is no blending transparency.\n");
	}
    }

#ifdef GD_BITS_ZBUFFER
    if (getgdesc(GD_BITS_ZBUFFER) > 0)
#else
#ifdef GD_BITS_NORM_ZBUFFER
    if (getgdesc(GD_BITS_NORM_ZBUFFER) > 0)
#else
!! No Z-buffer constant defined
#endif
#endif
    {
	hasZbuf = true;
	if (showConfig)
	{
	    printf("Z-buffer OK.\n");
	}
    }
    else
    {
	hasZbuf = false;
	if (showConfig)
	{
	    printf("There is no Z-buffer.\n");
	}
    }

    /*If there is a cmap mode, figure out the number of colors*/
    if (hasCmap)
    {
	char *nColorsString;
	char *colorBegString;
	colorEnd = 1;
	for (k = 0; k < cmapBitPlanes; ++k)
	{
	    colorEnd *= 2;
	}

	/*Trim off the top 512 if it's too big for the GTX version*/
	if (colorEnd >= 4096)
	{
	    colorEnd -= 512;
	}

	/*Determine beginning based on end*/
	if (colorEnd <= 512)
	{
	    colorBeg = 16;
	}
	else
	{
	    colorBeg = 512;
	}

	if (colorBegString = getenv("SCIAN_COLOR_BEG"))
	{
	    int temp;
	    if (1 == sscanf(colorBegString, " %d", &temp))
	    {
		colorBeg = temp;
		if (showConfig)
		fprintf(stderr, "Color beginning overridden to %d\n", temp);
	    }
	    else
	    {
		fprintf(stderr, "Bad color beginning: %s\n", colorBegString);
	    }
	}

	if (nColorsString = getenv("SCIAN_N_COLORS"))
	{
	    int nColors;
	    if (1 == sscanf(nColorsString, " %d", &nColors))
	    {
		colorEnd = colorBeg + nColors;
		if (showConfig)
		printf("Number of colors overridden to be %d\n", nColors);
	    }
	    else
	    {
		fprintf(stderr, "Bad value for environment variable SCIAN_N_COLORS: %s\n", nColorsString);
	    }
	}

	if (showConfig)
	{
	    printf("Colors from %d to %d\n", colorBeg, colorEnd);
	}

	uiColorBeg = colorBeg;
	colorBeg += NUICOLORS; 

	/*Make first color range*/
	colorRanges = new(ColorRange);
	colorRanges -> beg = colorBeg;
	colorRanges -> end = colorEnd;
	colorRanges -> next = (ColorRange *) 0;
    }
    else
    {
	colorRanges = 0;
    }

    /*Initialize user interface colors*/
    for (k = 0; k < 8; ++k)
    {
	MakeUIColor(UIBLACK + k, k * 32, k * 32, k * 32);
    }
    MakeUIColor(UIWHITE, 255, 255, 255);

    MakeUIColor(UIRED, 225, 0, 0);
    MakeUIColor(UIGREEN, 0, 225, 0);
    MakeUIColor(UIBLUE, 0, 0, 225);
    MakeUIColor(UIMAGENTA, 225, 0, 225);
    MakeUIColor(UIYELLOW, 245, 245, 0);
    MakeUIColor(UICYAN, 0, 225, 225);
    MakeUIColor(UIORANGE, 255, 88, 0);
    MakeUIColor(UIPURPLE, 100, 0, 225);
    MakeUIColor(UIGOLD, 247, 188, 0);

    MakeUIColor(UIPRED, 182, 128, 128);
    MakeUIColor(UIPGREEN, 128, 171, 128);
    MakeUIColor(UIPBLUE, 128, 145, 171);
    MakeUIColor(UIPMAGENTA, 171, 128, 171);
    MakeUIColor(UIPYELLOW, 171, 171, 128);
    MakeUIColor(UIPCYAN, 128, 171, 171);
#endif

    colorControlClass = NewObject(controlClass, 0);
    AddToReferenceList(colorControlClass);

    colorWheelClass = NewObject(colorControlClass, 0);
    AddToReferenceList(colorWheelClass);
#ifdef GRAPHICS
    SetMethod(colorWheelClass, DRAW, DrawColorWheel);
#endif
#ifdef INTERACTIVE
    SetMethod(colorWheelClass, PRESS, TrackColorWheel);
#endif
    SetMethod(colorWheelClass, SETVAL, SetColorWheelVal);
    SetVar(colorWheelClass, TYPESTRING, NewString("color wheel"));
    SetVar(colorWheelClass, HELPSTRING, NewString("To select a color, click at the color you desire.  \
Colors around the edge of the circle are fully saturated; colors near the center are \
less saturated.  Hold down the Shift key while pressing to \
constrain to full or half saturation.  Double-click to snap to the closest full- or half-saturated \
color or white.")); 

    /*Create a color bar class*/
    colorBarClass = NewObject(controlClass, 0);
    AddToReferenceList(colorBarClass);
#ifdef GRAPHICS
    SetMethod(colorBarClass, DRAW, DrawColorBar);
#endif
#ifdef INTERACTIVE
    SetMethod(colorBarClass, PRESS, PressColorBar);
#endif
    SetMethod(colorBarClass, SETVAL, SetColorBarVal);

    /*Create a palette display class*/
    paletteDisplayClass = NewObject(controlClass, 0);
    AddToReferenceList(paletteDisplayClass);
    SetVar(paletteDisplayClass, SHOWMINORTICS, ObjTrue);
#ifdef GRAPHICS
    SetMethod(paletteDisplayClass, DRAW, DrawPaletteDisplay);
#endif
#ifdef INTERACTIVE
    SetMethod(paletteDisplayClass, PRESS, PressPaletteDisplay);
#endif
    SetMethod(paletteDisplayClass, NEWCTLWINDOW, ShowPaletteDisplayControls);

    var = NewRealArray(1, 3L);
    ((real *) ELEMENTS(var))[0] = 0.75;
    ((real *) ELEMENTS(var))[1] = 0.75;
    ((real *) ELEMENTS(var))[2] = 0.75;
    SetVar(paletteDisplayClass, COLOR, var);
    SetTextFont(paletteDisplayClass, DSPPALETTEFONT);
    SetTextSize(paletteDisplayClass, DSPPALETTESIZE);
    SetMethod(paletteDisplayClass, SELECT, SelectPaletteDisplay);
    SetMethod(paletteDisplayClass, YOURENOTCURRENT, PaletteDisplayNotCurrent);
    SetTextAlign(paletteDisplayClass, RIGHTALIGN);
    SetVar(paletteDisplayClass, TICDENSITY, NewReal(10.0));
    SetVar(paletteDisplayClass, MINMAJORSTEP, NewInt(30));
}

void KillColors()
{
    DeleteThing(paletteDisplayClass);
    DeleteThing(colorBarClass);
    DeleteThing(colorWheelClass);
    DeleteThing(colorControlClass);
    DeleteThing(paletteClass);
}
Modified: Sun Nov 17 17:00:00 1996 GMT
Page accessed 2672 times since Sat Apr 17 21:54:16 1999 GMT