
/* Copyright (C) 1992  AHPCRC, Univeristy of Minnesota
 * Earlier versions Copyright 1992,  Minnesota Supercomputer Center Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program in a file named 'Copying'; if not, write to
 * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139.
 */

/* Author:
 *	Ken Chin-Purcell (ken@ahpcrc.umn.edu)
 *	Army High Performance Computing Research Center (AHPCRC)
 *	Univeristy of Minnesota
 *
 * $Header: /usr/people/ken/gvl/bob/RCS/setup.c,v 2.2 92/10/27 16:50:29 ken Exp Locker: ken $
 *
 * $Log:	setup.c,v $
 * Revision 2.2  92/10/27  16:50:29  ken
 * Added basic point support
 * 
 * Revision 2.1  92/10/19  17:03:38  ken
 * *** empty log message ***
 * 
 * Revision 1.1  92/10/19  17:03:02  ken
 * Initial revision
 * 
 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <Xm/Xm.h>
#include <Xm/ArrowB.h>
#include <Xm/ArrowBG.h>
#include <Xm/BulletinB.h>
#include <Xm/CascadeB.h>
#include <Xm/CascadeBG.h>
#include <Xm/DialogS.h>
#include <Xm/DrawingA.h>
#include <Xm/DrawnB.h>
#include <Xm/FileSB.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/Label.h>
#include <Xm/LabelG.h>
#include <Xm/List.h>
#include <Xm/MessageB.h>
#include <Xm/PanedW.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/RowColumn.h>
#include <Xm/Scale.h>
#include <Xm/ScrollBar.h>
#include <Xm/ScrolledW.h>
#include <Xm/SeparatoG.h>
#include <Xm/Separator.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>

#include <gl/gl.h>
#include <X11/Xirisw/GlxMDraw.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>

/* I don't normally use these widgets
 #include <Xm/Command.h>
 #include <Xm/MainW.h>
 #include <Xm/MenuShell.h>
 #include <Xm/SelectioB.h>
 */

#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <signal.h>
#include <ulocks.h>

#include "util.h"
#include "skip.h"
#include "xtutil.h"
#include "glutil.h"
#include "draw.h"
#include "vox.h"
#include "bob.h"
#include "setup.h"

#define	DLINELEN	256


#define BobOff(member)	XtOffsetOf(struct _BobApp, member)

static XtResource resources[] = {
    { "help",		"Help",		XtRInt, sizeof(int),
	  BobOff(help), XtRImmediate,		(XtPointer) 0 },
    { "debug",		"Debug",	XtRInt, sizeof(int),
	  BobOff(debug), XtRImmediate, 		(XtPointer) 0 },
    { "cmapName",	"CmapName",	XtRString, sizeof(String),
	  BobOff(cmapName), XtRImmediate, 	(XtPointer) 0 },
    { "amapName",	"AmapName",	XtRString, sizeof(String),
	  BobOff(amapName), XtRImmediate,	(XtPointer) 0 },
    { "defaultName",	"DefaultName",	XtRString, sizeof(String),
	  BobOff(defName), XtRImmediate,	(XtPointer) 0 },
    { "followName",	"FollowName",	XtRString, sizeof(String),
	  BobOff(followName), XtRImmediate,	(XtPointer) 0 },
    { "scaleText",	"ScaleText",	XtRString, sizeof(String),
	  BobOff(scaleText), XtRImmediate,	(XtPointer) 0 },
    { "saveBGR",	"SaveBGR",	XtRBoolean, sizeof(Boolean),
	  BobOff(anBGR), XtRImmediate,		(XtPointer) 1 },
    { "fastRate",	"FastRate",	XtRInt, sizeof(int),
	  BobOff(fastRate), XtRImmediate,	(XtPointer) 6 },
    { "singleBuffer",	"SingleBuffer",	XtRBoolean, sizeof(Boolean),
	  BobOff(singleBuffer), XtRImmediate,	(XtPointer) 0 },
    { "bugAddress", "BugAddress",	XtRString, sizeof(String),
	BobOff(bugAddress), XtRImmediate, (XtPointer) "gvlware@ahpcrc.umn.edu"},
    { "fieldOfView", "FieldOfView",	XtRInt, sizeof(int),
	  BobOff(fieldOfView), XtRImmediate,	(XtPointer) 340 },
    { "eyeSeparation", "EyeSeparation",	XtRFloat, sizeof(float),
	  BobOff(eyeSep), XtRString,	(XtPointer) "0.07" },
    { "eyeDistance", "EyeDistance",	XtRFloat, sizeof(float),
	  BobOff(eyeDist), XtRString,	(XtPointer) "3.00" },
    { "poffset",	"Poffset",	XtRInt, sizeof(int),
	  BobOff(poffset), XtRImmediate, (XtPointer) 0 },
    { "pstride",	"Pstride",	XtRInt, sizeof(int),
	  BobOff(pstride), XtRImmediate, (XtPointer) 12 },
    { "psize",		"Psize",	XtRInt, sizeof(int),
	  BobOff(psize), XtRImmediate, (XtPointer) 1 },
    { "pnumber",	"Pnumber",	XtRInt, sizeof(int),
	  BobOff(nallpoint), XtRImmediate, (XtPointer) 0 },
    { "pcolor",		"Pcolor",	XtRString, sizeof(String),
	  BobOff(pcolorName), XtRImmediate, (XtPointer) "yellow" },
    { "voxbg",		"Voxbg",	XtRString, sizeof(String),
	  BobOff(bgColorName), XtRImmediate, (XtPointer) "black" },
    { "fogDensity",	"FogDensity",	XtRString, sizeof(String),
	  BobOff(bgColorName), XtRImmediate, (XtPointer) "1.0" },
};

#undef BobOff

static  XrmOptionDescRec options[] = {
    { "-help",		"help",		XrmoptionNoArg,		"1"  },
    { "-h",		"help",		XrmoptionNoArg,		"1"  },
    { "-debug",		"debug",	XrmoptionSepArg,	NULL },
    { "-default",	"defaultName",	XrmoptionSepArg,	NULL },
    { "-d",		"defaultName",	XrmoptionSepArg,	NULL },
    { "-follow",	"followName",	XrmoptionSepArg,	NULL },
    { "-f",		"followName",	XrmoptionSepArg,	NULL },
    { "-cmap",		"cmapName",	XrmoptionSepArg,	NULL },
    { "-c",		"cmapName",	XrmoptionSepArg,	NULL },
    { "-amap",		"amapName",	XrmoptionSepArg,	NULL },
    { "-a",		"amapName",	XrmoptionSepArg,	NULL },
    { "-scale",		"scaleText",	XrmoptionSepArg,	NULL },
    { "-x",		"scaleText",	XrmoptionSepArg,	NULL },
    { "-savebgr",	"saveBGR",	XrmoptionNoArg,		"true" },
    { "-savergb",	"saveBGR",	XrmoptionNoArg,		"false" },
    { "-fastrate",	"fastRate",	XrmoptionSepArg,	NULL },
    { "-single",	"singleBuffer",	XrmoptionNoArg,		"true" },
    { "-fov",		"fieldOfView",	XrmoptionSepArg,	NULL },
    { "-eye",		"eyeSeparation",XrmoptionSepArg,	NULL },
    { "-distance",	"eyeDistance",	XrmoptionSepArg,	NULL },
    { "-poffset",	"poffset",	XrmoptionSepArg,	NULL },
    { "-pstride",	"pstride",	XrmoptionSepArg,	NULL },
    { "-psize",		"psize",	XrmoptionSepArg,	NULL },
    { "-pnum",		"pnumber",	XrmoptionSepArg,	NULL },
    { "-pcolor",	"pcolor",	XrmoptionSepArg,	NULL },
    { "-voxbg",		"voxbg",	XrmoptionSepArg,	NULL },
    { "-fog",		"fogDensity",	XrmoptionSepArg,	NULL },
};


static void Usage(void)
{
    Error("\n");
    Error("usage: bob [ options ] datafiles...\n");
    Error("	-cmap    | -c file	Initial Colormap\n");
    Error("	-amap    | -a file	Initial Alphamap\n");
    Error("	-scale   | -x XxYxZ	Dimension scale factor\n");
    Error("	-default | -d file	Default file (Bobfile by default)\n");
    Error("	-single			Force using single buffer\n");
    Error("	-savebgr		Save images in BGR order\n");
    Error("	-savergb		Save images in RGB order\n");
    Error("	-fastrate #		Frames per second interactive (6)\n");
    Error("	-poffset #		Offset of point data per record\n");
    Error("	-pstride #		Size of point data record\n");
    Error("	-psize #		Size of points\n");
    Error("	-pnum #			Number of points in file\n");
    Error("	-pcolor name		Color of points\n");
    Error("	-voxbg name		Color of voxel background\n");
    Error("	-fog density		Fog\n");
    Error("	-help    | -h		This message\n");
    Error("\n");
    Error("	The following options can be intermixed with file names:\n");
    Error("	-size    | -s XxYxZ	Brick dimension\n");
    Error("	-number  | -n #		Number of bricks in file\n");
    Error("	-offset  | -o #		Beginning of brick in file\n");
    Error("	-block   | -b #		Block size of device\n");
    Error("	-p			Point files follow\n");
    Error("	-v			Voxel files follow (default)\n");
    Error("\n");
    Error("	In a brick of bytes, x varies fastest, then y, then z.\n");
    Error("	Block size is for use with raw disk partitions.\n");
    Error("\n");
}


/* I suppose I should implement the darn help buttons,
 * instead of removing them...
 */
void RemoveHelp(Widget w)
{
    Widget	help = NULL;
    
    if (XtIsSubclass(w, xmFileSelectionBoxWidgetClass))
	help = XmFileSelectionBoxGetChild(w, XmDIALOG_HELP_BUTTON);
    else if (XtIsSubclass(w, xmMessageBoxWidgetClass))
	help = XmMessageBoxGetChild(w, XmDIALOG_HELP_BUTTON);
    if (help)
	XtUnmanageChild(help);
}


void RemoveCancel(Widget w)
{
    Widget	cancel = NULL;
    
    if (XtIsSubclass(w, xmFileSelectionBoxWidgetClass))
	cancel = XmFileSelectionBoxGetChild(w, XmDIALOG_CANCEL_BUTTON);
    else if (XtIsSubclass(w, xmMessageBoxWidgetClass))
	cancel = XmMessageBoxGetChild(w, XmDIALOG_CANCEL_BUTTON);
    if (cancel)
	XtUnmanageChild(cancel);
}


static GLXconfig stereoConfig[] = {
    { GLX_NORMAL, GLX_RGB, TRUE },
    { GLX_NORMAL, GLX_DOUBLE, TRUE },
    { GLX_NORMAL, GLX_ZSIZE, GLX_NOCONFIG },
    { 0, 0, 0 }
};


/*ARGSUSED*/
static Widget CreateStereoPopup(Widget p, char *name, 
				ArgList junk, Cardinal njunk)
{
    return XtCreatePopupShell(name, overrideShellWidgetClass, p, NULL, 0);
}


/*ARGSUSED*/
static Widget CreateStereo(Widget p, char *name, ArgList junk, Cardinal njunk)
{
    Arg		arg;
    Widget	glw;
    GLXconfig	*glxc = stereoConfig;
    
    if (bob->singleBuffer)
	while (glxc->buffer) {
	    if (glxc->mode == GLX_DOUBLE) {
		glxc->arg = FALSE;
		break;
	    }
	    ++glxc;
	}
    
    XtSetArg(arg, GlxNglxConfig, stereoConfig);
    glw = XtCreateWidget(name, glxMDrawWidgetClass, p, &arg, 1);
    
    return glw;
}


static GLXconfig glxConfig[] = {
    { GLX_NORMAL, GLX_RGB, TRUE },
    { GLX_NORMAL, GLX_DOUBLE, TRUE },
    { GLX_NORMAL, GLX_ZSIZE, GLX_NOCONFIG },
    { 0, 0, 0 }
};

/*ARGSUSED*/
static Widget CreateVox(Widget p, char *name, ArgList junk, Cardinal njunk)
{
    Arg		arg;
    Widget	glw;
    GLXconfig	*glxc = glxConfig;
    
    if (bob->singleBuffer)
	while (glxc->buffer) {
	    if (glxc->mode == GLX_DOUBLE) {
		glxc->arg = FALSE;
		break;
	    }
	    ++glxc;
	}
    
    XtSetArg(arg, GlxNglxConfig, glxConfig);
    glw = XtCreateWidget(name, glxMDrawWidgetClass, p, &arg, 1);
    
    GetValue(GlxNglxConfig, &glxc, glw);
    while (glxc->buffer) {
	if (glxc->buffer == GLX_NORMAL  &&  
	    glxc->mode == GLX_DOUBLE  &&
	    glxc->arg == FALSE) {
	    bob->singleBuffer = TRUE;
	    break;
	}
	++glxc;
    }
    
    return glw;
}


static GLXconfig finderConfig[] = {
    { GLX_NORMAL, GLX_RGB, TRUE },
    { GLX_NORMAL, GLX_DOUBLE, TRUE },
    { GLX_NORMAL, GLX_ZSIZE, GLX_NOCONFIG },
    { 0, 0, 0 }
};

/*ARGSUSED*/
static Widget CreateFinder(Widget p, char *name, ArgList junk, Cardinal njunk)
{
    Arg		arg;
    Widget	glw;
    GLXconfig	*glxc = finderConfig;
    
    if (bob->singleBuffer)
	while (glxc->buffer) {
	    if (glxc->mode == GLX_DOUBLE) {
		glxc->arg = FALSE;
		break;
	    }
	    ++glxc;
	}
    
    XtSetArg(arg, GlxNglxConfig, finderConfig);
    glw = XtCreateWidget(name, glxMDrawWidgetClass, p, &arg, 1);
    
    GetValue(GlxNglxConfig, &glxc, glw);
    while (glxc->buffer) {
	if (glxc->buffer == GLX_NORMAL  &&  
	    glxc->mode == GLX_DOUBLE  &&
	    glxc->arg == FALSE) {
	    bob->singleBuffer = TRUE;
	    break;
	}
	++glxc;
    }
    
    return glw;
}


static WidgetSpec widgetSpec[] = {
    { 0x101, "dimDialog",	XmCreateFormDialog },
    { 0x100, "finderFrame",	XmCreateFrame },
    { 0x010, "finder",		CreateFinder },
    { 0x110, "dimBox",		XmCreateForm },
    { 0x000, "xcenter",		XmCreateScale },
    { 0x000, "ycenter",		XmCreateScale },
    { 0x000, "zcenter",		XmCreateScale },
    { 0x000, "stride",		XmCreateScale },
    { 0x000, "xsize",		XmCreateScale },
    { 0x000, "ysize",		XmCreateScale },
    { 0x000, "zsize",		XmCreateScale },
    { 0x010, "msize",		XmCreateScale },
    
    { 0x101, "anDialog",	XmCreateFormDialog },
    { 0x000, "axis",		XmCreateTextField },
    { 0x000, "axisLabel",	XmCreateLabel },
    { 0x000, "axisX",		XmCreatePushButton },
    { 0x000, "axisY",		XmCreatePushButton },
    { 0x000, "axisZ",		XmCreatePushButton },
    { 0x000, "volumeLabel",	XmCreateLabel },
    { 0x000, "increment",	XmCreateTextField },
    { 0x000, "incLabel",	XmCreateLabel },
    { 0x000, "frameinc",	XmCreateTextField },
    { 0x000, "fincLabel",	XmCreateLabel },
    { 0x000, "ansep3",		XmCreateSeparator },
    { 0x000, "anSave",		XmCreateToggleButton },
    { 0x000, "nstep",		XmCreateTextField },
    { 0x000, "nstepLabel",	XmCreateLabel },
    { 0x000, "vsize",		XmCreateTextField },
    { 0x000, "vsizeLabel",	XmCreateLabel },
    { 0x000, "device",		XmCreateTextField },
    { 0x000, "deviceLabel",	XmCreateLabel },
    { 0x000, "offset",		XmCreateTextField },
    { 0x000, "offsetLabel",	XmCreateLabel },
    { 0x000, "anFrame",		XmCreateLabel },
    { 0x000, "ansep",		XmCreateSeparator },
    { 0x000, "anGo",		XmCreatePushButton },
    { 0x000, "anDone",		XmCreatePushButton },
    { 0x010, "ansep2",		XmCreateSeparator },
    
    { 0x101, "stereoShell",	CreateStereoPopup },
    { 0x010, "stereoVox",	CreateStereo },
    
    { 0x001, "fsBox",		XmCreateFileSelectionDialog },
    
    { 0x001, "copyright",	XmCreateInformationDialog },
    
    { 0x101, "bugBox",		XmCreateFormDialog },
    { 0x000, "send",		XmCreatePushButtonGadget },
    { 0x000, "cancel",		XmCreatePushButtonGadget },
    { 0x000, "sep",		XmCreateSeparatorGadget },
    { 0x010, "bugText",		XmCreateText },
    
    { 0x101, "rendBox",		XmCreateFormDialog },
    { 0x000, "dorend",		XmCreatePushButtonGadget },
    { 0x000, "sep",		XmCreateSeparatorGadget },
    { 0x000, "hostTitle",	XmCreateLabel },
    { 0x000, "rendHost",	XmCreateTextField },
    { 0x000, "textTitle",	XmCreateLabel },
    { 0x010, "rendText",	XmCreateText },
    
    { 0x110, "topform",		XmCreateForm },
    
    { 0x100, "voxframe",	XmCreateFrame },
    { 0x010, "vox",		CreateVox },
    
    { 0x100, "menuBar",		XmCreateMenuBar },
    
    { 0x000, "control",		XmCreateCascadeButtonGadget },
    { 0x101, "controlMenu",	XmCreatePulldownMenu },
    { 0x000, "open",		XmCreatePushButtonGadget },
    { 0x000, "save",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "animate",		XmCreatePushButtonGadget },
    { 0x000, "openDim",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "remote",		XmCreatePushButtonGadget },
    { 0x000, "startcmap",	XmCreatePushButtonGadget },
    { 0x000, "startamap",	XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x010, "quit",		XmCreatePushButtonGadget },
    
    { 0x000, "view",		XmCreateCascadeButtonGadget },
    { 0x101, "viewMenu",	XmCreatePulldownMenu },
    { 0x000, "stereo",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "home",		XmCreatePushButtonGadget },
    { 0x000, "flipx",		XmCreatePushButtonGadget },
    { 0x000, "flipy",		XmCreatePushButtonGadget },
    { 0x000, "flipz",		XmCreatePushButtonGadget },
    { 0x000, "sepMenu",		XmCreateSeparatorGadget },
    { 0x000, "bounds",		XmCreateToggleButtonGadget },
    { 0x000, "colorBar",	XmCreateToggleButtonGadget },
    { 0x000, "annotate",	XmCreateToggleButtonGadget },
    { 0x010, "point",		XmCreateToggleButtonGadget },
    
    { 0x000, "mode",		XmCreateCascadeButtonGadget },
    { 0x101, "modeMenu",	XmCreatePulldownMenu },
    { 0x000, "dbuffer",		XmCreateToggleButtonGadget },
    { 0x000, "fog",		XmCreateToggleButtonGadget },
    { 0x000, "maxval",		XmCreateToggleButtonGadget },
    { 0x000, "brightness",	XmCreateToggleButtonGadget },
    { 0x000, "interpolate",	XmCreateToggleButtonGadget },
    { 0x010, "softEdge",	XmCreateToggleButtonGadget },
    
    { 0x000, "dimension",	XmCreateCascadeButtonGadget },
    { 0x111, "dimMenu",		XmCreatePulldownMenu },
    { 0x000, "dactive",		XmCreateToggleButtonGadget },
    { 0x000, "dapply",		XmCreatePushButtonGadget },
    { 0x000, "dadd",		XmCreatePushButtonGadget },
    { 0x010, "ddelete",		XmCreatePushButtonGadget },
    
    { 0x110, "infoform", 	XmCreateForm },
    
    { 0x000, "ahpcrc",		XmCreatePushButton },
    { 0x000, "alpha",		XmCreateScale },
    { 0x000, "frame",		XmCreateScale },
    { 0x000, "filelist",	XmCreateScrolledList },
    { 0x010, "dimlist",		XmCreateScrolledList },
    
    { 0x000, NULL,		NULL }			/*ENDTABLE*/
};


#include "bob.x"

static void InstallBobPixmap(Widget w)
{
    Window	root = RootWindowOfScreen(XtScreen(w));
    Pixmap	pix = 0;
    int		depth;
    Pixel	fg, bg;
    
    /* Create the bitmaps using the root window, because
     * This widget may not be realized yet.
     */
    
    GetValue(XmNdepth, &depth, (Widget) w);
    fg = BlackPixelOfScreen(XtScreen(w));
    bg = WhitePixelOfScreen(XtScreen(w));
    
    pix = XCreatePixmapFromBitmapData(XtDisplay(w), root, bob_bits, 
				      bob_width, bob_height,
				      fg, bg, depth);
    if (pix)
	SetValue(XmNsymbolPixmap, (XtArgVal) pix, w);
}


static void DressWidgetTree(void)
{
#define AC(wn,rn,cb,cd)	XtAddCallback(GetWidget(wn),rn,cb,(XtPointer)cd)
    
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("controlMenu"),
	     GetWidget("control"));
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("viewMenu"),
	     GetWidget("view"));
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("modeMenu"),
	     GetWidget("mode"));
    SetValue(XmNsubMenuId, (XtArgVal) GetWidget("dimMenu"),
	     GetWidget("dimension"));
    
    RemoveHelp(GetWidget("fsBox"));
    RemoveHelp(GetWidget("copyright"));
    
    InstallBobPixmap(GetWidget("copyright"));
    
    AC("vox",		GlxNexposeCallback,	VoxExposeCB, 0);
    AC("vox",		GlxNresizeCallback,	VoxResizeCB, 0);
    AC("vox",		GlxNginitCallback,	VoxInitCB, 0);
    AC("vox",		GlxNinputCallback,	VoxInputCB, 0);
    
    AC("stereoVox",	GlxNexposeCallback,	StereoExposeCB, 0);
    AC("stereoVox",	GlxNresizeCallback,	StereoResizeCB, 0);
    AC("stereoVox",	GlxNginitCallback,	StereoInitCB, 0);
    AC("stereoVox",	GlxNinputCallback,	StereoInputCB, 0);
    
    AC("finder",	GlxNexposeCallback,	FinderExposeCB, 0);
    AC("finder",	GlxNresizeCallback,	FinderResizeCB, 0);
    AC("finder",	GlxNginitCallback,	FinderInitCB, 0);
    AC("finder",	GlxNinputCallback,	FinderInputCB, 0);
    
    AC("fsBox",		XmNokCallback,		FileCB, FileOK);
    AC("fsBox",		XmNokCallback,		UnmanageCB, 0);
    AC("fsBox",		XmNcancelCallback, 	FileCB, FileCancel);
    AC("fsBox",		XmNcancelCallback, 	UnmanageCB, 0);
    
    AC("anSave",	XmNvalueChangedCallback,ToggleCB, TogAnSave);
    AC("axisX",		XmNactivateCallback,	ButtonCB, ButAxisX);
    AC("axisY",		XmNactivateCallback,	ButtonCB, ButAxisY);
    AC("axisZ",		XmNactivateCallback,	ButtonCB, ButAxisZ);
    AC("anGo",		XmNactivateCallback,	ButtonCB, ButAnGo);
    AC("anDone",	XmNactivateCallback,	ButtonCB, ButAnDone);
    
    AC("ahpcrc",	XmNactivateCallback, ManageCB, GetWidget("copyright"));
    
    AC("copyright",	XmNcancelCallback,   ManageCB, GetWidget("bugBox"));
    
    AC("send",		XmNactivateCallback,	BugCB, 0);
    
    AC("dorend",	XmNactivateCallback,	ButtonCB, ButRender);

    AC("open",		XmNactivateCallback,	ButtonCB, ButOpen);
    AC("open",		XmNactivateCallback,	ManageCB, GetWidget("fsBox"));
    AC("save",		XmNactivateCallback,	ButtonCB, ButSave);
    AC("save",		XmNactivateCallback,	ManageCB, GetWidget("fsBox"));
    AC("startamap",	XmNactivateCallback,	ButtonCB, ButStartAmap);
    AC("startcmap",	XmNactivateCallback,	ButtonCB, ButStartCmap);
    AC("animate",	XmNactivateCallback, ManageCB, GetWidget("anDialog"));
    AC("openDim",	XmNactivateCallback, ManageCB, GetWidget("dimDialog"));
    AC("remote",	XmNactivateCallback,	ManageCB, GetWidget("rendBox"));
    AC("quit",		XmNactivateCallback,	ButtonCB, ButQuit);
    
    AC("stereo",	XmNactivateCallback,	ButtonCB, ButStereo);
    AC("home",		XmNactivateCallback,	ButtonCB, ButHome);
    AC("flipx",		XmNactivateCallback,	ButtonCB, ButFlipX);
    AC("flipy",		XmNactivateCallback,	ButtonCB, ButFlipY);
    AC("flipz",		XmNactivateCallback,	ButtonCB, ButFlipZ);
    
    AC("dbuffer",	XmNvalueChangedCallback,ToggleCB, TogBuffer);
    AC("brightness",	XmNvalueChangedCallback,ToggleCB, TogBright);
    AC("bounds",	XmNvalueChangedCallback,ToggleCB, TogBounds);
    AC("annotate",	XmNvalueChangedCallback,ToggleCB, TogAnnotate);
    AC("colorBar",	XmNvalueChangedCallback,ToggleCB, TogColorBar);
    AC("interpolate",	XmNvalueChangedCallback,ToggleCB, TogInterpolate);
    AC("maxval",	XmNvalueChangedCallback,ToggleCB, TogMaxVal);
    AC("softEdge",	XmNvalueChangedCallback,ToggleCB, TogSoft);
    
    AC("dactive",	XmNvalueChangedCallback,ToggleCB, TogDimActive);
    AC("dapply",	XmNactivateCallback,	ButtonCB, ButDimApply);
    AC("dadd",		XmNactivateCallback,	ButtonCB, ButDimAdd);
    AC("ddelete",	XmNactivateCallback,	ButtonCB, ButDimDelete);
    
    AC("filelist",	XmNsingleSelectionCallback,ListCB, ListFile);
    AC("dimlist",	XmNsingleSelectionCallback,ListCB, ListDim);
    
    AC("alpha",		XmNvalueChangedCallback,ScaleCB, ScaleAlpha);
    AC("frame",		XmNvalueChangedCallback,ScaleCB, ScaleFrame);
    
    AC("xcenter",	XmNvalueChangedCallback,ScaleCB, ScaleCenterX);
    AC("ycenter",	XmNvalueChangedCallback,ScaleCB, ScaleCenterY);
    AC("zcenter",	XmNvalueChangedCallback,ScaleCB, ScaleCenterZ);
    AC("stride",	XmNvalueChangedCallback,ScaleCB, ScaleStride);
    
    AC("xcenter",	XmNdragCallback,	ScaleCB, ScaleCenterX);
    AC("ycenter",	XmNdragCallback,	ScaleCB, ScaleCenterY);
    AC("zcenter",	XmNdragCallback,	ScaleCB, ScaleCenterZ);
    AC("stride",	XmNdragCallback,	ScaleCB, ScaleStride);
    
    AC("xsize",		XmNvalueChangedCallback,ScaleCB, ScaleSizeX);
    AC("ysize",		XmNvalueChangedCallback,ScaleCB, ScaleSizeY);
    AC("zsize",		XmNvalueChangedCallback,ScaleCB, ScaleSizeZ);
    AC("msize",		XmNvalueChangedCallback,ScaleCB, ScaleSizeM);
    
    AC("xsize",		XmNdragCallback,	ScaleCB, ScaleSizeX);
    AC("ysize",		XmNdragCallback,	ScaleCB, ScaleSizeY);
    AC("zsize",		XmNdragCallback,	ScaleCB, ScaleSizeZ);
    AC("msize",		XmNdragCallback,	ScaleCB, ScaleSizeM);
}


static void CreateFileEntry(char *fname, unsigned dim[3], unsigned offset,
			    unsigned blockSize, unsigned nframe)
{
    BobFile	*bfile = MallocType(BobFile);
    BobFrame	*bframe;
    unsigned	i, seek, stride;
    char	ftitle[256];
    
    MemCheck(bfile);
    bfile->fname = NewString(fname, 0);
    bfile->fd = 0;
    bfile->data = NULL;
    bfile->shmid = 0;
    bfile->dim[0] = dim[0];
    bfile->dim[1] = dim[1];
    bfile->dim[2] = dim[2];
    bfile->offset = offset;
    bfile->nframe = nframe;
    bfile->firstFrame = StackSize(bob->frameList) + 1;
    bfile->blockSize = blockSize;
    
    (void) SkipInsert(bob->fileList, bfile);
    
    seek = bfile->offset;
    stride = dim[0]*dim[1]*dim[2];
    if (bfile->blockSize)
	stride = RoundUp(stride, bfile->blockSize);
    
    for	(i = 0; i < bfile->nframe; ++i) {
	bframe = MallocType(BobFrame);
	MemCheck(bframe);
	bframe->bobFile = bfile;
	bframe->seek = seek;
	seek += stride;
	StackAddLast(bob->frameList, bframe);
    }
    
    if (nframe == 1)
	(void) sprintf(ftitle, "%s (%ux%ux%u)", fname, 
		       dim[0], dim[1], dim[2]);
    else
	(void) sprintf(ftitle, "%s (%ux%ux%u, %u frames)", fname, 
		       dim[0], dim[1], dim[2], nframe);
    
    bfile->listEntry = XmStringCreateSimple(ftitle);
    XmListAddItemUnselected(GetWidget("filelist"), bfile->listEntry, 0);
}


void FreeFileEntry(void *entry)
{
    BobFile	*bfile = (BobFile *) entry;
    unsigned	i;
    
    XmListDeleteItem(GetWidget("filelist"), bfile->listEntry);
    XmStringFree(bfile->listEntry);
    Free(bfile->fname);
    if (bfile->fd)
	(void) close(bfile->fd);
    if (bfile->data)
	(void) shmdt(bfile->data);

    for (i = bfile->nframe; i > 0; --i)
	Free(StackRemove(bob->frameList, bfile->firstFrame));
    Free(bfile);
}


static unsigned OptMatch(char *arg, char *opt)
{
    if (strlen(arg) == 2)
	return strncmp(arg, opt, 2) ? 0 : 1;
    return strcmp(arg, opt) ? 0 : 1;
}


static unsigned FillFileList(int argc, char *argv[], unsigned dim[3],
			     unsigned *offset, unsigned *blockSize,
			     unsigned *nframe)
{
    int		i;
    int		voxFile = True;
    
    for (i = 0; i < argc; ++i)
	if (OptMatch(argv[i], "-size")) {
	    if (sscanf(argv[++i], "%ux%ux%u", dim, dim+1, dim+2) < 3) {
		Error("Bad size specification: %s\n", argv[i]);
		Error("Use XxYxZ.  For example: 64x64x32\n");
		return 0;
	    }
	    
	} else if (OptMatch(argv[i], "-offset")) {
	    *offset = ScanLong(argv[++i]);
	    
	} else if (OptMatch(argv[i], "-block")) {
	    *blockSize = ScanLong(argv[++i]);
	    
	} else if (OptMatch(argv[i], "-number")) {
	    *nframe = ScanLong(argv[++i]);
	    *nframe = MAX(1, *nframe);
	    
	} else if (OptMatch(argv[i], "-point")) {
	    voxFile = False;
	    
	} else if (OptMatch(argv[i], "-voxel")) {
	    voxFile = True;
	    
	} else if (voxFile) {
	    if (dim[0] == 0) {
		Error("No dimensions specified!  Use -s option.\n");
		return 0;
	    }
	    CreateFileEntry(argv[i], dim, *offset, *blockSize, *nframe);

	} else {
	    StackAddLast(bob->pointList, NewString(argv[i], 0));
	}
    
    return 1;
}


static unsigned KeyMatch(char *arg, char *key)
{
    return strncasecmp(arg, key, strlen(key)) ? 0 : 1;
}


unsigned ReadDefaults(unsigned dim[3], unsigned *offset, 
		      unsigned *blockSize, unsigned *nframe,
		      char *src, FILE *dfile)
{
    unsigned char	cmap[768];
    char		dline[DLINELEN];
    char       		key[DLINELEN];
    char		*value;
    char		*srcline = src;
    unsigned		i, j, moredata;
    unsigned		dimEmpty = 0;
    
    if (src) {
	for (i = 0; srcline[i] && srcline[i] != '\n'; ++i)
	    dline[i] = srcline[i];
	dline[i] = '\0';
	if (srcline[i])
	    srcline += i + 1;
	else
	    srcline += i;
	moredata = i;
    } else {
	if (!dfile) {
	    if (bob->defName) {
		dfile = fopen(bob->defName, "r");
		if (!dfile) {
		    Error("Can't open defaults file %s\n", bob->defName);
		    return 0;
		}
	    } else {
		dfile = fopen("Bobfile", "r");
		if (!dfile)
		    dfile = fopen("bobfile", "r");
		if (!dfile)
		    return 1;
	    }
	}
	moredata = fgets(dline, DLINELEN, dfile) != NULL;
    }
    
    while (moredata) {
	if (strlen(dline) >= DLINELEN-1) {
	    Error("Maximum line length (%d) exceeded in defaults file\n",
		  DLINELEN-1);
	    return 0;
	}
	value = dline;
	while (isspace(*value))
	    ++value;
	if (*value == '#'  ||  *value == '\0')
	    (void) strcpy(key, "comment");
	else {
	    value = strchr(dline, '=');
	    if (value) {
		*value++ = '\0';
		(void) sscanf(dline, "%s", key);
	    } else {
		value = dline;
		(void) strcpy(key, "file");
	    }
	}
	
	/* Trim white space from both ends of value */
	while (isspace(*value))
	    ++value;
	for (i = strlen(value); i; --i)
	    if (!isspace(value[i-1])) {
		value[i] = '\0';
		break;
	    }
	
	if (KeyMatch(key, "size")) {
	    j = strlen(value);
	    for (i = 0; i < j; ++i)
		if (!isdigit(value[i]))
		    value[i] = ' ';
	    if (sscanf(value, "%u%u%u", dim, dim+1, dim+2) < 3) {
		Error("Bad size specification: %s\n", value);
		return 0;
	    }
	    
	} else if (KeyMatch(key, "offset")) {
	    *offset = ScanLong(value);
	    
	} else if (KeyMatch(key, "block")) {
	    *blockSize = ScanLong(value);
	    
	} else if (KeyMatch(key, "number")) {
	    *nframe = ScanLong(value);
	    *nframe = MAX(1, *nframe);
	    
	} else if (KeyMatch(key, "color")) {
	    (void) ReadColormap(cmap, value);
	    PackColor(cmap);
	    
	} else if (KeyMatch(key, "alpha")) {
	    (void) ReadColormap(cmap, value);
	    for (i = 0; i < 256; ++i)
		cmap[i] = (2*cmap[i*3] + 5*cmap[i*3+1] + cmap[i*3+2]) >> 3;
	    PackAlpha(cmap);
	    
	} else if (KeyMatch(key, "dim")) {
	    if (!dimEmpty) {
		XmListDeleteAllItems(GetWidget("dimlist"));
		dimEmpty = 1;
	    }
	    AddDimEntry(value);
	    
	} else if (KeyMatch(key, "scale")) {
	    j = strlen(value);
	    for (i = 0; i < j; ++i)
		if (isalpha(value[i]) || value[i] == ',')
		    value[i] = ' ';
	    if (sscanf(value, "%f%f%f", 
		       bob->dimScale, bob->dimScale+1, bob->dimScale+2) < 3) {
		Error("Improper scale factor: use, e.g., 1.0 2.0 2.5\n");
		bob->dimScale[0] = bob->dimScale[1] = bob->dimScale[2] = 1;
	    } else
		vscale(bob->dimScale, fsqrt(3.0)/vlength(bob->dimScale));
	    
	} else if (KeyMatch(key, "file")) {
	    if (dim[0] == 0) {
		Error("No file dimensions specified!.\n");
		return 0;
	    }
	    CreateFileEntry(value, dim, *offset, *blockSize, *nframe);
	    
	} else if (KeyMatch(key, "point")) {
	    StackAddLast(bob->pointList, NewString(value, 0));

	} else if (KeyMatch(key, "npoint")) {
	    if (sscanf(value, "%u", &bob->nallpoint) < 1) {
		Error("Bad point number specification: %s\n", value);
		return 0;
	    }
	}
	
	if (src) {
	    for (i = 0; srcline[i] && srcline[i] != '\n'; ++i)
		dline[i] = srcline[i];
	    dline[i] = '\0';
	    if (srcline[i])
		srcline += i + 1;
	    else
		srcline += i;
	    moredata = i > 0;
	} else
	    moredata = fgets(dline, DLINELEN, dfile) != NULL;
    }
    
    return 1;
}


void SaveDefaults(void)
{
    FILE		*dfile;
    BobFile		*bfile, *lfile;
    int			dimCount;
    XmString		*dimTable;
    char		*dimEntry;
    unsigned		i;
    
    if (bob->defName) {
	dfile = fopen(bob->defName, "w");
	if (!dfile) {
	    Error("Can't open defaults file %s\n", bob->defName);
	    return;
	}
    } else {
	dfile = fopen("Bobfile", "w");
	if (!dfile) {
	    Error("Can't open defaults file Bobfile\n");
	    return;
	}
    }
    
    (void) fprintf(dfile, "# Bobfile - defaults for Bob\n");
    
    if (bob->cmapName)
	(void) fprintf(dfile, "color = %s\n", bob->cmapName);
    if (bob->amapName)
	(void) fprintf(dfile, "alpha = %s\n", bob->amapName);
    if (bob->dimScale[0] != 1  ||  bob->dimScale[1] != 1)
	(void) fprintf(dfile, "scale = %f %f %f\n",
		       bob->dimScale[0], bob->dimScale[1], bob->dimScale[2]);
    
    GetValue(XmNitemCount, &dimCount, NULL);
    GetValue(XmNitems, &dimTable, GetWidget("dimlist"));
    for (i = 0; i < dimCount; ++i) {
	if (!XmStringGetLtoR(dimTable[i], XmSTRING_OS_CHARSET, &dimEntry))
	    continue;
	(void) fprintf(dfile, "dimension = %s\n", dimEntry);
	free(dimEntry);
    }
    
    lfile = NULL;
    bfile = SkipHead(bob->fileList);
    while (bfile) {
	
	if (!lfile || 
	    bfile->dim[0] != lfile->dim[0] ||
	    bfile->dim[1] != lfile->dim[1] ||
	    bfile->dim[2] != lfile->dim[2])
	    (void) fprintf(dfile, "size = %u %u %u\n", 
			   bfile->dim[0], bfile->dim[1], bfile->dim[2]);
	
	if ((!lfile  &&  bfile->offset != 0) || 
	    (lfile   &&  bfile->offset != lfile->offset))
	    if (bfile->offset % 1024)
		(void) fprintf(dfile, "offset = %u\n", bfile->offset);
	    else
		(void) fprintf(dfile, "offset = %uk\n", bfile->offset  /1024);
	
	if ((!lfile  &&  bfile->blockSize != 0) || 
	    (lfile   &&  bfile->blockSize != lfile->blockSize))
	    if (bfile->blockSize % 1024)
		(void) fprintf(dfile, "blocksize = %u\n", bfile->blockSize);
	    else
		(void) fprintf(dfile, "blocksize = %uk\n", 
			       bfile->blockSize  /1024);
	
	if ((!lfile  &&  bfile->nframe != 1) || 
	    (lfile   &&  bfile->nframe != lfile->nframe))
	    (void) fprintf(dfile, "number = %u\n", bfile->nframe);
	
	(void) fprintf(dfile, "file = %s\n", bfile->fname);
	
	lfile = bfile;
	bfile = SkipNext(bob->fileList);
    }
}


static int CompareFileNames(void *v1, void *v2)
{
    BobFile	*f1 = (BobFile *) v1;
    BobFile	*f2 = (BobFile *) v2;
    
    return strcmp(f1->fname, f2->fname);
}


void RestartFiles(char *cmd, FILE *file)
{
    unsigned		dim[3];
    unsigned		offset = 0;
    unsigned		blockSize = 0;
    unsigned		nframe = 1;
    
    dim[0] = dim[1] = dim[2] = 0;
    
    SkipFreeData(bob->fileList, FreeFileEntry);
    StackFreeData(bob->pointList, free);
    bob->file = NULL;
    (void) ReadDefaults(dim, &offset, &blockSize, &nframe, cmd, file);
}


void PositionDimDialog(void)
{
    Widget	dd = GetWidget("dimDialog");
    Dimension	h;
    Position	x, y;
    
    GetValue(XmNheight, &h, NULL);
    GetValue(XmNx, &x, NULL);
    GetValue(XmNy, &y, bob->shell);
    
    y += h + 40;
    SetValue(XmNx, (XtArgVal) x, NULL);
    SetValue(XmNy, (XtArgVal) y, dd);
    XtManageChild(dd);
}


#ifdef USEFALLBACK
static String fallbackResources[] = { 
#include "fallback.h"
    NULL,
};
#else
static String *fallbackResources = NULL;
#endif

int SetupBob(int *argc, char *argv[])
{
    unsigned		i;
    unsigned char	cmap[768];
    unsigned		dim[3];
    unsigned		offset = 0;
    unsigned		blockSize = 0;
    unsigned		nframe = 1;
    char		graphicsHW[64];
    Screen		*screen;
    Dimension		shadow;

    usconfig(CONF_INITUSERS, 16);
    
    bob = MallocType(BobApp);
    
    MemCheck(bob);
    ZeroType(bob, BobApp);
    
    (void) signal(SIGPIPE, SIG_IGN);
    
    bob->fileList = SkipNew(CompareFileNames, False);
    bob->frameList = StackNew();
    bob->pointList = StackNew();
    
    bob->shell = XtAppInitialize(&bob->app, CLASS_NAME, 
				 options, XtNumber(options),
				 (Cardinal *) argc, argv, 
				 fallbackResources, NULL, 0);
    AddWidgetConverter(bob->app);
    bob->display = XtDisplay(bob->shell);
    
    XtGetApplicationResources(bob->shell, (XtPointer) bob, 
			      resources, XtNumber(resources), NULL, 0);
    if (bob->help) {
	Usage();
	return 0;
    }
    
    bob->afact = 1.0;
    bob->vball = TrackballInit();
    bob->fball = TrackballInit();
    bob->running = TRUE;
    if (bob->scaleText)
	if (sscanf(bob->scaleText, "%fx%fx%f", 
		   bob->dimScale, bob->dimScale+1, bob->dimScale+2) < 3) {
	    Error("Improper scale factor: use, e.g., 1.0x2.0x2.5\n");
	    bob->dimScale[0] = bob->dimScale[1] = bob->dimScale[2] = 1;
	} else
	    vscale(bob->dimScale, fsqrt(3.0)/vlength(bob->dimScale));
    else
	bob->dimScale[0] = bob->dimScale[1] = bob->dimScale[2] = 1;
    
    BuildWidgetTree(bob->shell, widgetSpec);
    DressWidgetTree();
    
    /* Set shell size behaviour
     */
    screen = XtScreen(bob->shell);
    GetValue(XmNshadowThickness, &shadow, GetWidget("voxframe"));
    SetValue(XmNmaxHeight, (XtArgVal) (2*shadow + HeightOfScreen(screen)),
	     NULL);
    SetValue(XmNmaxHeight, (XtArgVal) (2*WidthOfScreen(screen)),
	     bob->shell);
    
    /* Determine the polygon fill rate of the graphics
     */
    (void) gversion(graphicsHW);
    if (strncmp(graphicsHW+4, "VGX", 3) == 0)
	bob->polyRate = 360000;
    else if (strncmp(graphicsHW+4, "GTX", 3) == 0)
	bob->polyRate = 67500;
    else if (strncmp(graphicsHW+4, "PIT", 3) == 0)
	bob->polyRate = 32000;
    else
	bob->polyRate = 10000;
    
    /* Determine pixmode
     */
    if (strncmp(graphicsHW+4, "PI", 2) == 0)
	bob->anPixmode = 0;
    else if (strncmp(graphicsHW+4, "LG", 2) == 0)
	bob->anPixmode = 0;
    else if (strncmp(graphicsHW+4, "XG", 2) == 0)
	bob->anPixmode = 0;
    else
	bob->anPixmode = 1;
    
    /* Turn off double buffer toggle if not available
     */
    if (bob->singleBuffer) {
	SetValue(XmNsensitive, (XtArgVal) False, NULL);
	SetValue(XmNset, (XtArgVal) False, GetWidget("dbuffer"));
    }
    
    for (i = 0; i < 256; ++i)
	cmap[i*3] = cmap[i*3+1] = cmap[i*3+2] = i;
    cmap[3] = cmap[4] = cmap[5] = 0;
    PackColor(cmap);
    
    dim[0] = dim[1] = dim[2] = 0;
    if (!ReadDefaults(dim, &offset, &blockSize, &nframe, NULL, NULL))
	return 0;
    if (*argc > 1)
	if (!FillFileList(*argc - 1, argv + 1, 
			  dim, &offset, &blockSize, &nframe))
	    return 0;
    
    if (bob->followName) {
	int	fd = open(bob->followName, O_RDONLY | O_NDELAY);
	
	if (fd >= 0) {
	    bob->followFile = fdopen(fd, "r");
	    (void) XtAppAddInput(bob->app, fd, (XtPointer) XtInputReadMask,
				 FollowInput, NULL);
	}
    }
    
    if (StackSize(bob->frameList) <= 1)
	XmTextFieldSetString(GetWidget("frameinc"), "0");
    
    if (bob->cmapName) {
	(void) ReadColormap(cmap, bob->cmapName);
	PackColor(cmap);
    }
    if (bob->amapName) {
	(void) ReadColormap(cmap, bob->amapName);
	for (i = 0; i < 256; ++i)
	    cmap[i] = (2*cmap[i*3] + 5*cmap[i*3+1] + cmap[i*3+2]) >> 3;
	PackAlpha(cmap);
    }

    {
	XColor		exact, xc;
	Colormap	xcmap;
	float		cscale = 0xffff;
	
	GetValue(XmNcolormap, &xcmap, bob->shell);
	if (XLookupColor(bob->display, xcmap, bob->pcolorName, &exact, &xc)) {
	    bob->pcolor[0] = exact.red   / cscale;
	    bob->pcolor[1] = exact.green / cscale;
	    bob->pcolor[2] = exact.blue  / cscale;
	} else {
	    bob->pcolor[0] = 1;
	    bob->pcolor[1] = 1;
	    bob->pcolor[2] = 1;
	}
	if (XLookupColor(bob->display, xcmap, bob->bgColorName, &exact, &xc)) {
	    int	rr = exact.red >> 8;
	    int	gg = exact.green >> 8;
	    int	bb = exact.blue >> 8;
	    bob->bgColor = (bb << 16) + (gg << 8) + rr;
	} else {
	    bob->bgColor = 0;
	}
    }

    XmToggleButtonGadgetSetState(GetWidget("point"), 
				 StackSize(bob->pointList) > 0, False);
    
    XtRealizeWidget(bob->shell);
    
    SetFirstFile();
    
    return 1;
}

