#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <sys/stat.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <tcl/tcl.h>
#include <Obm.h>
#include <ObmW/Gterm.h>
#include "ximtool.h"

/*
 * XIMCLIENT.C -- The Ximtool "client" object.  This code implements an OBM
 * client and responds to messages sent to the client object by the GUI code
 * executing under the object manager.
 *
 *	     xim_clientOpen (xim)
 *	    xim_clientClose (xim)
 *	  xim_clientExecute (xim, objname, key, cmd)
 *
 * The clientExecute callback is called by the GUI code in the object manager
 * to execute ximtool client commands.
 *
 * Client commands:
 *
 *		   setFrame  frameno
 *       frameno = getFrame  [raster]
 *	 raster = getRaster  [frameno]
 *	  frame = getSource  [raster [sx sy snx sny]]
 *		  nextFrame
 *		  prevFrame
 *		matchFrames  [frames [reference_frame]]
 *	     registerFrames  [frames [reference_frame]]
 *	    offfsetRegister  [frames [reference_frame]]
 *		   fitFrame
 *		 clearFrame  [frame]
 *
 *		  setOption  option value [args]
 *		setColormap  colormap
 *		  setOffset  xoff yoff
 *	     windowColormap  offset scale
 *	          windowRGB  color offset scale save_flag
 *		       zoom  [mag | xmag ymag [ xcen ycen ]]
 *		    zoomAbs  [mag | xmag ymag [ xcen ycen ]]
 *        center = centroid  xcen ycen size [type]
 *          pix = getPixels  x0 y0 nx ny [format]
 *			pan  xcen ycen
 *		       flip  axis [axis ...]
 *
 *           setPrintOption  option value [args]
 *                    print  [x0 y0 nx ny]
 *            setSaveOption  option value [args]
 *                     save  [x0 y0 nx ny]
 *            setLoadOption  option value [args]
 *                     load
 *                     help
 *                     info  option
 *
 *       wcsstr = encodewcs  sx sy sz
 *	       retCursorVal  sx sy [frame [wcs [key [strval]]]]
 *
 *		  ism_start  task
 *		   ism_stop  task
 *		    ism_cmd  task [args]
 *
 *		 initialize
 *		      Reset
 *		       Quit
 */


/* Client callback struct. */
typedef struct {
        XimDataPtr xim;
        Tcl_Interp *tcl;
        Tcl_Interp *server;
} XimClient, *XimClientPtr;



static int initialize(), Reset(), Quit();
static int setColormap(), updateColormap(), windowColormap();
static int zoom(), pan(), getSource();
static int setFrame(), getFrame(), getRaster(), nextFrame(), prevFrame();
static int fitFrame(), matchFrames(), registerFrames(), retCursorVal();
static int encodewcs(), flip(), clearFrame(), setOption(), setOffset();
static int setPrintOption(), setSaveOption(), setLoadOption();
static int print(), save(), load(), help(), windowRGB();
static int centroid(), getPixels();
static int ism_start(), ism_stop(), ism_cmd();
static void info_server(), info_imtoolrc(), info_clients(), cmapScale();

extern int ism_evaluate(), info();
extern IsmModule ismNameToPtr();

void info_wcs();

/* xim_clientOpen -- Initialize the ximtool client code.
 */
void
xim_clientOpen (xim)
XimDataPtr xim;
{
	register XimClientPtr xc;
	register Tcl_Interp *tcl;

	xc = (XimClientPtr) XtCalloc (1, sizeof(XimClient));
	xim->clientPrivate = (int *)xc;

	xc->xim = xim;
	xc->tcl = tcl = Tcl_CreateInterp();
	ObmAddCallback (xim->obm, OBMCB_clientOutput|OBMCB_preserve,
	    xim_clientExecute, (XtPointer)xc);

	Tcl_CreateCommand (tcl,
	    "Quit", Quit, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "Reset", Reset, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "initialize", initialize, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setFrame", setFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "getFrame", getFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "getRaster", getRaster, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "getSource", getSource, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "nextFrame", nextFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "prevFrame", prevFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "fitFrame", fitFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "matchFrames", matchFrames, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "registerFrames", registerFrames, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "offsetRegister", registerFrames, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "clearFrame", clearFrame, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setOption", setOption, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setColormap", setColormap, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setOffset", setOffset, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "windowColormap", windowColormap, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "updateColormap", updateColormap, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "zoom", zoom, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "zoomAbs", zoom, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "centroid", centroid, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "getPixels", getPixels, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "pan", pan, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "flip", flip, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "retCursorVal", retCursorVal, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "encodewcs", encodewcs, (ClientData)xc, NULL);

	Tcl_CreateCommand (tcl,
	    "setPrintOption", setPrintOption, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "print", print, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setSaveOption", setSaveOption, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "save", save, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "setLoadOption", setLoadOption, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "load", load, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "help", help, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "info", info, (ClientData)xc, NULL);
	Tcl_CreateCommand (tcl,
	    "windowRGB", windowRGB, (ClientData)xc, NULL);

	/* ISM module callbacks. */
        Tcl_CreateCommand (tcl,
            "ism_start", ism_start, (ClientData)xc, NULL);
        Tcl_CreateCommand (tcl,
            "ism_stop", ism_stop, (ClientData)xc, NULL);
        Tcl_CreateCommand (tcl,
            "ism_cmd", ism_cmd, (ClientData)xc, NULL);

	Tcl_Preserve(tcl);
}


/* xim_clientClose -- Shutdown the ximtool client code.
 */
void
xim_clientClose (xim)
XimDataPtr xim;
{
	register XimClientPtr xc = (XimClientPtr) xim->clientPrivate;
	Tcl_Eval(xc->tcl, "exit");
	if (!Tcl_InterpDeleted(xc->tcl))
	    Tcl_DeleteInterp (xc->tcl);
	Tcl_Release(xc->tcl);
}


/* xim_clientExecute -- Called by the GUI code to send a message to the
 * "client", which from the object manager's point of view is ximtool itself.
 */
int
xim_clientExecute (xc, tcl, objname, key, command)
register XimClientPtr xc;
Tcl_Interp *tcl;		/* caller's Tcl */
char *objname;			/* object name */
int key;			/* notused */
char *command;
{
	register XimDataPtr xim = xc->xim;

	xc->server = tcl;
	if (strcmp (objname, "client") == 0)
            Tcl_Eval (xc->tcl, command);
	else
	    ism_evaluate (xim, objname, command);

	return (0);
}


/*
 * XIMTOOL CLIENT commands.
 * ----------------------------
 */

/* Quit -- Exit ximtool.
 *
 * Usage:	Quit
 */
static int
Quit (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	return xim_shutdown (xim);
}


/* Reset -- Reset ximtool.
 *
 * Usage:	Reset
 *
 * Reset does a full power-on reset of ximtool.
 */
static int
Reset (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	xim_initialize (xim, xim->fb_configno, xim->nframes, 1);
	return 0;
}


/* initialize -- Reinitialize ximtool.
 *
 * Usage:	initialize
 *
 * Initialize does a partial reinitialization of ximtool, preserving the
 * current frame buffers and view.
 */
static int
initialize (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	xim_initialize (xim, xim->fb_configno, xim->nframes, 0);
	return 0;
}


/* setFrame -- Set the frame to be displayed.
 *
 * Usage:	setFrame <frameno>
 */
static int 
setFrame (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	if (argc == 2)
	    xim_setFrame (xc->xim, atoi(argv[1]));
	return (TCL_OK);
}


/* getFrame -- Get the frame number.
 *
 * Usage:	frameno = getFrame [raster]
 *
 * This routine has two forms.  When called with no argument getFrame returns
 * the current display frame.  When called with a raster number getFrame
 * returns the frame number with which the raster is associated.
 */
static int 
getFrame (xc, tcl, argc, argv)
XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb;
	char frameno[SZ_NAME];
	register int i;
	int raster;

	if (argc == 1)
	    sprintf (frameno, "%d", xc->xim->display_frame);
	else {
	    raster = atoi (argv[1]);
	    strcpy (frameno, "0");

	    for (i=1;  i <= xim->nframes;  i++) {
		fb = &xim->frames[i-1];
		if (fb->raster == raster || fb->zoomras == raster) {
		    sprintf (frameno, "%d", fb->frameno);
		    break;
		}
	    }
	}

	Tcl_SetResult (xc->server, frameno, TCL_VOLATILE);
	return (TCL_OK);
}


/* getRaster -- Get the image raster number of the given frame.
 *
 * Usage:	raster = getRaster [frameno]
 *
 * This routine has two forms.  When called with no argument getRaster returns
 * the raster number of the current display frame.  When called with a frame
 * number getRaster returns the raster number of the given frame.
 */
static int 
getRaster (xc, tcl, argc, argv)
XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb;
	char buf[SZ_NAME];
	register int i;
	int frameno;

	if (argc == 1)
	    sprintf (buf, "%d", xim->df_p->raster);
	else {
	    frameno = atoi (argv[1]);
	    strcpy (buf, "0");

	    for (i=1;  i < xim->nframes;  i++) {
		fb = &xim->frames[i-1];
		if (fb->frameno == frameno) {
		    sprintf (buf, "%d", fb->raster);
		    break;
		}
	    }
	}

	Tcl_SetResult (xc->server, buf, TCL_VOLATILE);
	return (TCL_OK);
}


/* getSource -- Get the source of the currently displayed image.
 *
 * Usage:	frame = getSource [raster [sx sy snx sny]]
 *
 * getSource returns the frame number of the currently displayed frame as
 * the function value, and the raster number and source rect within that
 * raster are returned as function arguments.
 */
static int 
getSource (xc, tcl, argc, argv)
XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	int src, st, sx, sy, snx, sny;
	int dst, dt, dx, dy, dnx, dny;
	char buf[SZ_NAME];
	int rop;

	GtGetMapping (xim->gt, fb->zoommap, &rop,
	    &src,&st,&sx,&sy,&snx,&sny, &dst,&dt,&dx,&dy,&dnx,&dny);

	if (argc > 1) {
	    sprintf (buf, "%d", fb->raster);
		Tcl_SetVar (xc->server, argv[1], buf, 0);
	}
	if (argc > 2) {
	    sprintf (buf, "%d", sx);
		Tcl_SetVar (xc->server, argv[2], buf, 0);
	    sprintf (buf, "%d", sy);
		Tcl_SetVar (xc->server, argv[3], buf, 0);
	    sprintf (buf, "%d", snx);
		Tcl_SetVar (xc->server, argv[4], buf, 0);
	    sprintf (buf, "%d", sny);
		Tcl_SetVar (xc->server, argv[5], buf, 0);
	}

	sprintf (buf, "%d", fb->frameno);
	Tcl_SetResult (xc->server, buf, TCL_VOLATILE);

	return (TCL_OK);
}


/* nextFrame -- Display the next frame in sequence.
 *
 * Usage:	nextFrame
 */
static int 
nextFrame (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int frame;

	if (xim->display_frame < xim->nframes)
	    frame = xim->display_frame + 1;
	else
	    frame = 1;

	xim_setFrame (xc->xim, frame);
	return (TCL_OK);
}


/* prevFrame -- Display the previous frame in sequence.
 *
 * Usage:	prevFrame
 */
static int 
prevFrame (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int frame;

	if (xim->display_frame > 1)
	    frame = xim->display_frame - 1;
	else
	    frame = xim->nframes;

	xim_setFrame (xc->xim, frame);
	return (TCL_OK);
}


/* matchFrames -- Set the enhancement of the listed frames to match that of
 * the given reference frame.  If no reference frame is given the current
 * display frame is used.  If no frames are listed all frames are matched
 * to the current display frame.
 *
 * Usage:	matchFrames [frames [reference_frame]]
 */
static int 
matchFrames (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int *frames, frame_list[32], reference_frame;
	int nitems, i;
	const char **items;

	/* Get reference frame. */
	if (argc > 2)
	    reference_frame = atoi (argv[2]);
	else
	    reference_frame = xim->display_frame;

	/* Get frame list. */
	if (argc > 1) {
	    if (Tcl_SplitList (tcl, argv[1], &nitems, &items) != TCL_OK)
		goto nolist;
	    for (i=0, frames=frame_list;  i < nitems;  i++)
		frames[i] = atoi (items[i]);
	    frames[i] = 0;
	    Tcl_Free ((char *)items);
	} else
nolist:	    frames = NULL;

	xim_matchFrames (xc->xim, frames, reference_frame);
	return (TCL_OK);
}


/* registerFrames -- Register the listed frames with the given reference
 * frame.  If no reference frame is given the current display frame is used.
 * If no frames are listed all frames are registered with the current
 * display frame.
 *
 * Usage:	registerFrames [frames [reference_frame]]
 * 		offsetRegister [frames [reference_frame]]
 */
static int 
registerFrames (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int *frames, frame_list[32], reference_frame;
	int nitems, i, offsets;
	const char **items;

	/* Get reference frame. */
	if (argc > 2)
	    reference_frame = atoi (argv[2]);
	else
	    reference_frame = xim->display_frame;

	/* Get frame list. */
	if (argc > 1) {
	    if (Tcl_SplitList (tcl, argv[1], &nitems, &items) != TCL_OK)
		goto nolist;
	    for (i=0, frames=frame_list;  i < nitems;  i++)
		frames[i] = atoi (items[i]);
	    frames[i] = 0;
	    Tcl_Free ((char *)items);
	} else
nolist:	    frames = NULL;

	offsets = (strcmp (argv[0], "offsetRegister") == 0);
	xim_registerFrames (xc->xim, frames, reference_frame, offsets);
	return (TCL_OK);
}


/* setOffset -- Set the offset for the current display frame buffer.
 *
 * Usage:	setOffset xoff yoff
 */
static int 
setOffset (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
        register FrameBufPtr fb = xim->df_p;
	float	 xcen, ycen;
	float	 xmag, ymag;
	float	 xoff, yoff;
	Boolean  absolute = False;
	int	 frame = xim->display_frame - 1;


	if (argc < 3)
	    return (TCL_ERROR);

	xcen = fb->xcen;
	ycen = fb->ycen;
	xmag = fb->xmag;
	ymag = fb->ymag;

	/* Get offset values for the frame. */
	xoff = atof (argv[1]);
	yoff = atof (argv[2]);

	/* Set frame offset.  */
        xim_setZoom (xim, fb, fb->frameno, fb->zoommap,
            fb->raster, fb->zoomras, xcen, ycen, xmag, ymag, xoff, yoff,
	    absolute);

	/* Now set he frame values independent of the display frame. */
	fb = &xim->frames[frame];
	fb->xoff = xoff;
	fb->yoff = yoff;

	return (TCL_OK);
}



/* clearFrame -- Clear the given frame, or the current frame in no frame
 * number is given.
 *
 * Usage:	clearFrame [frame]
 */
static int 
clearFrame (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int frame;

	if (argc > 1)
	    frame = atoi (argv[1]);
	else
	    frame = xim->display_frame;

	xim_eraseFrame (xc->xim, frame);
	return (TCL_OK);
}


/* fitFrame -- Attempt to make the display window the same size as the frame
 * buffer.
 *
 * Usage:	fitFrame
 */
static int 
fitFrame (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	xim_fitFrame (xc->xim);
	return (TCL_OK);
}


/* setOption -- Set an ximtool client option.
 *
 * Usage:	setOption option value [args]
 *
 * Options:
 *	autoscale	true|false
 *	antialias	true|false [type]
 *	tileFrames	true|false [frames]
 *	tileByRow	true|false
 *	tileTopDown	true|false
 *	tileGeom	type|geom  [frames]
 *	cmfocus		size
 */
char *h_orient[] = {
    "1x1","2x1","3x1","2x2", "3x2","3x2","4x2","4x2", "3x3","5x2","4x3","4x3"
};
char *v_orient[] = {
    "1x1","1x2","1x3","2x2", "2x3","2x3","2x4","2x4", "3x3","2x5","3x4","3x4"
};

static int 
setOption (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	char *option, *strval;
	const char **items;
	char buf[SZ_LINE];
	int ch, value, nx, ny, nitems, i, frame_list=0;


	if (argc < 3)
	    return (TCL_ERROR);
	else {
	    option = argv[1];
	    strval = argv[2];

	    if (strcmp (option, "tileGeom") != 0) {
	        ch = strval[0];
		if (isdigit (ch))
		    value = atoi (strval);
	        else if (ch == 'T' || ch == 't')
		    value = 1;
	        else if (ch == 'F' || ch == 'f')
		    value = 0;
	    }
	}

	if (strcmp (option, "autoscale") == 0) {
	    if (xim->autoscale != value) {
		xim->autoscale = value;
		xim_resize (xim, xim->gt);
		sprintf (buf, "%s", value ? "True" : "False");
		xim_message (xim, "autoscale", buf);
	    }
	} else if (strcmp (option, "antialias") == 0) {
	    if (xim->antialias != value) {
		xim->antialias = value;
		if (value) {
		    if (argc > 3)
			xim->rop = xim_getAntialias (xim, argv[3]);
		    else
			xim->rop = xim_getAntialias (xim, xim->antialiasType);
		} else
		    xim->rop = 0;
		xim_setRop (xim, fb, xim->rop);
		sprintf (buf, "%s", value ? "True" : "False");
		xim_message (xim, "antialias", buf);
	    }

	} else if (strcmp (option, "cmfocus") == 0) {
	    if (xim->cm_focus != value) {
		int box_size = value;
		xim->cm_focus = box_size;
		GtSetColormapFocus (box_size);
	    }

	} else if (strcmp (option, "tileFrames") == 0) {
	    if (xim->tileFrames != value) {
		/* Get list of frames to be tiled. */
		if (argc > 3) {
		    if (Tcl_SplitList (tcl, argv[3], &nitems, &items) != TCL_OK)
			goto nolist1;
		    for (i=0;  i < nitems;  i++)
			frame_list |= (1 << (atoi(items[i]) - 1));
		    Tcl_Free ((char *)items);
		} else {
nolist1:	    for (i=0;  i < xim->nframes;  i++)
			frame_list |= (1 << i);
		    nitems = xim->nframes;
		}

		/* Set or clear tile frame mode. */
		xim_tileFrames (xim, value ? frame_list : 0);
	    }

	} else if (strcmp (option, "tileByRows") == 0) {
	    xim->tileByRows = value;
	    xim_tileFrames (xim, xim->tileFramesList);

	} else if (strcmp (option, "tileTopDown") == 0) {
	    xim->tileTopDown = value;
	    xim_tileFrames (xim, xim->tileFramesList);

	} else if (strcmp (option, "tileLabels") == 0) {
	    xim->tileLabels = value;
	    xim_tileFrames (xim, xim->tileFramesList);

	} else if (strcmp (option, "tileGeom") == 0) {
	    /* Get list of frames to be tiled. */
	    if (argc > 3) {
	        if (Tcl_SplitList (tcl, argv[3], &nitems, &items) != TCL_OK)
	    	    goto nolist2;
	        for (i=0;  i < nitems;  i++)
	    	    frame_list |= (1 << (atoi(items[i]) - 1));
	        Tcl_Free ((char *)items);
	    } else {
nolist2:        for (i=0;  i < xim->nframes;  i++)
	    	    frame_list |= (1 << i);
		nitems = xim->nframes;
	    }
	    nitems = max (nitems, 1);


	    /* Get the option or tile geometry. */
	    if (strcmp (strval, "Best") == 0) {
		if (xim->width < xim->height)
		    goto horient;
		else
		    goto vorient;
	    } else if (strcmp (strval, "Square") == 0) {
		for (i=0; (i*i) < nitems; i++)
		    ;
		nx = ny = i;
	    } else if (strcmp (strval, "Horizontal") == 0) {
horient:	if (nitems >= 13)
		    nx = ny = 4;
		else
		    sscanf (h_orient[nitems-1], "%dx%d", &nx, &ny);
	    } else if (strcmp (strval, "Vertical") == 0) {
vorient:	if (nitems >= 13)
		    nx = ny = 4;
		else
		    sscanf (v_orient[nitems-1], "%dx%d", &nx, &ny);
	    } else if (strcmp (strval, "Row") == 0) {
		nx = nitems;
		ny = 1;
	    } else if (strcmp (strval, "Column") == 0) {
		nx = 1;
		ny = nitems;
	    } else {
		sscanf (strval, "%dx%d", &nx, &ny);
	    }

	    /* Set or clear tile frame mode. */
	    xim->tileRows = ny;
	    xim->tileCols = nx;
	    xim->tileFramesList = frame_list;
	    sprintf (buf, "%d %d", nx, ny);
	    xim_message (xim, "tileOptions", buf);

	    xim_tileFrames (xim, xim->tileFramesList);
	}

	return (TCL_OK);
}


/* setColormap -- Set the colormap for the current display frame.
 *
 * Usage:	setColormap <colormap>
 *
 * The colormap may be specified either by number or by name.
 */
static int 
setColormap (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	unsigned short m_red[MAX_COLORS];
	unsigned short m_green[MAX_COLORS];
	unsigned short m_blue[MAX_COLORS];
	ColorMapPtr cm;
	int i;


	if (argc == 2) {
	    if (isdigit (*argv[1]))
		i = atoi(argv[1]);
	    else {
		for (i=1;  i <= ncolormaps;  i++)
		    if (strcmp (colormaps[i-1].name, argv[1]) == 0)
			break;
	    }

	    if (i >= 1 && i <= ncolormaps) {
		cm = &colormaps[i-1];
		if (strncmp (cm->name, "Random", 6) == 0) {
		    xim_setColormap (cm->name, NULL,
			m_red, m_green, m_blue, xim->ncolors);
		    GtWriteColormap (xim->gt, cm->mapno,
			first_color, xim->ncolors, m_red, m_green, m_blue);
		}

		fb->colormap = i;
	    	GtSetColormapFocus (-1);	/* force full update	*/
		GtLoadColormap (xim->gt, cm->mapno, fb->offset, fb->scale);
	    	GtSetColormapFocus (xim->cm_focus);
		xim_enhancement (xim, fb);
	    }
	}

	return (TCL_OK);
}


/* windowColormap -- Set the colormap for the current display frame.
 *
 * Usage:	windowColormap <offset> <scale>
 */
static int 
windowColormap (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	register ColorMapPtr cm;

	if (argc > 1) {
	    cm = &colormaps[fb->colormap-1];
	    fb->offset = atof(argv[1]);
	    fb->scale = (argc > 2) ? (float)atof(argv[2]) : fb->scale;
	    GtLoadColormap (xim->gt, cm->mapno, fb->offset, fb->scale);
	    xim_enhancement (xim, fb);
	}

	return (TCL_OK);
}


/* updateColormap -- Update the colormap for the entire display frame.
 *
 * Usage:	updateColormap <offset> <scale>
 */
static int 
updateColormap (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	register ColorMapPtr cm;

	if (argc > 1) {
	    cm = &colormaps[fb->colormap-1];
	    fb->offset = atof(argv[1]);
	    fb->scale = (argc > 2) ? (float)atof(argv[2]) : fb->scale;
	    GtSetColormapFocus (-1);	/* force full update	*/
	    GtLoadColormap (xim->gt, cm->mapno, fb->offset, fb->scale);
	    GtSetColormapFocus (xim->cm_focus);
	    xim_enhancement (xim, fb);
	}

	return (TCL_OK);
}


/* zoom -- Set the zoom factors for the current frame to the given values.
 * A zoom factor > 1 enlarges the image, < 1 shrinks the image, 1.0 maps
 * one source pixel to one destination pixel.
 *
 * Usage:	zoom <xymag>				1 argument
 *		zoom <xmag> <ymag>			2 arguments
 *		zoom <xmag> <ymag> <xcen> <ycen>	4 arguments
 *
 * When called as "zoom" the magnification is relative to the fixed scaling,
 * if any, used to scale the frame to fit the display window at mag=1.0.
 * When called as zoomAbs" the magnification given is the actual scale factor
 * used to map raster pixels to display pixels.
 */
static int 
zoom (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	float xmag, ymag;
	float xcen, ycen;
	float xoff, yoff;
	Boolean absolute;

	xmag = fb->xmag;
	ymag = fb->ymag;
	xcen = fb->xcen;
	ycen = fb->ycen;
	xoff = fb->xoff;
	yoff = fb->yoff;

	switch (argc) {
	case 7:
	    xoff = atof (argv[5]);
	    yoff = atof (argv[6]);
	    /* fall through */
	case 5:
	    xcen = atof (argv[3]);
	    ycen = atof (argv[4]);
	    /* fall through */
	case 3:
	    xmag = atof (argv[1]);
	    ymag = atof (argv[2]);
	    break;
	case 2:
	    xmag = ymag = atof (argv[1]);
	    break;
	}

	absolute = (strcmp (argv[0], "zoomAbs") == 0);
	xim_setZoom (xim, fb, fb->frameno, fb->zoommap,
	    fb->raster, fb->zoomras, xcen, ycen, xmag, ymag, xoff, yoff,
	    absolute);

	return (TCL_OK);
}


/* pan -- Pan the current frame, i.e., change the view center.
 *
 * Usage:	pan <xcen> <ycen>
 */
static int 
pan (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	Boolean absolute = False;
	float xmag, ymag;
	float xcen, ycen;
	float xoff, yoff;
	double atof();

	xmag = fb->xmag;
	ymag = fb->ymag;
	xoff = fb->xoff;
	yoff = fb->yoff;

	if (argc == 3) {
	    xcen = atof (argv[1]);
	    ycen = atof (argv[2]);

	    xim_setZoom (xim, fb, fb->frameno, fb->zoommap,
		fb->raster, fb->zoomras, xcen, ycen, xmag, ymag, xoff, yoff,
		absolute);
	}

	return (TCL_OK);
}


/* centroid -- Center the cursor on the feature given an initial position
 * and box size.  Return a correction to the center.
 *
 * Usage:	centroid <xcen> <ycen> <size> [ <type> ]
 */
static int 
centroid (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr  xim = xc->xim;
	register FrameBufPtr fb  = xim->df_p;
        register CtranPtr    ct  = (CtranPtr) &fb->ctran;
        unsigned char junk[MAX_COLORS];
	unsigned char *pix = NULL;
	float 	 *data=NULL, *xm=NULL, *ym=NULL;
	float    xsum, ysum, xsumx, ysumx;
	float    cx, cy, lo, hi, px, xcen, ycen, size;
	float    xmin=999999.0, xmax=-999999.0;
	int	 w, h, ncolors, i, j;
	int	 dist=0, mind=99999, maxd=99999, imin=0, imax=0;
	int	 x0, y0, nx, npix, min_max=-1;
 	char     buf[SZ_LINE];
	double   atof();


	if (argc < 4)
	    return (TCL_ERROR);

	xcen = atof (argv[1]);
	ycen = atof (argv[2]);
	size = atof (argv[3]);
	if (argc == 5)
	    min_max = strcmp (argv[4],"max");

	x0 = xcen - size;
	y0 = ycen - size;
	nx = size * 2 + 1;
	npix = nx * nx;

	/* Read the display raster. */
	pix = xim_readDisplay (xim, x0,y0,nx,nx, &w,&h, junk,junk,junk,
		 &ncolors);

	/* Scale the data to the WCS pixel values for centroiding. */
	data = (float *) XtMalloc (npix * sizeof(float));
	for (i=0; i < npix; i++) {
            if (pix[i] == 0) {
                data[i] = 0.0;
            } else {
                if (ct->zt == W_LINEAR) {
                    data[i] = ((pix[i]-1) * (ct->z2 - ct->z1) / 199) + ct->z1;
                    data[i] = max (ct->z1, min (ct->z2, data[i]));
                } else
                    data[i] = (float) pix[i];
            }
	    cx = (nx / 2) - (i % nx);
	    cy = (nx / 2) - (i / nx);
	    dist = (int) (sqrt (cx*cx + cy*cy) + 0.5);

	    if (data[i] > xmax)
		xmax = data[i], maxd = dist, imax = i;
	    else if (data[i] == xmax && dist < maxd)
		maxd = dist, imax = i;

	    if (data[i] < xmin)
		xmin = data[i], mind = dist, imin = i;
	    else if (data[i] == xmin && dist < mind)
		mind = dist, imin = i;
	}
	XtFree ((char *)pix);

	if (min_max >= 0) {
	    if (min_max == 0) {
	        if (data[npix/2] == xmax)
	            sprintf (buf, "0 0");
		else
	            sprintf (buf, "%g %g", (imax%nx)-size, (imax/nx)-size);
	    } else {
	        if (data[npix/2] == xmin)
	            sprintf (buf, "0 0");
		else
	            sprintf (buf, "%g %g", (imin%nx)-size, (imin/nx)-size);
	    }

	    /* Return the correction to the position. */
	    Tcl_SetResult (xc->server, buf, TCL_VOLATILE);
	    return (TCL_OK);
	}


	/* Find the low threshold for the subraster (i.e. the mean). */
	lo = hi = xsum = data[0];
	for (i=1; i < npix ; i++) {
	    xsum += data[i];
	    lo = (data[i] < lo ? data[i] : lo);
	    hi = (data[i] > hi ? data[i] : hi);
	}

	/* Check for a raster with all the same pixels, in which case
	 * just return a zero offset.
	 */
	if (lo == hi) {
	    sprintf (buf, "0 0");
	    Tcl_SetResult (xc->server, buf, TCL_VOLATILE);
	    return (TCL_OK);

	} else
	    lo   = xsum / (float)npix; 

	/* Accumulate the x and y marginals. */
	xm = (float *) XtMalloc (nx * sizeof(float));
	ym = (float *) XtMalloc (nx * sizeof(float));
	for (i=0; i < nx; i++) {
	    xsum = xm[i] = 0.0, ysum = ym[i] = 0.0;
	    for (j=0; j < nx; j++) {
		px = data[(j*nx)+i];			/* column sum 	*/
		if (lo <= px)
		    xsum += px - lo;

		px = data[(i*nx)+j];			/* row sum 	*/
		if (lo <= px)
		    ysum += px - lo;
	    }
	    xm[i] = xsum;
	    ym[i] = ysum;
	}
	XtFree ((char *)data);

	/* Now calculate the centroids as the first moment.  If all the 
	 * marginals are zero (i.e. all pixels the same) then return a
	 * zero correction. 
	 */
	xsum = xsumx = 0.0, ysum = ysumx = 0.0;
	px = (float) nx;
	for (i=0; i < nx; i++) {
            xsum  += (xm[i] / px);
            xsumx += (xm[i] / px) * i;
	
            ysum  += (ym[i] / px);
            ysumx += (ym[i] / px) * i;
	}
	cx = (xsum == 0.0) ? size : xsumx / xsum;
	cy = (ysum == 0.0) ? size : ysumx / ysum;

	XtFree ((char *)xm); 		/* clean up */
	XtFree ((char *)ym);

	/* Return the correction to the position. */
	sprintf (buf, "%d %d", nint(cx-size), nint(cy-size));
	Tcl_SetResult (xc->server, buf, TCL_VOLATILE);

	return (TCL_OK);
}


/* getPixels -- Get an array of pixels around the given center position.
 *
 * Usage:	getPixels <x0> <y0> <nx> <ny> [format [scale]]
 */

#define PF_NONE		0		/* don't format the output pixels */
#define PF_PIXTAB	1		/* format for pixel table 	  */
#define PF_HCUT		2		/* format for horizontal cut-plot */
#define PF_VCUT		3		/* format for vertical cut-plot   */

static int 
getPixels (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr  xim = xc->xim;
	register FrameBufPtr fb  = xim->df_p;
        register CtranPtr    ct  = (CtranPtr) &fb->ctran;
	register int	 i;
	unsigned char *pix = NULL;
	char 	 *buf = NULL, ch, val[32];
	float 	 *data = NULL;
	float	 z1 = ct->z1, z2 = ct->z2;
	float    scale=1.0, yscale = 128.0 / (z2 - z1);
	int	 x0, y0, nx, ny, format=0, npix, sample=1, raster=0;
	double   atof();


	if (argc < 5)
	    return (TCL_ERROR);

	x0 = atof (argv[1]);
	y0 = atof (argv[2]);
	nx = atof (argv[3]);
	ny = atof (argv[4]);
	if (argc >= 6) {
	    if (isdigit ((ch = *argv[5])))
		format = atoi (argv[5]);
	    else if (ch == 'T' || ch == 't')
		format = 1;
	    else
		format = 0;
	}
	if (argc >= 7) 
	    sample = (isdigit(*argv[6]) ? atoi (argv[6]) : 1);
	if (argc == 8)
	    scale = atof (argv[7]);
	
	npix = nx * ny;


	/* Read the display raster. */
	pix = (unsigned char *) XtMalloc (npix);
        if (GtReadPixels (xim->gt, raster, pix, 8, x0, y0, nx, ny) < 0)
	    return (TCL_ERROR);

	/* Scale the data to the WCS pixel values for display.  We don't
	 * get here if an ISM is running that provides access to the real
	 * pixel values.
	 */
	data = (float *) XtCalloc (npix, sizeof(float));
	for (i=0; i < npix; i+=sample) {
	    if (pix[i] == 0) {
	        data[i] = 0.0;
	    } else {
		if (ct->zt == W_LINEAR) {
	    	    data[i] = ((pix[i]-1) * (z2 - z1) / 199) + z1;
	    	    data[i] = max (z1, min (z2, data[i]));
		} else
	            data[i] = (float) pix[i];
	    }
	    if (format > 1) { data[i] = (z2 - data[i]) * yscale; }
	}
	XtFree ((char *)pix);

        /* Get a text buffer large enough to hold the encoded data. */
        if (!(buf = (char *) XtMalloc ((npix + 4) * 30))) {
            XtFree ((char *)data);
            return (TCL_ERROR);
        }

        /* Encode the data as {ddd} {ddd} {ddd}...{ddd}.  The first four
	 * elements are the zscale values and the array min/max.
	 */
	strcpy (buf, "");
	sprintf (val, "{%10.1f} ", z1);   strcat (buf, val);
	sprintf (val, "{%10.1f} ", z2);   strcat (buf, val);

	if (format == PF_PIXTAB) {
	    for (i=0; i < npix; i++) {
	    	sprintf (val, "{%10.1f%c} ", data[i],
	        	(data[i] <= z1 ? '-' : (data[i] >= z2 ? '+' : ' ')) );
		strcat (buf, val);
	    }
	} else if (format == PF_HCUT) {
	    for (i=0; i < npix; i+=sample) {
	    	sprintf (val, "{%g %g} ", i * scale, data[i]);
		strcat (buf, val);
	    }
	} else if (format == PF_VCUT) {
	    for (i=0; i < npix; i+=sample) {
	    	sprintf (val, "{%g %g} ", data[i], i * scale);
		strcat (buf, val);
	    }
	} else {
	    for (i=0; i < npix; i++) {
	    	sprintf (val, "{%f} ", data[i]);
	    	strcat (buf, val);
	    }
	}
            
        Tcl_SetResult (xc->server, buf, TCL_VOLATILE);
        XtFree ((char *)data);
        XtFree ((char *)buf);

	return (TCL_OK);
}


/* flip -- Flip the current display frame in the indicated axis or axes.
 *
 * Usage:	flip [axis [axis ...]]
 */
static int 
flip (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	int flip_x = 0, flip_y = 0;
	int ch, i;

	for (i=1;  i < argc;  i++) {
	    ch = argv[i][0];
	    if (ch == 'x' || ch == 'X')
		flip_x = !flip_x;
	    else if (ch == 'y' || ch == 'Y')
		flip_y = !flip_y;
	}

	xim_setFlip (xim, fb, flip_x, flip_y);
	return (TCL_OK);
}


/* retCursorVal -- Return a cursor value to the ximtool client process.  This
 * should be executed by the GUI to terminate a cursor read.
 *
 * Usage:	retCursorVal sx sy [frame [wcs [key [strval]]]]
 */
static int 
retCursorVal (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	int frame, wcs, key;
	float sx, sy;
	char *s, *strval;

	if (argc < 3)
	    return (TCL_ERROR);

	sx = atof (argv[1]);
	sy = atof (argv[2]);
	frame = (argc > 3) ? atoi (argv[3]) : xim->display_frame;
	wcs = (argc > 4) ? atoi (argv[4]) : 1;

	if (argc > 5) {
	    s = argv[5];
	    if (s[0] == '^')
		key = s[1] - 'A' + 1;
	    else
		key = s[0];
	} else
	    key = 0;

	strval = (argc > 6) ? argv[6] : "";

	xim_retCursorVal (xim, sx, sy, frame, wcs, key, strval);

	return (TCL_OK);
}


/* encodewcs -- Convert raw screen coordinates x,y,z (z=pixel value) to
 * world coordinates using the WCS passed to ximtool by the client application
 * when the frame was loaded.  The encoded description of the current position
 * and pixel value is returned to the GUI as a string value.
 *
 * Usage:	string = encodewcs sx sy sz
 */
static int 
encodewcs (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	char buf[SZ_LINE];
	float sx, sy;
	int sz;

	if (argc < 3)
	    return (TCL_ERROR);

	sx = atof (argv[1]);
	sy = atof (argv[2]);
	sz = (argc > 3) ? atoi (argv[3]) : 0;

	xim_encodewcs (xc->xim, sx, sy, sz, buf);
	Tcl_SetResult (xc->server, buf, TCL_VOLATILE);

	return (TCL_OK);
}


/* setPrintOption -- Set an ximtool client hardcopy option.
 *
 * Usage:	setPrintOption option value [args]
 *
 * Options:
 *	autoscale	true|false
 *	autorotate	true|false
 *	maxaspect	true|false
 *	annotate	true|false
 *	compress	true|false
 *
 * 	orientation	portrait|landscape
 *	papersize	letter|legal|A4|B5
 *	imscale		value
 *
 *	colortype	gray|pseudo|rgb
 *	printername	strval
 *	devicetype	printer|file
 *
 *	printcmd	command
 *	printfile	filename
 *
 *	dotitle		true|false
 *	doborders	true|false
 *	docolorbars	true|false
 *	title		string
 *
 *	corners		llx lly urx ury
 */
static int 
setPrintOption (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	register PSImagePtr psim = xim->psim;
	register PrintCfgPtr pcp = xim->pcp;
	register int i;
	int	 llx, lly, urx, ury;
	char *option, strval[SZ_LINE];
	char buf[SZ_LINE];
	int pnum, ch, value, psflags = psim->page.flags;

	if (argc < 3)
	    return (TCL_ERROR);
	else {
	    option = argv[1];
	    strcpy (strval, argv[2]);
	    for (i=3; i < argc; i++) {
		strcat (strval, " ");
		strcat (strval, argv[i]);
	    }

	    ch = strval[0];
	    if (isdigit (ch))
		value = atoi (strval);
	    else if (ch == 'T' || ch == 't')
		value = 1;
	    else if (ch == 'F' || ch == 'f')
		value = 0;
	}

	if (strcmp (option, "autoscale") == 0) { 		/* AUTOSCALE */
		psflags = value ? psflags | EPS_AUTOSCALE :
				  psflags & ~EPS_AUTOSCALE ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "autorotate") == 0) {	/* AUTOROTATE */
		psflags = value ? psflags | EPS_AUTOROTATE :
				  psflags & ~EPS_AUTOROTATE ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "maxaspect") == 0) {		/* MAXASPECT */
		psflags = value ? psflags | EPS_MAXASPECT :
				  psflags & ~EPS_MAXASPECT ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "dotitle") == 0) {		/* TITLE */
		psflags = value ? psflags | EPS_DOTITLE :
				  psflags & ~EPS_DOTITLE ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "doborders") == 0) {		/* BORDERS */
		psflags = value ? psflags | EPS_DOBORDERS :
				  psflags & ~EPS_DOBORDERS ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "docolorbar") == 0) {	/* COLORBAR */
		psflags = value ? psflags | EPS_DOCOLORBAR :
				  psflags & ~EPS_DOCOLORBAR ;
		psim->page.flags = psflags;
		sprintf (buf, "%s %s", option, value ? "True" : "False");
		xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "title") == 0) {      	/* TITLE STR */
	    if (strcmp ("imtitle", strval) != 0) {
	        strcpy (psim->label, strval);
                sprintf (buf, "title %s", strval);
                xim_message (xim, "printOptions", buf);
	    }

	} else if (strcmp (option, "annotate") == 0) {		/* ANNOTATE */
	    if (value) {
		if (!psim->label)
		    psim->label = (char *) calloc (SZ_LINE, sizeof (char));
        	sprintf (psim->label, "[Frame %d] %s", 
		    fb->frameno, fb->ctran.imtitle);
	 	psim->annotate = 1;
	    } else {
		if (psim->label) {
		    XtFree ((char *)psim->label);
		    psim->label = NULL;
		}
	 	psim->annotate = 0;
	    }
	    sprintf (buf, "%s %s", option, value ? "True" : "False");
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "compress") == 0) {	      /* COMPRESS    */
	    if (value)
		psim->compression = RLECompression;
	    else
		psim->compression = NoCompression;
	    sprintf (buf, "%s %s", option, value ? "True" : "False");
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "orientation") == 0) {     /* ORIENTATION */
	    if (ch == 'P' || ch == 'p')
		psim->page.orientation = EPS_PORTRAIT;
	    else if (ch == 'L' || ch == 'l')
		psim->page.orientation = EPS_LANDSCAPE;
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "papersize") == 0) {	       /* PAPER SIZE */
	    if (strval[2] == 'T' || strval[2] == 't')
		psim->page.page_type = EPS_LETTER;
	    else if (strval[2] == 'G' || strval[2] == 'g')
		psim->page.page_type = EPS_LEGAL;
	    else if (strval[0] == 'A' || strval[0] == 'a')
		psim->page.page_type = EPS_A4;
	    else if (strval[0] == 'B' || strval[0] == 'b')
		psim->page.page_type = EPS_B5;
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "imscale") == 0) {	       /* IMAGE SCALE */
	    if (value >= 10) {
	        if ((int)(psim->page.scale*100.0) != value)
	            psim->page.scale = (float) value / 100.0;
	        sprintf (buf, "%s %d", option, value);
	        xim_message (xim, "printOptions", buf);
	    }

	} else if (strcmp (option, "colortype") == 0) {		/* COLORTYPE */
	    if (ch == 'G' || ch == 'g')
		psim->colorClass = EPS_GRAYSCALE;
	    else if (ch == 'P' || ch == 'p')
		psim->colorClass = EPS_PSEUDOCOLOR;
	    else if (ch == 'R' || ch == 'r')
		psim->colorClass = EPS_TRUECOLOR;
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "devicetype") == 0) {       /* DEVICETYPE */

	    if (strval[0] == 'p' || strval[0] == 'P') {
		pcp->diskfile = 0;
	        sprintf (buf, "deviceType Printer");
	        xim_message (xim, "printOptions", buf);
        	sprintf (buf, "printCmd %s", pcp->printCmd);
        	xim_message (xim, "printOptions", buf);
        	sprintf (buf, "printerName %d", pcp->printno);
        	xim_message (xim, "printOptions", buf);

	    } else if (strval[0] == 'f' || strval[0] == 'F') {
		pcp->diskfile = 1;
	        sprintf (buf, "deviceType File");
	        xim_message (xim, "printOptions", buf);
        	sprintf (buf, "printFile %s", pcp->printFile);
        	xim_message (xim, "printOptions", buf);
	    }

	} else if (strcmp (option, "printername") == 0) {    /* PRINTER NAME */
	    /* Set to printer mode if called in file mode. */
	    if (pcp->diskfile) {
		pcp->diskfile = 0;
	        sprintf (buf, "deviceType Printer");
	        xim_message (xim, "printOptions", buf);
	    }
	    pnum = xim_getPrinterInfo (xim, strval);
	    sprintf (buf, "printerName %s", strval);
	    xim_message (xim, "printOptions", buf);
	    strcpy (pcp->printCmd, printer_list[pnum].printCmd);
	    sprintf (buf, "printCmd %s", pcp->printCmd);
	    xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "printcmd") == 0) {      /* PRINT COMMAND */
	    strcpy (pcp->printCmd, strval);
            sprintf (buf, "printCmd %s", strval);
            xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "printfile") == 0) {    /* PRINT FILENAME */
	    strcpy (pcp->printFile, strval);
            sprintf (buf, "printFile %s", strval);
            xim_message (xim, "printOptions", buf);

	} else if (strcmp (option, "corners") == 0) {      /* IMAGE CORNERS */
	    /* Set the corners of the image being printed. */
	    sscanf (strval, "%d %d %d %d", &llx, &lly, &urx, &ury);
	    eps_setCorners (psim, llx, lly, urx, ury);
	}

        /* Reload the page parameters in case anything's changed. */
        eps_setPage (psim, psim->page.orientation, psim->page.page_type,
            (int)(psim->page.scale*100), psim->page.flags);

	return (TCL_OK);
}


/* setSaveOption -- Set an ximtool client disk file option.
 *
 * Usage:       setSaveOption option value [args]
 *
 * Options:
 *
 *	format 	fmt
 *	color	gray|pseudo|rgb
 *	fname	strval
 */
static int
setSaveOption (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
	register fileSavePtr fsp = xim->fsp;
	register int i;
        char *option, strval[SZ_LINE];
        char buf[SZ_LINE];
        int ch;

        if (argc < 3)
            return (TCL_ERROR);
        else {
            option = argv[1];
	    strcpy (strval, argv[2]);
	    for (i=3; i < argc; i++) {
		strcat (strval, " ");
		strcat (strval, argv[i]);
	    }
        }

	if (strcmp (option, "format") == 0) {	       		/* FORMAT */
	    switch (strval[0]) {
	    case 'r':
		if (strval[2] == 's') {
		    fsp->format = XIM_RAS;
		    strcpy (fsp->fname, "frame%d.ras");
		} else if (strval[2] == 'w') {
		    fsp->format = XIM_RAW;
		    strcpy (fsp->fname, "frame%d.raw");
		}
		break;
	    case 'g': 	
		fsp->format = XIM_GIF; 	
		strcpy (fsp->fname, "frame%d.gif");
		break;
	    case 'j': 	
		fsp->format = XIM_JPEG; 
		strcpy (fsp->fname, "frame%d.jpg");
		break;
	    case 't': 	
		fsp->format = XIM_TIFF; 
		strcpy (fsp->fname, "frame%d.tiff");
		break;
	    case 'f': 	
		fsp->format = XIM_FITS; 
		strcpy (fsp->fname, "frame%d.fits");
		break;
	    case 'e': 	
		fsp->format = XIM_EPS; 	
		strcpy (fsp->fname, "frame%d.eps");
		break;
	    case 'x': 	
		fsp->format = XIM_X11; 	
		strcpy (fsp->fname, "frame%d.xwd");
		break;
	    }
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "saveOptions", buf);
            sprintf (buf, "fname %s", fsp->fname);
            xim_message (xim, "saveOptions", buf);

	} else if (strcmp (option, "color") == 0) {	      	/* COLOR */
            if (ch == 'G' || ch == 'g')
                fsp->colorType = XIM_GRAYSCALE;
            else if (ch == 'P' || ch == 'p')
                fsp->colorType = XIM_PSEUDOCOLOR;
            else if (ch == 'R' || ch == 'r')
                fsp->colorType = XIM_RGB;
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "saveOptions", buf);

	} else if (strcmp (option, "fname") == 0) {	       	/* FILENAME */
	    strcpy (fsp->fname, strval);
	    sprintf (buf, "%s %s", option, strval);
	    xim_message (xim, "saveOptions", buf);
	}

	return (TCL_OK);
}


/* setLoadOption -- Set an ximtool client disk file option.
 *
 * Usage:       setLoadOption option value [args]
 *
 * Options:
 * 	up
 *	root
 *	home
 * 	rescan
 * 	headers
 *	pattern	patstr
 *	gray    0|1
 *	zscale  0|1
 *	zrange  0|1
 *	z1 value
 *	z2 value
 *	nsample value
 *
 */
static int
setLoadOption (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register int i;
        register XimDataPtr xim = xc->xim;
	register fileLoadPtr flp = xim->flp;
        char *option, *strval;
        char buf[SZ_LINE];

        if (argc < 2)
            return (TCL_ERROR);
        else {
            option = argv[1];
            strval = (argc == 3 ? argv[2] : "" );
        }

	if (strcmp (option, "up") == 0) {	       		/* UP      */
	    if (strcmp("/", flp->curdir) != 0) {
		for (i=strlen(flp->curdir); i > 1; i--) {
		    if (flp->curdir[i] == '/')
			break;
		}
		flp->curdir[i] = '\0';
	        sprintf (buf, "curdir %s", flp->curdir);
	        xim_message (xim, "loadOptions", buf);
	        xim_dirRescan (xim);
	    }

	} else if (strcmp (option, "root") == 0) {	      	/* ROOT    */
	    strcpy (flp->curdir, "/");
	    sprintf (buf, "curdir %s", flp->curdir);
	    xim_message (xim, "loadOptions", buf);
	    xim_dirRescan (xim);

	} else if (strcmp (option, "home") == 0) {	      	/* HOME    */
	    strcpy (flp->curdir, flp->homedir);
	    sprintf (buf, "curdir %s", flp->curdir);
	    xim_message (xim, "loadOptions", buf);
	    xim_dirRescan (xim);

	} else if (strcmp (option, "pattern") == 0) {	       	/* PATTERN */
	    if (strcmp(strval, flp->pattern) != 0) {
	        strcpy (flp->pattern, strval);
	        sprintf (buf, "pattern %s", flp->pattern);
	        xim_message (xim, "loadOptions", buf);
	        xim_dirRescan (xim);
	    }

	} else if (strcmp (option, "rescan") == 0) {	       	/* RESCAN  */
	    xim_dirRescan (xim);

	} else if (strcmp (option, "headers") == 0) {	       	/* HEADERS */
	    xim_scanHeaders (xim);

	} else if (strcmp (option, "gray") == 0) {	      	/* GRAY    */
	    flp->gray = (strval[0] == '0' ? 0 : 1);
	    sprintf (buf, "gray %s", strval[0] == '0' ? "off" : "on");
	    xim_message (xim, "loadOptions", buf);

	} else if (strcmp (option, "zscale") == 0) {	      	/* ZSCALE  */
	    flp->zscale = (strval[0] == '0' ? 0 : 1);
	    sprintf (buf, "zscale %s", strval[0] == '0' ? "off" : "on");
	    xim_message (xim, "loadOptions", buf);

	} else if (strcmp (option, "zrange") == 0) {	      	/* ZRANGE  */
	    flp->zrange = (strval[0] == '0' ? 0 : 1);
	    sprintf (buf, "zrange %s", strval[0] == '0' ? "off" : "on");
	    xim_message (xim, "loadOptions", buf);

	} else if (strcmp (option, "z1") == 0) {	      	/* Z1      */
	    sscanf (argv[2], "%g", &(flp->z1));
	    sprintf (buf, "z1 %s", argv[2]);
	    xim_message (xim, "loadOptions", buf);

	} else if (strcmp (option, "z2") == 0) {	      	/* Z2      */
	    sscanf (argv[2], "%g", &(flp->z2));
	    sprintf (buf, "z2 %s", argv[2]);
	    xim_message (xim, "loadOptions", buf);

	} else if (strcmp (option, "nsample") == 0) {	      	/* NSAMPLE */
	    sscanf (argv[2], "%d", &(flp->nsample));
	    sprintf (buf, "nsample %s", argv[2]);
	    xim_message (xim, "loadOptions", buf);
	}

	return (TCL_OK);
}


/* Print -- Print the current display frame to a printer or to a file (EPS).
 *
 * Usage:       print [x0 y0 nx ny]
 *
 *		print rename old new
 *		print cancel fname
 *
 * If a subregion is given the indicated region is printed, otherwise the
 * full display frame is printed.
 *
 * The forms "print rename" and "print cancel" are actions for print alerts.
 */
static int
print (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
        int x0, y0, nx, ny;

        /* Handle the special cases first. */
        if (argc == 4 && strcmp (argv[1], "rename") == 0) {
	    ximp_rename (xim, argv[2], argv[3]);
	    return (TCL_OK);
        } else if (argc == 3 && strcmp (argv[1], "cancel") == 0) {
	    ximp_cancel (xim, argv[2]);
	    return (TCL_OK);
        }

	/* Normal case of a print. */
	if (argc == 5) {
	    x0 = atoi (argv[1]);
	    y0 = atoi (argv[2]);
	    nx = atoi (argv[3]);
	    ny = atoi (argv[4]);
	} else
	    x0 = y0 = nx = ny = 0;

	if (xim_print (xim, x0,y0, nx,ny) < 0)
	    return (TCL_ERROR);
	else
	    return (TCL_OK);
}


/* Save -- Save the current display frame to a disk file.
 *
 * Usage:       save [x0 y0 nx ny]
 *
 *		save rename old new
 *		save cancel fname
 *
 * If a subregion is given the indicated region is saved, otherwise the
 * full display frame is saved.
 *
 * The forms "save rename" and "save cancel" are actions for save alerts.
 */
static int
save (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
	register fileSavePtr fsp = xim->fsp;
        int x0, y0, nx, ny;

        /* Handle the special cases first. */
        if (argc == 4 && strcmp (argv[1], "rename") == 0) {
	    xims_rename (xim, argv[2], argv[3]);
	    return (TCL_OK);
        } else if (argc == 3 && strcmp (argv[1], "cancel") == 0) {
	    xims_cancel (xim, argv[2]);
	    return (TCL_OK);
        }

	if (argc == 5) {
	    x0 = atoi (argv[1]);
	    y0 = atoi (argv[2]);
	    nx = atoi (argv[3]);
	    ny = atoi (argv[4]);
	} else
	    x0 = y0 = nx = ny = 0;

	/* Pass off to the file save routines. */
	if (xim_saveFile (xim, fsp->fname, fsp->format, x0,y0, nx,ny) < 0)
            return (TCL_ERROR);
	else
            return (TCL_OK);
}


/* Load -- Load a frame from a disk file.
 *
 * Usage:       load filename [frame]
 *
 * Options:	frame			display frame to be loaded
 */
static int
load (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
	register fileLoadPtr flp = xim->flp;
        char *fname;
        char buf[SZ_LINE];
	struct stat file_info;
	int frame;

        if (argc < 2)
            return (TCL_ERROR);

	fname = argv[1];
	frame = (argc >= 3) ? atoi(argv[2]) : xim->display_frame;

        /* If given a directory change the directory browser current reference
	 * directory and get a listing.
	 */
        if (fname[strlen(fname)-1] == '/') {
            fname[strlen(fname)-1] = '\0';
	    if (fname[0] == '/') {
                sprintf (flp->curdir, "%s", fname);
	    } else {
                strcat (flp->curdir, "/");
                strcat (flp->curdir, fname);
	    }
            sprintf (buf, "curdir %s", flp->curdir);
            xim_message (xim, "loadOptions", buf);

            xim_dirRescan (xim);

        } else {
            /* Otherwise it may be some kind of image to be loaded, but first
	     * check to see if it's not some other directory first.
	     */
	    (void) stat (fname, &file_info);
	    if (S_ISDIR(file_info.st_mode)) {
                sprintf (flp->curdir, "%s", fname);
                sprintf (buf, "curdir %s", flp->curdir);
                xim_message (xim, "loadOptions", buf);
                xim_dirRescan (xim);
		return (TCL_OK);
	    }

	    /* It's not a directory, so try loading the file. */
	    if (xim_loadFile (xim, fname, frame) != 0)
                return (TCL_ERROR);
        }

	return (TCL_OK);
}


/* Help -- Send the default help text (HTML) to the GUI.
 *
 * Usage:       help
 */

/* The builtin default help text. */
static char *help_text[] = {
    "setValue {",
#ifndef OSI_COMPLIANT
#   include "ximtool.html.h"
#endif
    "}",
    NULL
};

static int
help (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
        register char *ip, *op, *helptxt;
        register int i;

        helptxt = (char *) XtMalloc (1024000);
        for (i=0, op=helptxt;  (ip = help_text[i]);  i++) {
            while (*ip)
                *op++ = *ip++;
            *op++ = '\n';
        }
        *op++ = '\0';

        ObmDeliverMsg (xim->obm, "help", helptxt);
        XtFree ((char *)helptxt);

	return (TCL_OK);
}


/* Info -- Send various kinds of information to the GUI.  The 'args' option
 * allows us to pass in information from the GUI that cannot be easily
 * obtained otherwise, e.g. private information inthe Gterm widget such as
 * the basePixel resource.
 *
 * Usage:       info  option [ args ... ] 
 */
int
info (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
        register XimDataPtr xim = xc->xim;
        char *option, *message;

        if (argc < 2)
            return (TCL_ERROR);
        else
            option = argv[1];

        message = (char *) XtCalloc (8182, sizeof(char));

	if (strcmp (option, "server") == 0) {
	    info_server (xim, argc, argv, message);

	} else if (strcmp (option, "wcs") == 0) {
	    info_wcs (xim, message);

	} else if (strcmp (option, "clients") == 0) {
	    info_clients (xim, message);

	} else if (strcmp (option, "imtoolrc") == 0) {
	    info_imtoolrc (xim, message);

        } else {
            XtFree ((char *)message);
            return (TCL_ERROR);
	}

	strcat (message, "\n\0");

	if (*message)
            xim_message (xim, "info", message);
        XtFree ((char *)message);

	return (TCL_OK);
}


/* INFO_SERVER -- Helper routine to report server state information.
 */
static void
info_server (xim, argc, argv, text)
register XimDataPtr xim;
int	argc;
char	**argv;
char	*text;
{
	extern char *ximtool_version[];
	extern int ncolormaps, first_color;
	char cmapname[80], line[SZ_LINE];
        ColorMapPtr cm;

	sprintf (text, "\t%s\n\n", ximtool_version[0]);

	sprintf (line, "%20s:  %s\n", "Base Pixel",
		(argc >= 3 ? argv[2] :""));
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Max Colors",
		(argc >= 4 ? argv[3] :""));
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Memory Model", xim->memModel);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Antialias Type", xim->antialiasType);
	strcat (text, line);
	strcat (text, "\n");

        cm = &colormaps[DEF_COLORMAP-1];
        strcpy (cmapname, cm->name);
	sprintf (line, "%20s:  %s\n", "Current Colormap", cmapname);
	strcat (text, line);
	sprintf (line, "%20s:  %d\n", "Colormaps Available", ncolormaps);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "User Cmap 1", xim->userCMap1);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "User Cmap 2", xim->userCMap1);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Cmap Dir 1", xim->userCMapDir1);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Cmap Dir 2", xim->userCMapDir2);
	strcat (text, line);
	strcat (text, "\n");
	sprintf (line, "%20s:  %s\n", "Printer Config", xim->printConfig);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Printer File", xim->pcp->printFile);
	strcat (text, line);
	sprintf (line, "%20s:  %s\n", "Printer Cmd", xim->pcp->printCmd);
	strcat (text, line);
}


/* INFO_CLIENTS -- Helper routine to report client (display or ISM) state.
 */
void
info_clients (xim, text)
register XimDataPtr xim;
char	*text;
{
        register IsmModule ism;
	register int i;
        char line[SZ_LINE], path[80];
	extern ismModule ism_modules[];
	extern int ism_nmodules;

	strcpy (text, "\t\tClient Communications Channels\n\n");

	strcat (text, "  Display Client Connections\t    ");
	strcat (text, "ISM Client Connections\n");
	strcat (text, "  --------------------------\t    ");
	strcat (text, "----------------------\n");

	if (xim->port)
	    sprintf (line, "  inet:  %d\t\t\t    ", xim->port);
	else
	    sprintf (line, "  inet:  Disabled\t\t");
	strcat (text, line);

	sprintf (path, xim->ism_addr, getuid());
	sprintf (line, "unix:  %s\n", path);
	strcat (text, line);

	if (strcmp(xim->unixaddr, "none") != 0) {
	    sprintf (path, xim->unixaddr, getuid());
	    sprintf (line, "  unix:  %s\n", path);
	} else
	    sprintf (line, "  unix:  Disabled\n");
	strcat (text, line);

	if (strcmp(xim->input_fifo,"") != 0 && 
	    strcmp(xim->input_fifo,"none") != 0)
	        sprintf (line, "  fifo:  %s\n\t %s\n\n",
		    xim->input_fifo, xim->output_fifo);
	else
	    sprintf (line, "  fifo:  Disabled\n\n");
	strcat (text, line);

	strcat (text, "\n");
	strcat (text, "\t\t   Available ISM Components\n\n");
	strcat (text, "  Name      Channel         Command\n");
	strcat (text, "  ----      -------         -------\n");
        for (i=0; i < ism_nmodules; i++) {
            ism = &ism_modules[i];
	    sprintf (line, "  %-9.9s %-15.15s '%s'\n", 
		    ism->name,
		    (ism->connected ? ism->chan->path : "Disabled"),
		    ism->command);
	    strcat (text, line);
        }
}


/* INFO_WCS -- Helper routine to report WCS and mapping information for 
 * each frame in the display.
 */
void
info_wcs (xim, text)
register XimDataPtr xim;
char	*text;
{
        register FrameBufPtr fr = xim->df_p;
        register CtranPtr ct = &fr->ctran;
	register int i;
        MappingPtr mp = (MappingPtr) NULL;
	char	 line[SZ_LINE];

	/* Write the current frame WCS. */
	sprintf (line,
	    "\t\tFrame %d  WCS & Mappings\n\t\t------------------------\n\n",
		fr->frameno);
	strcat (text, line);

	strcat (text, "Frame WCS:\n");
	sprintf (line, "    a = %9.3f\t b = %9.3f %s\n",
	    ct->a, ct->b, "# Scale factors");
	strcat (text, line);

	sprintf (line, "    c = %9.3f\t d = %9.3f %s\n",
	    ct->c, ct->d, "# Cross factors");
	strcat (text, line);

	sprintf (line, "   tx = %9.3f\tty = %9.3f %s\n",
	    ct->tx, ct->ty, "# Translation");
	strcat (text, line);
	sprintf (line, "   z1 = %9.3f\tz2 = %9.3f %s\n",
	    ct->z1, ct->z2, "# z-scale range");
	strcat (text, line);

	sprintf (line, "   zt = %9s\t%30s\n",
	    (ct->zt == W_UNITARY ? "unitary" : 
	    (ct->zt == W_LINEAR  ? "linear" : 
	    (ct->zt == W_LOG     ? "log" : "unknown"))),
	    "# z-scale type\n");
	strcat (text, line);
		

	fr = (FrameBufPtr) NULL; 
	for (i=0; i < xim->nframes; i++) {
	    fr = &xim->frames[i];
	    if (xim->display_frame == fr->frameno)
		break;
	}

	if (!fr) {
	    strcat (text, " \n \n");
	    return;
	}

	for (i=0; i < fr->nmaps; i++) {
	    mp = &(fr->mapping[i]);
            ct = &(mp->ctran);

	    sprintf (line, "\nMapping %d: \n", mp->id);
	    strcat (text, line);

	    sprintf (line, "    a = %7.3f    b = %7.3f\n", ct->a, ct->b);
	    strcat (text, line);
	    sprintf (line, "    c = %7.3f    d = %7.3f\n", ct->c, ct->d);
	    strcat (text, line);
	    sprintf (line, "   tx = %7.3f   ty = %7.3f\n", ct->tx, ct->ty);
	    strcat (text, line);
	    sprintf (line, "   z1 = %7.3f   z2 = %7.3f\tzt: %s\n",
		ct->z1, ct->z2,
	        (ct->zt == W_UNITARY ? "unitary" : 
	        (ct->zt == W_LINEAR  ? "linear" : 
	        (ct->zt == W_LOG     ? "log" : "unknown"))) );
	    strcat (text, line);

	    sprintf (line, "   region %d: %s\n", mp->regid, mp->region);
	    strcat (text, line);
	    sprintf (line, "      src: x=%9f  y=%9f  nx=%d ny=%d\n",
		mp->sx, mp->sy, mp->snx, mp->sny);
	    strcat (text, line);
	    sprintf (line, "     dest: x=%9d  y=%9d  nx=%d ny=%d\n",
		mp->dx, mp->dy, mp->dnx, mp->dny);

	    strcat (text, line);
	    sprintf (line, "      ref: %s\n", mp->ref);
	    strcat (text, line);
	}
	strcat (text, " \n \n");
}


/* INFO_IMTOOLRC -- Helper routine to report the frame buffer configuration
 * table.
 */
void
info_imtoolrc (xim, text)
register XimDataPtr xim;
char	*text;
{
	register int last_fb_used = MAX_FBCONFIG;
	register int i, w, h, nf, fb_config = xim->fb_configno;
	char	 line[SZ_LINE];

	strcpy (text, "    Frame Buffer Configuration Table\n");
	strcat (text, "    --------------------------------\n\n");

	sprintf (line, "  Imtoolrc File:  %s\n", xim->imtoolrc);
	strcat (text, line);
	strcat (text, "\n  Config      NFrames\tWidth\tHeight\n");
	strcat (text, "  ------      -------\t-----\t------\n");

	/* Find the index of the last FB defined. */
	for (i=MAX_FBCONFIG; i > 1; i--)
	    if (xim->fb_config[i-1].width != DEF_FRAME_WIDTH || 
	        xim->fb_config[i-1].height != DEF_FRAME_HEIGHT) {
	            last_fb_used = i;
	            break;
	    }

	/* Print out the frame buffer configurations. */
	for (i=1; i <= last_fb_used; i++) {
	    w = xim->fb_config[i-1].width;
	    h = xim->fb_config[i-1].height;
	    nf = xim->fb_config[i-1].nframes;
	    if (i > 1 && (w == DEF_FRAME_WIDTH && h == DEF_FRAME_HEIGHT)) {
	        sprintf (line, "  %4d\t\t 0\t  n/a\t  n/a\n", i);
	    } else {
	        sprintf (line, "  %4d\t\t%2d\t%5d\t%5d\t  %s\n",
	            i, nf, w, h, ((i==fb_config) ? "<--- current" : " "));
	    }
	    strcat (text, line);
	}
	strcat (text, " \n \n");
}


/* windowRGB -- Window an individual component of an RGB colormap.  We start
 * with the currently defined cmap and scale it's component by the given
 * offset and slope.  A 'save' flag is set when the button is released meaning
 * the user is done with that component and the loaded colormap is updated,
 * otherwise continue changing that color from the previous call allowing us
 * to window the color progressively.  The GUI's 'initialize' option should
 * restore the original colormap.  [This is still test code.]
 *
 * Usage:	windowRGB <color> <offset> <scale> <save>
 *
 * Options:	color		color to manipulate (1=Red,2=Green,3=Blue)
 *		offset		offset of transformation
 *		scale		slope of transformation
 *		save		save to loaded colormap when complete?
 */

static int 
windowRGB (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register FrameBufPtr fb = xim->df_p;
	register ColorMapPtr cm;
	int color, first, nelem, maxelem, save = 0;
	unsigned short r[MAX_COLORS];
	unsigned short g[MAX_COLORS];
	unsigned short b[MAX_COLORS];


	if (argc > 1) {
	    cm = &colormaps[fb->colormap-1];
	    color = atoi(argv[1]);
	    fb->offset = atof(argv[2]);
	    fb->scale = (argc > 2) ? (float)atof(argv[3]) : fb->scale;
	    save = atoi(argv[4]);

	    /* Query and read the current colormap. */
	    GtQueryColormap (xim->gt, cm->mapno, &first, &nelem, &maxelem);
	    GtReadColormap (xim->gt, cm->mapno, first, nelem, r,g,b);

	    /* compute the scaled colormap, scaling only the color we're
	     * interested in.
	     */
	    switch (color) {
	    case 1:
		cmapScale (r, nelem, first, fb->offset, fb->scale);
		break;
	    case 2:
		cmapScale (g, nelem, first, fb->offset, fb->scale);
		break;
	    case 3:
		cmapScale (b, nelem, first, fb->offset, fb->scale);
		break;
	    }

	    /* Lastly, write it back to the widget. */
	    GtWriteColormap (xim->gt, 0, first, nelem, r, g, b);
	    if (save)
	        GtWriteColormap (xim->gt, cm->mapno, first, nelem, r, g, b);
	}

	return (TCL_OK);
}


/* cmapScale -- Given a single-color cmap scale it with the given offset and
 * slope, the scaling is done in place.
 */
void
cmapScale (map, ncells, first, offset, slope)
unsigned short map[MAX_COLORS];
int ncells, first;
float offset, slope;
{
	register int i, c1, c2;
	register float x, y, z, frac;
	unsigned short val, out[MAX_COLORS];

        for (i=0;  i < ncells;  i++) {
            x = (float)i / (float)(ncells - 1);
            y = (x - offset) * slope + 0.5;

            if (y <= 0.0) {
                val = map[first];
            } else if (y >= 1.0) {
                val = map[ncells-1];
            } else {
                z = y * (ncells - 1);
                c1 = (int)z;
                c2 = min (ncells-1, c1 + 1);
                frac = z - c1;
                val = map[c1] * (1.0 - frac) + map[c2] * frac;
            }

            out[i] = val;
        }  

	for (i=0; i < MAX_COLORS; i++)
	    map[i] = out[i];
}



/* ISM_START -- Start the ISM task.  The named task must be listed in
 * the array of ISM modules.
 *
 * Usage:       ism_start  task
 */
static int
ism_start (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register IsmModule ism;

        if (argc < 2)
            return (TCL_ERROR);

	/* Lookup the command for the task and start it.
	 */
	if ((ism = ismNameToPtr (argv[1]))) {
            system (ism->command);
            return (TCL_OK);
	}

	/* Task not found, return an error. */
        return (TCL_ERROR);
}


/* ISM_STOP -- Stop the ISM task.  The named task is told to shut itself
 * down by executing the registered shutdown callback.  We return OK if
 * the shutdown can be executed, an ERR is returned if the named task is
 * not currently running.
 *
 * Usage:       ism_stop  task
 */
static int
ism_stop (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register IsmModule ism;

        if (argc < 2)
            return (TCL_ERROR);

	/* Lookup the command for the task and stop it.  */
	if ((ism = ismNameToPtr (argv[1]))) {
            (*ism->shutdownCB) (xim, ism);
            ism->connected = 0;
            return (TCL_OK);
 	}

	/* Task not found, return an error. */
        return (TCL_ERROR);
}


/* ISM_CMD -- Send a command to the named ISM command callback.  Return
 * values are sent as messages to the GUI 'ism_msg' parameter object, we
 * simply pass the argv to the appropriate function.
 *
 * Usage:       ism_cmd  task <args>
 */
static int
ism_cmd (xc, tcl, argc, argv)
register XimClientPtr xc;
Tcl_Interp *tcl;
int argc;
char **argv;
{
	register XimDataPtr xim = xc->xim;
	register IsmModule ism;
	char 	 **cmd_argv;
	int  	 cmd_argc;

        if (argc < 2)
            return (TCL_ERROR);

	/* Lookup the command callback for the task and run it.  */
	if ((ism = ismNameToPtr (argv[1]))) {

	    /* Get local copy of argc and argv containing only the commands
	     * for the ISM callback.
	     */
            if ((cmd_argc = (argc - 2) > 0)) {
                cmd_argv = (char **) XtMalloc (cmd_argc * sizeof(char *));
                memmove (cmd_argv, &argv[2], cmd_argc * sizeof(char *)); 
            }

            /* Process the command. */
	    (*ism->commandCB) (xim, ism, cmd_argc, cmd_argv);
            return (TCL_OK);
	}

	/* Task not found, return an error. */
        return (TCL_ERROR);
}
