/*ScianVisWindows.c
  Visualization window stuff
  Eric Pepke
  April 6, 1990
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianArrays.h"
#include "ScianLists.h"
#include "ScianWindows.h"
#include "ScianTextBoxes.h"
#include "ScianTitleBoxes.h"
#include "ScianObjWindows.h"
#include "ScianIcons.h"
#include "ScianColors.h"
#include "ScianControls.h"
#include "ScianButtons.h"
#include "ScianSpaces.h"
#include "ScianLights.h"
#include "ScianSliders.h"
#include "ScianIDs.h"
#include "ScianVisWindows.h"
#include "ScianDatasets.h"
#include "ScianPictures.h"
#include "ScianDialogs.h"
#include "ScianEvents.h"
#include "ScianScripts.h"
#include "ScianErrors.h"
#include "ScianMethods.h"
#include "ScianStyle.h"
#include "ScianVisObjects.h"
#include "ScianDraw.h"
#include "ScianDrawings.h"
#include "ScianObjFunctions.h"
#include "ScianFilters.h"
#include "ScianPick.h"
#include "ScianSymbols.h"
#include "ScianTemplates.h"
#include "ScianTemplateHelper.h"

ObjPtr visWindowClass;			/*Class for vis windows*/
ObjPtr spaceWindowClass;		/*Class for space subwindows*/
ObjPtr visCorralClass;			/*Class for corral of a vis window*/
Bool showControlPanels = true;		/*True iff show control panels by default*/
ObjPtr visualizeAsList;			/*List for Visualize As*/

static ObjPtr HideVisWindowPanel(window)
WinInfoPtr window;
/*Hides the panel of the controls on the vis window.  Returns true iff it did*/
{

    if (window)
    {
	ObjPtr panel, space, assocPanel;
	WinInfoPtr subWindow;
	int panelWidth;
	int left, right, bottom, top;
	int nl, nr, nb, nt;

	SelWindow(window);
	GetWindowBounds(&left, &right, &bottom, &top);

	space = FindSpace(window);

        if (!space)
	{
	    return false;
	}

	panel = GetVar(space, OBJPANEL);
	if (panel)
	{
	    /*Get the panel's bounds*/
	    Get2DIntBounds(panel, &nl, &nr, &nb, &nt); 
	    panelWidth = nr - nl;

	    /*Change panel to be off the screen*/
	    nl = right + 1;
	    nr = nl + panelWidth;
	    Set2DIntBounds(panel, nl, nr, nb, nt);
	    ImInvalid(panel);
	}

	panel = GetVar(space, TOOLPANEL);
	if (panel)
	{
	    /*Get the panel's bounds*/
	    Get2DIntBounds(panel, &nl, &nr, &nb, &nt); 
	    panelWidth = nr - nl;

	    /*Change panel to be off the screen*/
	    nr = left - 1;
	    nl = nr - panelWidth;
	    Set2DIntBounds(panel, nl, nr, nb, nt);
	    ImInvalid(panel);
	}

	/*Change the window or space*/
	Get2DIntBounds(space, &nl, &nr, &nb, &nt); 
	nr = right;
	nl = left;

	if ((subWindow = (WinInfoPtr) GetVar((ObjPtr) window, SPACEWINDOW)) &&
	    subWindow != window)
	{
	    /*Change the window*/
	    int ox, oy;
	    SelWindow(window);
	    GetWindowOrigin(&ox, &oy);
	    SelWindow(subWindow);
	    SetWindowPosition(ox, ox + nr - nl, oy, oy + nt - nb);
	}
	else
	{
	    /*Change the space*/
	    Set2DIntBounds(space, nl, nr, nb, nt);
	
	    ImInvalid(space);

	    /*And its panels*/
	    assocPanel = GetVar(space, FRONTPANEL);
	    if (assocPanel)
	    {
		int ol, or, ob, ot;

		Get2DIntBounds(assocPanel, &ol, &or, &ob, &ot);
		ReshapeObject(assocPanel, ol, or, ob, ot, nl, nr, nb, nt);
		ImInvalid(assocPanel);
	    }

	    assocPanel = GetVar(space, BACKPANEL);
	    if (assocPanel)
	    {
		int ol, or, ob, ot;

		Get2DIntBounds(assocPanel, &ol, &or, &ob, &ot);
		ReshapeObject(assocPanel, ol, or, ob, ot, nl, nr, nb, nt);
		ImInvalid(assocPanel);
	    }
	}

	SetVar((ObjPtr) window, PANELHIDDEN, ObjTrue);

	DeleteMenus(window);

	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

void DoHidePanel()
{
    if (selWinInfo)
    {
	if (logging)
	{
	    Log("hide panel\n");
	}

	DeferMessage((ObjPtr) selWinInfo, HIDEPANEL);
    }
}

static ObjPtr ShowVisWindowPanel(window)
WinInfoPtr window;
/*Shows the panel of the controls on the vis window.  Returns true iff it did*/
{
    if (window)
    {
	ObjPtr panel, space, assocPanel;
	WinInfoPtr subWindow;
	int lPanelWidth, rPanelWidth;
	int left, right, bottom, top;
	int nl, nr, nb, nt;

	SelWindow(window);
	GetWindowBounds(&left, &right, &bottom, &top);

	space = FindSpace(window);

        if (!space)
	{
	    return false;
	}

	panel = GetVar(space, OBJPANEL);
	if (panel)
	{
	    /*Get the panel's bounds*/
	    Get2DIntBounds(panel, &nl, &nr, &nb, &nt); 
	    rPanelWidth = nr - nl;

	    /*Change panel to be on the screen*/
	    nr = right;
	    nl = right - rPanelWidth;
	    Set2DIntBounds(panel, nl, nr, nb, nt);
	    ImInvalid(panel);
	}

	panel = GetVar(space, TOOLPANEL);
	if (panel)
	{
	    /*Get the panel's bounds*/
	    Get2DIntBounds(panel, &nl, &nr, &nb, &nt); 
	    lPanelWidth = nr - nl;

	    /*Change panel to be on the screen*/
	    nl = left;
	    nr = left + lPanelWidth;
	    Set2DIntBounds(panel, nl, nr, nb, nt);
	    ImInvalid(panel);
	}

	/*Change the window or space*/
	Get2DIntBounds(space, &nl, &nr, &nb, &nt); 
	nr = right - rPanelWidth;
	nl = left + lPanelWidth;

	if ((subWindow = (WinInfoPtr) GetVar((ObjPtr) window, SPACEWINDOW)) &&
	    subWindow != window)
	{
	    /*Change the window*/
	    int ox, oy;
	    SelWindow(window);
	    GetWindowOrigin(&ox, &oy);
	    SelWindow(subWindow);
	    SetWindowPosition(ox, ox + nr - nl, oy, oy + nt - nb);
	}
	else
	{
	    /*Change the space*/
	    Set2DIntBounds(space, nl, nr, nb, nt);

	    ImInvalid(space);

	    /*And its panels*/
	    assocPanel = GetVar(space, FRONTPANEL);
	    if (assocPanel)
	    {
		int ol, or, ob, ot;

		Get2DIntBounds(assocPanel, &ol, &or, &ob, &ot);
		ReshapeObject(assocPanel, ol, or, ob, ot, nl, nr, nb, nt);
		ImInvalid(assocPanel);
	    }

	    assocPanel = GetVar(space, BACKPANEL);
	    if (assocPanel)
	    {
		int ol, or, ob, ot;

		Get2DIntBounds(assocPanel, &ol, &or, &ob, &ot);
		ReshapeObject(assocPanel, ol, or, ob, ot, nl, nr, nb, nt);
		ImInvalid(assocPanel);
	    }
	}

	SetVar((ObjPtr) window, PANELHIDDEN, ObjFalse);

	DeleteMenus(window);

	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

void DoShowPanel()
{
    if (selWinInfo)
    {
	if (logging)
	{
	    Log("show panel\n");
	}

	DeferMessage((ObjPtr) selWinInfo, SHOWPANEL);
    }
}

int annotStagger = 0;

ObjPtr DeleteSpacePanelText(text)
ObjPtr text;
/*Delete text from a space panel*/
{
    return ObjTrue;
}

void AddAnnotation(name, bounds)
char *name;
real bounds[4];
/*Adds annotation with name and bounds to current window*/
{
    if (selWinInfo)
    {
	/*Search for a space*/
	ObjPtr space, frontPanel;

	space = FindSpace(selWinInfo);

	if (space)
	{
	    frontPanel = GetObjectVar("AddAnnotation", space, FRONTPANEL);
	    if (frontPanel)
	    {
		/*There's a front panel.  Stick an annotation there*/
		ObjPtr annotation;


		++nAnnot;
		annotation = NewTextBox((int) bounds[0], (int) bounds[1], (int) bounds[2], (int) bounds[3],
					/*WITH_BG + */EDITABLE + ADJUSTABLE, name, "");

		SetMethod(annotation, CLONE, CloneAnnotation);
		SetMethod(annotation, DUPLICATE, DuplicateDrawing);
		PrefixList(GetVar(frontPanel, CONTENTS), annotation);
		SetVar(annotation, PARENT, frontPanel);
		SetTextColor(annotation, NewInt(UIWHITE));
		ImInvalid(annotation);
		SetTextFont(annotation, ANNOTFONT);
		SetTextSize(annotation, ANNOTFONTSIZE);
		SetVar(annotation, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
		SetMethod(annotation, DELETEICON, DeleteSpacePanelText);
		SetMethod(annotation, DELETE, DeleteObject); 
		SetMethod(annotation, PUSHTOBOTTOM, PushDrawingToBottom);
		SetMethod(annotation, BRINGTOTOP, BringDrawingToTop);
		SetMethod(annotation, MOVETOBACKPANEL, MoveDrawingToBackPanel);
		SetMethod(annotation, MOVETOFRONTPANEL, MoveDrawingToFrontPanel);

		DeselectAll();
		Select(annotation, true);
		MakeMeCurrent(annotation);

		if (logging)
		{
		    /*Log the action.*/
		    char cmd[256];
		    char *d;

		    d = &(cmd[0]);
		    sprintf(cmd, "annotation ");
		    while (*d) ++d;
		    MakeObjectName(d, annotation);
		    while (*d) ++d;
		    sprintf(d, " [%g %g %g %g]\n",
			bounds[0], bounds[1], bounds[2], bounds[3]);
		    Log(cmd);
		}
	    }
	}
    }
}

void AddTimeReadout(name, bounds)
char *name;
real bounds[4];
/*Adds annotation with name and bounds to current window*/
{
    if (selWinInfo)
    {
	/*Search for a space*/
	ObjPtr space, frontPanel, list;

	space = FindSpace(selWinInfo);

	if (space)
	{
	    frontPanel = GetObjectVar("AddTimeReadout", space, FRONTPANEL);
	    if (frontPanel)
	    {
		/*There's a front panel.  Stick a time readout there*/
		ObjPtr timeReadout;

		++nTimeReadout;
		timeReadout = NewTimeReadout((int) bounds[0], (int) bounds[1], (int) bounds[2], (int) bounds[3],
					name, space);
		SetMethod(timeReadout, CLONE, CloneTimeReadout);
		SetMethod(timeReadout, DUPLICATE, DuplicateDrawing);
		PrefixList(GetVar(frontPanel, CONTENTS), timeReadout);
		SetVar(timeReadout, PARENT, frontPanel);
		SetTextColor(timeReadout, NewInt(UIWHITE));
		ImInvalid(timeReadout);
		SetTextFont(timeReadout, ANNOTFONT);
		SetTextSize(timeReadout, ANNOTFONTSIZE);
		SetVar(timeReadout, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
		SetMethod(timeReadout, DELETEICON, DeleteSpacePanelText);
		SetMethod(timeReadout, DELETE, DeleteObject); 
		SetMethod(timeReadout, PUSHTOBOTTOM, PushDrawingToBottom);
		SetMethod(timeReadout, BRINGTOTOP, BringDrawingToTop);
		SetMethod(timeReadout, MOVETOBACKPANEL, MoveDrawingToBackPanel);
		SetMethod(timeReadout, MOVETOFRONTPANEL, MoveDrawingToFrontPanel);
		list = NewList();
		PrefixList(list, NewSymbol(BOUNDS));
		PrefixList(list, NewSymbol(TEXTFONT));
		PrefixList(list, NewSymbol(TEXTSIZE));
		PrefixList(list, NewSymbol(ALIGNMENT));
		SetVar(timeReadout, SNAPVARS, list);

		DeselectAll();
		Select(timeReadout, true);

		if (logging)
		{
		    /*Log the action.*/
		    char cmd[256];
		    char *d;

		    d = &(cmd[0]);
		    sprintf(cmd, "timereadout ");
		    while (*d) ++d;
		    MakeObjectName(d, timeReadout);
		    while (*d) ++d;
		    sprintf(d, " [%g %g %g %g]\n",
			bounds[0], bounds[1], bounds[2], bounds[3]);
		    Log(cmd);
		}
	    }
	}
    }
}

void AddRectangle(name, bounds)
char *name;
real bounds[4];
/*Adds rectangle with name and bounds to current window*/
{
    if (selWinInfo)
    {
	/*Search for a space*/
	ObjPtr space, frontPanel;

	space = FindSpace(selWinInfo);

	if (space)
	{
	    frontPanel = GetObjectVar("AddRectangle", space, FRONTPANEL);
	    if (frontPanel)
	    {
		/*There's a front panel.  Stick a rectangle there*/
		ObjPtr rectangle;

		++nRect;
		rectangle = NewRectangle((int) bounds[0], (int) bounds[1], (int) bounds[2], (int) bounds[3],
					name);
		PrefixList(GetVar(frontPanel, CONTENTS), rectangle);
		SetVar(rectangle, PARENT, frontPanel);
		ImInvalid(rectangle);
 
		DeselectAll();
		Select(rectangle, true);

		if (logging)
		{
		    /*Log the action.*/
		    char cmd[256];
		    char *d;

		    d = &(cmd[0]);
		    sprintf(cmd, "rectangle ");
		    while (*d) ++d;
		    MakeObjectName(d, rectangle);
		    while (*d) ++d;
		    sprintf(d, " [%g %g %g %g]\n",
			bounds[0], bounds[1], bounds[2], bounds[3]);
		    Log(cmd);
		}
	    }
	}
    }
}

void AddLine(name, x1, y1, x2, y2)
char *name;
int x1, y1, x2, y2;
/*Adds line with name and endpoints (x1, y1)-(x2, y2) to current window*/
{
    if (selWinInfo)
    {
	/*Search for a space*/
	ObjPtr space, frontPanel;

	space = FindSpace(selWinInfo);

	if (space)
	{
	    frontPanel = GetObjectVar("AddLine", space, FRONTPANEL);
	    if (frontPanel)
	    {
		/*There's a front panel.  Stick a line there*/
		ObjPtr line;

		++nLine;
		line = NewLine(x1, y1, x2, y2, name);
		PrefixList(GetVar(frontPanel, CONTENTS), line);
		SetVar(line, PARENT, frontPanel);
		ImInvalid(line);
 
		DeselectAll();
		Select(line, true);

		if (logging)
		{
		    /*Log the action.*/
		    char cmd[256];
		    char *d;

		    d = &(cmd[0]);
		    sprintf(cmd, "line ");
		    while (*d) ++d;
		    MakeObjectName(d, line);
		    while (*d) ++d;
		    sprintf(d, " %d %d %d %d\n",
			x1, y1, x2, y2);
		    Log(cmd);
		}
	    }
	}
    }
}

static void DoTogglePanel()
/*Toggles the panel of the controls on the top window.*/
{
    if (selWinInfo)
    {
	if (GetPredicate((ObjPtr) selWinInfo, PANELHIDDEN))
	{
	    DoShowPanel();
	}
	else
	{
	    DoHidePanel();
	}
    }
}

void DoShowControls()
/*Shows the controls in the selected objects in the window*/
{
    DoObjFunction(OF_SHOW_CONTROLS);
}

ObjPtr MakeLocalCopy(object)
ObjPtr object;
/*Makes a local copy of an object*/
{
    object = ObjectWhichRepresents(selWinInfo, object);

    if (object && IsIcon(object))
    {
	ObjPtr repObj, newObj;
	FuncTyp method;
	
	repObj = GetVar(object, REPOBJ);
	if (repObj)
	{
	    ObjPtr contentsList, corral = NULLOBJ;
	    contentsList = GetVar(object, PARENT);
	    if (contentsList || !IsList(contentsList))
	    {
		contentsList = GetVar(contentsList, CONTENTS);
	    }
	    if (!contentsList || !IsList(contentsList))
	    {
		ReportError("MakeLocalCopy","Internal error: cannot find contents list");
	    }

	    if (contentsList)
	    {
		corral = contentsList;
		while (corral && !IsCorral(corral))
		{
		    corral = GetVar(corral, PARENT);
		}
	    }

	    if (!corral)
	    {
		ReportError("MakeLocalCopy", "Cannot find local corral");
	    }

	    method = GetMethodSurely("MakeLocalCopy", repObj, CLONE);
	    if (!method)
	    {
		return ObjFalse;
	    }

	    method = GetMethod(object, DELETEICON);
	    if (method)
	    {
		if (IsTrue((*method)(object)))
		{
		    if (contentsList && IsList(contentsList))
		    {
			ImInvalid(corral);
			DeleteFromList(contentsList, object);
		    }
		}
	    }

	    method = GetMethod(repObj, CLONE);
	    if (method)
	    {
		ObjPtr name;
	        newObj = (*method)(repObj);
		object = Clone(object);
	        SetVar(object, REPOBJ, newObj);
		MakeVar(newObj, NAME);
		name = GetVar(newObj, NAME);
		SetVar(object, NAME, object);
		SetVar(object, SELECTED, ObjFalse);
	    }

	    method = GetMethod(corral, DROPINCONTENTS);
	    if (method)
	    {
		ObjPtr locArray;
		int x, y;
		real loc[2];
		locArray = GetVar(object, ICONLOC);
		if (locArray)
		{
		    Array2CArray(loc, locArray);
		    x = loc[0];
		    y = loc[1];
		}
		else
		{
		    x = y = 0;
		}
		(*method)(corral, object, x, y);
	    }
	    return ObjTrue;
	}
    }
    return ObjFalse;
}

ObjPtr CloneObject(object)
ObjPtr object;
/*Clones an individual object*/
{
    object = ObjectWhichRepresents(selWinInfo, object);
    if (object && IsIcon(object))
    {
	ObjPtr repObj, newVis;
	FuncTyp method;

	repObj = GetVar(object, REPOBJ);
	if (repObj && !GetPredicate(repObj, ONEONLY))
	{
	    ObjPtr corral = NULLOBJ;

	    corral = GetVar(object, PARENT);

	    if (corral)
	    {
		while (corral && !IsCorral(corral))
		{
		    corral = GetVar(corral, PARENT);
		}
	    }

	    if (!corral)
	    {
		ReportError("CloneObject", "Cannot find local corral");
		return ObjFalse;
	    }

	    method = GetMethodSurely("CloneObject", repObj, CLONE);
	    if (!method)
	    {
		return ObjFalse;
	    }

	    newVis = (*method)(repObj);
	    SetVar(newVis, SELECTED, ObjFalse);
	    if (IsController(newVis))
	    {
		AddControllerToSpace(newVis, FindSpace(selWinInfo), corral, NULLOBJ);
		ImInvalid(newVis);
	    }
	    else
	    {
		IdleAllWindows();
		AddObjToSpace(newVis, FindSpace(selWinInfo), corral, NULLOBJ, NULLOBJ);
	    }
	    return ObjTrue;
	}
    }
    return ObjFalse;
}

int vwSerialNumber = 0;

void NewSerializedVisWindow()
/*Creates a new, serialized visualization window*/
{
    WinInfoPtr visWindow;

    /*Create a vis window*/
    sprintf(tempStr, "Visualization %d", ++vwSerialNumber); 
    visWindow = NewVisWindow(tempStr, WINDBUF + WINZBUF + WINRGB);
}


int vawSerialNumber;

void DoShowFrontPanelControls()
/*Shows the controls for the front panel in the selected vis window*/
{
    ObjPtr space, panel;
    if (!selWinInfo)
    {
	return;
    }

    space = FindSpace(selWinInfo);
    if (!space)
    {
	return;
    }

    panel = GetVar(space, FRONTPANEL);
    if (!panel)
    {
	return;
    }

    if (logging)
    {
	Log("show front panel controls\n");
	InhibitLogging(true);
    }
    NewControlWindow(panel);
    if (logging)
    {
	InhibitLogging(false);
    }
}

void DoShowBackPanelControls()
/*Shows the controls for the back panel in the selected vis window*/
{
    ObjPtr space, panel;
    if (!selWinInfo)
    {
	return;
    }

    space = FindSpace(selWinInfo);
    if (!space)
    {
	return;
    }

    panel = GetVar(space, BACKPANEL);
    if (!panel)
    {
	return;
    }

    if (logging)
    {
	Log("show back panel controls\n");
	InhibitLogging(true);
    }

    NewControlWindow(panel);
    if (logging)
    {
	InhibitLogging(false);
    }
}

void DoShowSpaceControls()
/*Shows the controls for the space in the selected vis window*/
{
    ObjPtr space;
    if (!selWinInfo)
    {
	return;
    }

    space = FindSpace(selWinInfo);
    if (!space)
    {
	return;
    }

    if (logging)
    {
	Log("show space controls\n");
	InhibitLogging(true);
    }

    NewControlWindow(space);
    if (logging)
    {
	InhibitLogging(false);
    }
}

#define MINANNOTSIZE	40
#define MINRECTSIZE	0
#define MINLINESIZE	0

static ObjPtr DeleteClockReadout(readout)
ObjPtr readout;
/*Deletes a clock readout from a space*/
{
    return ObjTrue;
}

static ObjPtr MakeClockValue(clockDisplay)
ObjPtr clockDisplay;
/*Makes the VALUE of a clock display*/
{
    ObjPtr space, timeObj, format;
    real time;
    char s[400];

    space = GetObjectVar("MakeClockValue", clockDisplay, SPACE);
    if (!space)
    {
	return ObjFalse;
    }

    MakeVar(space, TIME);
    timeObj = GetVar(space, TIME);
    if (!timeObj)
    {
	time = 0.0;
    }
    else
    {
	time = GetReal(timeObj);
    }

    format = GetVar(clockDisplay, FORMAT);
    if (format)
    {
	PrintClock(s, GetString(format), time);
    }
    else
    {
        sprintf(s, "%#.2f", time);
    }

    SetVar(clockDisplay, VALUE, NewString(s));

    return ObjTrue;
}

ObjPtr ChangeClockDisplayFormat(textBox)
ObjPtr textBox;
/*Changes a clock display's FORMAT according to textBox*/
{
    ObjPtr repObj;
    repObj = GetObjectVar("ChangeClockFormat", textBox, REPOBJ);
    if (!repObj) return ObjFalse;
    SetVar(repObj, FORMAT, GetValue(textBox));
    ImInvalid(repObj);
    return ObjTrue;
}

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

    dialogExists = DialogExists((WinInfoPtr) display, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) display, NewString("Controls"), windowName, 
	CDWINWIDTH, CDWINHEIGHT, CDWINWIDTH,
	CDWINHEIGHT, WINDBUF + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	long info;
	ObjPtr value;
	ObjPtr format;
	ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
	ObjPtr colorWheel, slider, radioGroup;
	int left, right, bottom, top;

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

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

	/*Add in a panel*/
	panel = NewPanel(greyPanelClass, 0, CDWINWIDTH, 0, CDWINHEIGHT);
	if (!panel)
	{
	    return NULLOBJ;
	}
	contents = GetVar((ObjPtr) controlWindow, CONTENTS);
	PrefixList(contents, panel);
	SetVar(panel, PARENT, (ObjPtr) controlWindow);
	ContentsExpectWindowSize(controlWindow, CDWINWIDTH, CDWINHEIGHT);

	contents = GetVar(panel, CONTENTS);

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

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

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

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

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

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

	left -= MINORBORDER;	
	right += MINORBORDER;
	
	/*Make the background controls*/
	top = CDWINHEIGHT - MINORBORDER;
	right = CDWINWIDTH - MINORBORDER;
	left = right - (2 * MINORBORDER + 2 * MAJORBORDER + COLORWHEELWIDTH + SLIDERWIDTH);
	
	titleBox = NewTitleBox(left, right,
			top - TITLEBOXTOP - MINORBORDER - 2 * MINORBORDER - COLORWHEELWIDTH
				- CHECKBOXHEIGHT - TEXTBOXHEIGHT - TEXTBOXSEP,
			top, "Background");
	SetVar(titleBox, PARENT, panel);
	PrefixList(contents, titleBox);
	left += MINORBORDER;
	right -= MINORBORDER;
	top -= TITLEBOXTOP + MINORBORDER;

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

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

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

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

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

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

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

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

	/*Create the format text box*/
	textBox = NewTextBox(left, left + CDFORMATLABELWIDTH, 
				top - EDITBOXHEIGHT + (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2,
				top - (EDITBOXHEIGHT - TEXTBOXHEIGHT) / 2, 
				PLAIN, "Format:", "Format:");
	PrefixList(contents, textBox);
	SetVar(textBox, PARENT, panel);
	    
	/*Create the format editable text box*/
	left = left + CDFORMATLABELWIDTH;
	format = GetVar(display, FORMAT);
	textBox = NewTextBox(left, right, 
				top - EDITBOXHEIGHT,
				top, 
				EDITABLE + WITH_PIT + ONE_LINE, "Format",
				format ? GetString(format) : "%#.2t");
	PrefixList(contents, textBox);
	SetVar(textBox, PARENT, panel);
	SetVar(textBox, REPOBJ, display);
	SetVar(textBox, HELPSTRING, NewString(
		"This text box contains the format used to print the current time in \
the time readout.  It works very much like printf with the special conversion \
characters listed below.  Flags and field widths are exactly as in printf.  \
All times are printed as real numbers, so you may need to limit the number \
of decimal places.  \
There may be several conversions in one string, in which case, all will refer \
to the same time.  Hours and minutes are derives from the internal time specified in seconds.\n\
\n\
%h\t\tPrint the number of hours, from 1 to 12.\n\
%H\t\tPrint the number of hours, from 0 to 23.\n\
%m\t\tPrint the number of minutes, from 0 to 59.\n\
%M\t\tPrint the total number of minutes.\n\
%s\t\tPrint the number of seconds from 0 to 59.\n\
%S\t\tPrint the time in seconds.\n\
%t\t\tPrint the time in timesteps.  For this to work properly, \
each time step of data must have a time equal to the number of the time step.\n\
\n\
Examples for time = 43440 seconds:\n\
Format\t\t\t\tResult\t\tExplanation\n\
%.0t\t\t\t\t43440\t\tThe time is printed in seconds with no decimal places.\n\
%2.0h:%02.0m %a\t\t\t\t12:04 pm\t\tThe time is assumed to be 43440 seconds after \
midnight and is converted into 12-hour format.\n\
%.2M\t\t\t\t724.00\t\tThe time is printed in minutes with 2 decimal places."));
	SetMethod(textBox, CHANGEDVALUE, ChangeClockDisplayFormat);

    }
    return (ObjPtr) controlWindow;
}

#ifdef PROTO
static ObjPtr NewTimeReadout(int left, int right, int bottom, int top, char *name, ObjPtr space)
#else
static ObjPtr NewTimeReadout(left, right, bottom, top, name, space)
int left, right, bottom, top;
char *name;
ObjPtr space;
#endif
{
    ObjPtr clockThere;
    clockThere = NewTextBox(left, right, bottom, top, ADJUSTABLE, name, "");
    SetMethod(clockThere, DELETEICON, DeleteClockReadout);
    DeclareDependency(clockThere, VALUE, FORMAT);
    SetVar(clockThere, SPACE, space);
    DeclareIndirectDependency(clockThere, VALUE, SPACE, TIME);
    SetMethod(clockThere, NEWCTLWINDOW, ShowClockDisplayControls);
    SetMethod(clockThere, VALUE, MakeClockValue);
    SetVar(clockThere, FORMAT, NewString("%#.2t"));
    SetVar(clockThere, HELPSTRING, NewString("This time readout displays the time \
given by the clock in this space.  The display format is controlled by the Format \
text box in the readout's control panel.\n"));
    return clockThere;
}

#ifdef INTERACTIVE
static ObjPtr PressSpacePanel(object, x, y, flags)
ObjPtr object;
int x, y;
long flags;
/*Does a press in a field beginning at x and y.  Returns
  true iff the press really was in the field.*/
{
    int left, right, bottom, top;
    FuncTyp pressContents;		/*Routine to press the contents*/

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

    if (x >= left && x <= right && y >= bottom && y <= top)
    {
	/*Hey!  It really was a click in the field*/
	ObjPtr space;
	int tool;
	ObjPtr contents;
	ObjPtr retVal = ObjFalse;

        contents = GetVar(object, CONTENTS);

	if (TOOL(flags) == T_HELP)
	{
	    StartPanel(left, right, bottom, top);
	    retVal = PressObject(contents, x, y, flags);
	    StopPanel();
	    return retVal;
	}

	if (flags & F_OPTIONDOWN)
	{
	    /*Ignore press if this is the front panel*/
	    if (GetPredicate(object, FRONTPANEL))
	    {
		return ObjFalse;
	    }
	}

	if (TOOL(flags) == T_ROTATE) flags |= F_SHIFTDOWN;

	SetScreenGrid(object);

	/*See if it's a dragBuffer click*/
	if (dragBuffer)
	{
	    /*Yes it is.  Move dragBuffer and exit*/
	    dropObject = dragBuffer;
	    dragBuffer = NULLOBJ;
	    return ObjTrue;
	}

    	/*Setup the new viewport*/
	StartPanel(left, right, bottom, top);

        x -= left;
        y -= bottom;
	
	tool = ST_FINGER;
	space = GetVar(object, SPACE);
	if (space)
	{
	    ObjPtr var;
	    var = GetVar(space, EDITTOOL);
	    if (var)
	    {
		tool = GetInt(var); 
	    }
	}
	if (TOOL(flags) == T_HELP)
	{
	    tool = ST_FINGER;
	}

	switch (tool)
	{
	    case ST_FINGER:
		retVal = PressObject(contents, x, y, flags);

		if (TOOL(flags) == T_HELP && !IsTrue(retVal))
		{
		    ContextHelp(object);
		    StopPanel();
		    return ObjTrue;
		}
		else if (!IsTrue(retVal))
		{
		    {
			StopPanel();
			if (!GetPredicate(object, FRONTPANEL))
			{
			    ObjPtr space;
			    if (0 == (flags & F_SHIFTDOWN))
			    {
				DeselectAll();
			    }

			    /*Return press to space*/
			    space = GetVar(object, SPACE);
			    if (TOOL(flags) == T_ROTATE)
			    {
				RotateSpace(space, x + left, y + bottom, flags);
			    }
			    else
			    {
				MoveSpace(space, x + left, y + bottom, flags);
			    }
			    return ObjTrue;
			}
			else
			{
			    return ObjFalse;
			}
		    }
		}
		retVal = ObjTrue;
		break;
	    case ST_ANNOTATION:
	    case ST_RECTANGLE:
	    case ST_LINE:
	    case ST_TIME_READOUT:
		{
		    char name[30];
		    int l, r, b, t;
		    int minSize;
		    ObjPtr boundsArray;
		    ObjPtr screenObject;
		    int startX, startY, newX, newY, endX, endY;

		    if (flags & F_SHIFTDOWN)
		    {
			x = GRIDX(x);
			y = GRIDY(y);
		    }

		    startX = x;
		    startY = y;

		    if (tool == ST_ANNOTATION || tool == ST_TIME_READOUT)
		    {
			minSize = MINANNOTSIZE;
		    }
		    else if (tool == ST_RECTANGLE)
		    {
			minSize = MINRECTSIZE;
		    }
		    else if (tool == ST_LINE)
		    {
			minSize = MINLINESIZE;
		    }

		    l = startX - HANDLESIZE / 2;
		    r = startX + minSize + HANDLESIZE / 2;
		    b = startY - minSize - HANDLESIZE / 2;
		    t = startY + HANDLESIZE / 2;

		    if (tool == ST_ANNOTATION)
		    {
			sprintf(name, "Annotation %d", nAnnot++);

		        screenObject = NewTextBox(l, r, b, t,
					/*WITH_BG + */EDITABLE + ADJUSTABLE, name, "");
			SetMethod(screenObject, CLONE, CloneAnnotation);
			SetMethod(screenObject, DUPLICATE, DuplicateDrawing);
			SetMethod(screenObject, PUSHTOBOTTOM, PushDrawingToBottom);
			SetMethod(screenObject, BRINGTOTOP, BringDrawingToTop);
			SetMethod(screenObject, MOVETOBACKPANEL, MoveDrawingToBackPanel);
			SetMethod(screenObject, MOVETOFRONTPANEL, MoveDrawingToFrontPanel);
		    }
		    else if (tool == ST_TIME_READOUT)
		    {
			sprintf(name, "Time Readout %d", nTimeReadout++);

		        screenObject = NewTimeReadout(l, r, b, t, name, space);
			SetMethod(screenObject, CLONE, CloneTimeReadout);
			SetMethod(screenObject, DUPLICATE, DuplicateDrawing);
			SetMethod(screenObject, PUSHTOBOTTOM, PushDrawingToBottom);
			SetMethod(screenObject, BRINGTOTOP, BringDrawingToTop);
			SetMethod(screenObject, MOVETOBACKPANEL, MoveDrawingToBackPanel);
			SetMethod(screenObject, MOVETOFRONTPANEL, MoveDrawingToFrontPanel);
		    }
		    else if (tool == ST_RECTANGLE)
		    {
			sprintf(name, "Rectangle %d", nRect++);

			screenObject = NewRectangle(l, r, b, t, name);
		    }
		    else if (tool == ST_LINE)
		    {
			sprintf(name, "Line %d", nLine++);

			screenObject = NewLine(startX, startY, startX, startY, name);
		    }
		    SetVar(screenObject, STICKINESS, NewInt(FLOATINGLEFT + FLOATINGRIGHT + FLOATINGTOP + FLOATINGBOTTOM));
		    PrefixList(contents, screenObject);
		    SetVar(screenObject, PARENT, object);
		    if (tool == ST_ANNOTATION)
		    {
			SetTextColor(screenObject, NewInt(UIWHITE));
			ImInvalid(screenObject);
			SetTextFont(screenObject, ANNOTFONT);
			SetTextSize(screenObject, ANNOTFONTSIZE);
			SetMethod(screenObject, DELETEICON, DeleteSpacePanelText);
			SetMethod(screenObject, DELETE, DeleteObject);
		    }
		    else if (tool == ST_TIME_READOUT)
		    {
			SetTextColor(screenObject, NewInt(UIWHITE));
			ImInvalid(screenObject);
			SetTextFont(screenObject, ANNOTFONT);
			SetTextSize(screenObject, ANNOTFONTSIZE);
			SetMethod(screenObject, DELETEICON, DeleteClockReadout);
			SetMethod(screenObject, DELETE, DeleteObject);
		    }

		    if (tool == ST_ANNOTATION || tool == ST_TIME_READOUT)
		    {
			InhibitLogging(true);
			Select(screenObject, true);
			InhibitLogging(false);
		    }

		    /*Set up to draw in the overlay area*/
		    DrawSkeleton(true);

		    DrawMe(screenObject);

		    while (Mouse(&newX, &newY))
		    {

			if (tool == ST_ANNOTATION ||
			    tool == ST_TIME_READOUT ||
			    tool == ST_RECTANGLE ||
			    tool == ST_LINE)
			{
			    if (newX >= startX && newX - startX < minSize)
			    {
				newX = startX + minSize;
			    }
			    else if (newX < startX && startX - newX < minSize)
			    {
				newX = startX - minSize;
			    }

			    if (newY >= startY && newY - startY < minSize)
			    {
				newY = startY + minSize;
			    }
			    else if (newY < startY && startY - newY < minSize)
			    {
				newY = startY - minSize;
			    }
			}

			if (flags & F_SHIFTDOWN)
			{
			    newX = GRIDX(newX);
			    newY = GRIDY(newY);
			}
			if (newX != x || newY != y)
			{
			    x = newX;
			    y = newY;
			    Set2DIntBounds(screenObject,
					MIN(startX, x) - HANDLESIZE / 2,
					MAX(startX, x) + HANDLESIZE / 2,
					MIN(startY, y) - HANDLESIZE / 2, 
					MAX(startY, y) + HANDLESIZE / 2);
			    if (tool == ST_LINE)
			    {
				ObjPtr var;
				real loc[2];
				loc[0] = newX;
				loc[1] = newY;
				var = NewRealArray(1, 2L);
				CArray2Array(var, loc);
				SetVar(screenObject, ENDPOINT, var);
			    }
			    DrawMe(screenObject);
			}
		    }
		    DrawSkeleton(false);
		    Get2DIntBounds(screenObject, &l, &r, &b, &t);

		    if (logging)
		    {
			/*Log the action.*/
			char cmd[256];
			char *d, *s;

			s = name;
			d = &(cmd[0]);
			if (tool == ST_ANNOTATION)
			{
			    strcpy(cmd, "annotation ");
			}
			else if (tool == ST_RECTANGLE)
			{
			    strcpy(cmd, "rectangle ");
			}
			else if (tool == ST_LINE)
			{
			    strcpy(cmd, "line ");
			}
			else if (tool == ST_TIME_READOUT)
			{
			    strcpy(cmd, "timereadout ");
			}
			while (*d) ++d;
			MakeObjectName(d, screenObject);
			while (*d) ++d;
			if (tool == ST_LINE)
			{
			    sprintf(d, " %d %d %d %d\n",
				startX, startY, x, y);
			}
			else
			{
			    sprintf(d, " [%d %d %d %d]\n",
				l, r, b, t);
			}
			Log(cmd);

			if (flags & F_OPTIONDOWN)
			{
			    strcpy(d, "move ");
			    d = &(cmd[0]);
			    while (*d) ++d;
			    MakeObjectName(d, screenObject);
			    while (*d) ++d;
			    strcpy(d, " to back panel\n");
			    Log(cmd);
			}
		    }
		    SetValue(GetVar(space, TOOLGROUP), NewInt(ST_FINGER));

		    if (!(flags & F_SHIFTDOWN))
		    {
			DeselectAll();
			IdleAllWindows();
		    }

		    Select(screenObject, true);
		    if (tool == ST_ANNOTATION)
		    {
			MakeMeCurrent(screenObject);
		    }
		}
		retVal = ObjTrue;
		break;
	}

	StopPanel();
	return retVal;
    }
    else
    {
	return ObjFalse;
    }
}
#endif

void SetupVisualizeAsList()
{
    EmptyList(visualizeAsList);
}

void AddToVisualizeAsList(object)
ObjPtr object;
{
    PostfixList(visualizeAsList, object);
}

void EmptyVisualizeAsList()
{
    EmptyList(visualizeAsList);
}

ObjPtr ChangeCorralButton(object)
ObjPtr object;
/*Changedvalue for a corral button*/
{
    ObjPtr corral, parent, value, curButton;

    value = GetValue(object);

    parent = GetObjectVar("ChangeCorralButton", object, PARENT);
    if (!parent) return ObjFalse;
    corral = GetObjectVar("ChangeCorralButton", object, CORRAL);
    if (!corral) return ObjFalse;
    curButton = GetVar(parent, BUTTON);

    if (value && IsInt(value) && 0 == GetInt(value) && curButton != object)
    {
	SetVar(corral, CONTENTS, GetVar(object, PANELCONTENTS));
	if (curButton)
	{
		SetVar(curButton, VALUE, NewInt(0));
	}
	SetVar(object, VALUE, NewInt(1));
	SetVar(parent, BUTTON, object);
	ImInvalid(corral);
	return ObjTrue;
    }
    else
    {
	return ObjTrue;
    }
}

void NewVisAsWindow()
/*Brings up a new, empty visAs window*/
{
    WinInfoPtr newWindow;
    ObjPtr objList, contents, fieldContents;
    ObjPtr corral, panel, button, icon;
    ObjPtr textBox;
    ObjPtr controlField;
    ThingListPtr runner;
    int whichIcon;
    int bw;
    char *name;
    ObjPtr var;
    int x;
    int l, r, b, t;

    /*Create a vis as window*/
    sprintf(tempStr, "Visualize As %d", ++vawSerialNumber); 
    newWindow = NewObjWindow(NULLOBJ, tempStr, WINDBUF,
		VAWINWIDTH, VAWINHEIGHT, SCRWIDTH, SCRHEIGHT);

    SetVar((ObjPtr) newWindow, HELPSTRING,
	    NewString("This window shows all the possible ways to visualize a \
group of datasets."));
    ContentsExpectWindowSize(newWindow, VAWINWIDTH, VAWINHEIGHT);

    /*Put in a panel*/
    panel = NewPanel(greyPanelClass, 0, VAWINWIDTH, 0, VAWINHEIGHT);
    SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));

    contents = GetVar((ObjPtr) newWindow, CONTENTS);
    PrefixList(contents, panel);
    SetVar(panel, PARENT, (ObjPtr) newWindow);

    /*Put in contents*/
    contents = GetListVar("NewVisAsWindow", panel, CONTENTS);
    if (!contents)
    {
	return;
    }

    /*Explanatory text at top*/
    textBox = TemplateTextBox(VisAsTemplate, "Visualize As String", 0, "Visualize datasets as...");
    PrefixList(contents, textBox);
    SetVar((ObjPtr) newWindow, TITLETEXT, textBox);
    SetVar(textBox, PARENT, panel);

    /*Icon corral*/
    corral = TemplateIconCorral(VisAsTemplate, "Visualizations Corral", NULLOBJ,
		OBJECTSFROMTOP + BARRIGHT + BARBOTTOM);
    SetVar(corral, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    SetVar((ObjPtr) newWindow, CORRAL, corral);
    SetVar(corral, HELPSTRING,
	NewString("This corral contains icons for all possible visualizations of a group of \
datasets.  You can visualize or show info on the visualizations by selecting \
some of them and pressing the buttons at the bottom of the window.  You can delete \
visualizations by selecting them and choosing Delete from the Object menu."));
    PrefixList(contents, corral);
    SetVar(corral, PARENT, panel);
    SetVar((ObjPtr) newWindow, CORRAL, corral);

    /*Control field*/
    controlField = TemplateControlField(VisAsTemplate, "Visualization Type Field",
		BARBOTTOM);
    SetVar(controlField, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT + STICKYTOP));
    SetVar(controlField, PARENT, panel);
    PrefixList(contents, controlField);
    SetVar((ObjPtr) newWindow, VISASFIELD, controlField);

    /*Buttons in the control field*/
    fieldContents = GetVar(controlField, CONTENTS);
    runner = LISTOF(allVisObjClasses);
    x = 0;
    while (runner)
    {
	icon = GetVar(runner -> thing, DEFAULTICON);
	var = GetIntVar("ShowVisControls", icon, WHICHICON);
	if (var)
	{
	    whichIcon = GetInt(var);
	}
	else
	{
	    whichIcon = ICONQUESTION;
	}
	var = GetVar(runner -> thing, NAME);
	if (var)
	{
	    name = GetString(var);
	}
	else
	{
	    name = "?";
	}

	/*Make a button*/
	button = NewIconLabeledButton(x, x + VAWINICONBUTWIDTH, 0, CWINICONBUTHEIGHT,
			whichIcon, UIYELLOW, name, BS_PITTED);
	x += VAWINICONBUTWIDTH;
	SetMethod(button, ICONEXTRADRAW, GetMethod(icon, ICONEXTRADRAW));

	SetMethod(button, CHANGEDVALUE, ChangeCorralButton);

	SetVar(button, CORRAL, corral);
	PostfixList(fieldContents, button);
	SetVar(button, PARENT, controlField);

	runner = runner -> next;
    }
    RecalcScroll(controlField);


    l = b = 0;
    r = VAWINWIDTH;
    t = VAWINHEIGHT;

    l += MINORBORDER;
    r -= MINORBORDER;
    b += MINORBORDER;
    t = b + BUTTONHEIGHT;
    bw = (r - l - MINORBORDER) / 2;

    /*Make a visualize button*/
    button = NewFunctionButton(newWindow,
		l, l + bw,
		b, b + BUTTONHEIGHT, OF_VISUALIZE); 
    if (button)
    {
	SetVar(button, PARENT, panel);
	SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + STICKYLEFT + FLOATINGRIGHT));
	PrefixList(contents, button);
    }

    /*Make a show info button*/
    button = NewFunctionButton(newWindow,
		r - bw, 
		r,
		b, b + BUTTONHEIGHT, OF_SHOW_CONTROLS); 
    if (button)
    {
	SetVar(button, PARENT, panel);
	SetVar(button, STICKINESS, NewInt(STICKYBOTTOM + FLOATINGLEFT + STICKYRIGHT));
	PrefixList(contents, button);
    }

    /*Set up the list for modifications*/
    SetupVisualizeAsList();
}


void ProcessVisAsList()
/*Processes the visualize as modification list*/
{
    ObjPtr allFilters;
    ThingListPtr visRunner, dataRunner, buttonRunner;
    ObjPtr firstSuccessful;
    ObjPtr contentsList, visList, var;
    ObjPtr corral;

    if (!selWinInfo)
    {
	return;
    }

    if (LISTOF(visualizeAsList))
    {
	/*There's at least one vis as*/
	ObjPtr var, textBox;

	if (LISTOF(visualizeAsList) -> next)
	{
	    /*More than one, don't bother changing text */
	}
	else
	{

	    var = GetVar(LISTOF(visualizeAsList) -> thing, NAME);
	    textBox = GetVar((ObjPtr) selWinInfo, TITLETEXT);
	    if (var && textBox)
	    {
		sprintf(tempStr, "Visualize %s as...", GetString(var));
		SetValue(textBox, NewString(tempStr));
	    }
	}
    }

    /*Find all modifications for the list of filters*/
    allFilters = AllEasyFilters(visualizeAsList);

    corral = GetObjectVar("ProcessVisAsList", (ObjPtr) selWinInfo, CORRAL);
    if (!corral) return;

    var = GetObjectVar("ProcessVisAsList", (ObjPtr) selWinInfo, VISASFIELD);
    if (!var) return;
    var = GetListVar("ProcessVisAsList", var, CONTENTS);
    if (!var) return;
    buttonRunner = LISTOF(var);

    /*Go through all the visualization objects*/ 
    firstSuccessful = NULLOBJ;
    visRunner = LISTOF(allVisObjClasses);
    while (visRunner)
    {
	contentsList = NewList();

	/*Do visualization just on normal data*/
	dataRunner = LISTOF(visualizeAsList);
	while (dataRunner)
	{
	    visList = GetAllVis(dataRunner -> thing, false, visRunner -> thing);
	    if (visList)
	    {
		ThingListPtr runner;
		runner = LISTOF(visList);
		while (runner)
		{
		    ObjPtr vis, icon;
		    vis = runner -> thing;
		    if (vis)
		    {
			SetVar(vis, TEMPLATEP, ObjTrue);
			icon = NewVisIcon(vis);
			if (icon)
			{
			    ObjPtr format;
			    format = GetVar(icon, FORMAT);
			    if (format)
			    {
				SetVar(icon, FORMAT, format);
			    }
			    SetVar(icon, CORRAL, corral);
			    SetVar(icon, ICONLOC, NULLOBJ);
			    PostfixList(contentsList, icon);
			    if (!firstSuccessful)
			    {
				firstSuccessful = buttonRunner -> thing;
			    }
			}
		    }
		    runner = runner -> next;
		}
	    }
	    dataRunner = dataRunner -> next;
	}

	/*Do visualization on filtered data*/
	dataRunner = LISTOF(allFilters);
	while (dataRunner)
	{
	    visList = GetAllVis(dataRunner -> thing, false, visRunner -> thing);
	    if (visList)
	    {
		ThingListPtr runner;
		runner = LISTOF(visList);
		while (runner)
		{
		    ObjPtr vis, icon;
		    vis = runner -> thing;
		    if (vis)
		    {
			SetVar(vis, TEMPLATEP, ObjTrue);
			icon = NewVisIcon(vis);
			if (icon)
			{
			    ObjPtr format;
			    format = GetVar(icon, FORMAT);
			    if (format)
			    {
				SetVar(icon, FORMAT, format);
			    }
			    SetVar(icon, CORRAL, corral);
			    SetVar(icon, ICONLOC, NULLOBJ);
			    PostfixList(contentsList, icon);
			    if (!firstSuccessful)
			    {
				firstSuccessful = buttonRunner -> thing;
			    }
			}
		    }
		    runner = runner -> next;
		}
	    }
	    dataRunner = dataRunner -> next;
	}

	SetVar(buttonRunner -> thing, PANELCONTENTS, contentsList);
	buttonRunner = buttonRunner -> next;

	visRunner = visRunner -> next;
    }

    if (firstSuccessful)
    {
	InhibitLogging(true);
	SetValue(firstSuccessful, ObjTrue);
	InhibitLogging(false);
    }

    EmptyVisualizeAsList();
}

void VisObjectsAs()
/*Opens a new VisObjectsAs window using default for all of the
  selected objects.
*/
{
    DoObjFunction(OF_VISUALIZE_AS);
}

static ObjPtr DropInVisCorral(corral, object, x, y)
ObjPtr corral, object;
int x, y;
/*Drops an icon in a vis corral*/
{
    ObjPtr repObj;
    repObj = GetVar(object, REPOBJ);
    if (repObj)
    {
	real loc[2];
	ObjPtr locArray;
	loc[0] = x;
	loc[1] = y;
	locArray = NewRealArray(1, 2L);
	CArray2Array(locArray, loc);
	if (IsController(repObj))
	{
	    AddControllerToSpace(repObj, FindSpace(selWinInfo), GetVar((ObjPtr) selWinInfo, CORRAL), locArray);
	    ImInvalid(repObj);
	}
	else
	{
	    AddObjToSpace(repObj, FindSpace(selWinInfo), GetVar((ObjPtr) selWinInfo, CORRAL), locArray, NULLOBJ);
	    IdleAllWindows();
	}
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

void ForAllVisWindows(routine)
void (*routine)();
/*Performs (*routine)(window) on all vis windows*/
{
    WinInfoPtr curWindow;
    curWindow = allWindows;
    while (curWindow)
    {
	if (IsVisWindow((ObjPtr) curWindow))
	{
	    (*routine)(curWindow);
	}
	curWindow = curWindow -> next;
    }
}

void PushNonVisWindows()
/*Pushes all the non vis windows*/
{
#ifdef GRAPHICS
#ifdef WINDOWS4D
    int maxDepth;
    WinInfoPtr curWindow;
    maxDepth = -1;

    curWindow = allWindows;
    while (curWindow)
    {
	if (IsVisWindow((ObjPtr) curWindow))
	{
	    if ((curWindow) -> id)
	    {
		maxDepth = MAX(maxDepth, windepth((curWindow) -> id));
	    }
	}
	curWindow = curWindow -> next;
    }

    curWindow = allWindows;
    while (curWindow)
    {
	if (!IsVisWindow((ObjPtr) curWindow))
	{
	    if ((curWindow) -> id && windepth((curWindow) -> id) < maxDepth)
	    {
		SelWindow(curWindow);
		--maxDepth;
		winpush();
	    }
	}
	curWindow = curWindow -> next;
    }
#endif
#endif
}

void Tile(width, height)
int width, height;
/*Tiles all the vis windows in the lower left corner as big as width, height*/
{
#ifdef GRAPHICS
#ifdef WINDOWS4D
    int nWide, nHigh;		/*# of windows wide and high*/
    int i, j;
    int nVisWindows;
    WinInfoPtr curWindow;
    long oldWindow;

    if (logging)
    {
	char cmd[256];
	sprintf(cmd, "tile %d %d\n", width, height);
	Log(cmd);
    }

    /*Count the number of vis windows*/
    nVisWindows = 0;
    curWindow = allWindows;
    while (curWindow)
    {
	if (IsVisWindow((ObjPtr) curWindow))
	{
	    ++nVisWindows;
	}
	curWindow = curWindow -> next;
    }
    if (!nVisWindows) return;

    /*Estimate the number of windows high*/
    nHigh = sqrt((double) nVisWindows) + 0.49;
    nWide = nVisWindows / nHigh;
    if (nWide * nHigh < nVisWindows) ++nHigh;

    /*Go through and tile the windows*/
    i = 0;
    j = nHigh - 1;
    curWindow = allWindows;
    while (curWindow)
    {
	if (IsVisWindow((ObjPtr) curWindow))
	{
	    long l, r, b, t;
	    
	    if (i)
	    {
		l = i * width / nWide + 1;
	    }
	    else
	    {
		l = 0;
	    }
	    r = (i + 1) * width / nWide;
	    if (j)
	    {
		b = j * height / nHigh + 1;
	    }
	    else
	    {
		b = 0;
	    }
	    t = (j + 1) * height / nHigh;
	    if (++i >= nWide)
	    {
		i = 0;
		--j;
	    }

	    /*Move the window*/
	    SelWindow(curWindow);
	    SetWindowPosition(l, r, b, t);
	}
	curWindow = curWindow -> next;
    }
#endif
#endif
}

void DoTileFullScreen()
/*Tiles all the visualization windows within the full screen*/
{
    Tile(SCRWIDTH, SCRHEIGHT);
}

void DoTileVideoScreen()
/*Tiles all the visualization windows within the full screen*/
{
    Tile(recScrWidth, recScrHeight);
}

static void RGBTask()
/*Task to change the current window to RGB*/
{
    if (selWinInfo)
    {
	selWinInfo -> flags |= WINRGB;

	SetMode(selWinInfo);
	ImInvalid((ObjPtr) selWinInfo);
    }
}

static void CMapTask()
/*Task to change the current window to CMap mode*/
{
    if (selWinInfo)
    {
	selWinInfo -> flags &= ~WINRGB;

	SetMode(selWinInfo);
	ImInvalid((ObjPtr) selWinInfo);
    }
}

static ObjPtr ChangeVisColorMode(radio)
ObjPtr radio;
/*Changes the color mode of the current visualization window according to
  the value of radio*/
{
    int rgbp;
    ObjPtr repObj;

    rgbp = GetInt(GetValue(radio));

    repObj = GetWindowVar("ChangeVisColorMode", radio, REPOBJ);
    if (!repObj)
    {
	return ObjFalse;
    }

    if (rgbp)
    {
	DeferMessage(repObj, SETTORGBMODE);
    }
    else
    {
	DeferMessage(repObj, SETTOCMAPMODE);
    }
    return ObjTrue;
}

ObjPtr ChangeShowGrid(checkBox)
ObjPtr checkBox;
/*Changes show grid according to a check box*/
{
    ObjPtr repObj;
    ObjPtr value;
    real hsv[3], rgb[3];

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

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

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

    return ObjTrue;
}

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

    dialogExists = DialogExists((WinInfoPtr) spacePanel, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) spacePanel, NewString("Controls"), windowName, 
	SPWINWIDTH, SPWINHEIGHT, SPWINWIDTH,
	SPWINHEIGHT, WINDBUF + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	long info;
	ObjPtr value;
	
	ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
	ObjPtr colorWheel, slider, radioGroup;
	int left, right, bottom, top;

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

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

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

	contents = GetVar(panel, CONTENTS);

	/*Add in the group of controls for text color*/
	left = MAJORBORDER;
	top = SPWINHEIGHT - MAJORBORDER;
	right = SPWINWIDTH - MAJORBORDER;
	bottom = SPWINHEIGHT - (MAJORBORDER + (COLORWHEELWIDTH + TEXTBOXSEP + TEXTBOXHEIGHT + CHECKBOXHEIGHT + TITLEBOXTOP + 3 * MINORBORDER))
;

	titleBox = NewTitleBox(left, right, bottom, top, "Background");
	PrefixList(contents, titleBox);
	SetVar(titleBox, PARENT, panel);

	left += MINORBORDER;
	right -= MINORBORDER;
	top -= MINORBORDER + TITLEBOXTOP;
	
	/*Get the color for priming the controls*/
	var = GetVar(spacePanel, BACKGROUND);
	if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	{
	    Array2CArray(rgb, var);
	    hasBackground = true;
	}
	else if (var && IsInt(var))
	{
	    rgb[0] = uiColors[GetInt(var)][0] / 255.0;
	    rgb[1] = uiColors[GetInt(var)][1] / 255.0;
	    rgb[2] = uiColors[GetInt(var)][2] / 255.0;
	    hasBackground = true;
	}
	else
	{
	    rgb[0] = rgb[1] = rgb[2] = 0.0;
	    hasBackground = false;
	}
	RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]), (rgb[0]), (rgb[1]), (rgb[2]));

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

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

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

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

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

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

	/*Make check box for show grid*/
	left = MAJORBORDER;
	top = MAJORBORDER + CHECKBOXHEIGHT;
	right = SPWINWIDTH - MAJORBORDER;
	bottom = MAJORBORDER;

	checkBox = NewCheckBox(left, right, bottom, top, "Show grid",
			GetPredicate(spacePanel, SHOWGRID));
	PrefixList(contents, checkBox);
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, spacePanel);
	SetMethod(checkBox, CHANGEDVALUE, ChangeShowGrid);
    }
    return (ObjPtr) controlWindow;
}

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

    dialogExists = DialogExists((WinInfoPtr) spacePanel, NewString("Controls"));
    controlWindow = GetDialog((WinInfoPtr) spacePanel, NewString("Controls"), windowName, 
	SPWINWIDTH, SPWINHEIGHT, SPWINWIDTH,
	SPWINHEIGHT, WINDBUF + WINFIXEDSIZE);
    
    if (!dialogExists)
    {
	long info;
	ObjPtr value;
	
	ObjPtr checkBox, icon, name, colorBar, titleBox, textBox, button;
	ObjPtr colorWheel, slider, radioGroup;
	int left, right, bottom, top;

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

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

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

	contents = GetVar(panel, CONTENTS);

	/*Add in the group of controls for text color*/
	left = MAJORBORDER;
	top = SPWINHEIGHT - MAJORBORDER;
	right = SPWINWIDTH - MAJORBORDER;
	bottom = SPWINHEIGHT - (MAJORBORDER + (COLORWHEELWIDTH +
		TEXTBOXSEP + TEXTBOXHEIGHT + CHECKBOXHEIGHT + TITLEBOXTOP + 3 * MINORBORDER));

	titleBox = NewTitleBox(left, right, bottom, top, "Background");
	PrefixList(contents, titleBox);
	SetVar(titleBox, PARENT, panel);

	left += MINORBORDER;
	right -= MINORBORDER;
	top -= MINORBORDER + TITLEBOXTOP;
	
	/*Get the color for priming the controls*/
	var = GetVar(spacePanel, BACKGROUND);
	if (var && IsRealArray(var) && RANK(var) == 1 && DIMS(var)[0] == 3)
	{
	    Array2CArray(rgb, var);
	    hasBackground = true;
	}
	else if (var && IsInt(var))
	{
	    rgb[0] = uiColors[GetInt(var)][0] / 255.0;
	    rgb[1] = uiColors[GetInt(var)][1] / 255.0;
	    rgb[2] = uiColors[GetInt(var)][2] / 255.0;
	    hasBackground = true;
	}
	else
	{
	    rgb[0] = rgb[1] = rgb[2] = 0.0;
	    hasBackground = false;
	}
	RGB2HSV(&(hsv[0]), &(hsv[1]), &(hsv[2]), (rgb[0]), (rgb[1]), (rgb[2]));

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

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

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

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

	/*Make check box for show grid*/
	left = MAJORBORDER;
	top = MAJORBORDER + CHECKBOXHEIGHT;
	right = SPWINWIDTH - MAJORBORDER;
	bottom = MAJORBORDER;

	checkBox = NewCheckBox(left, right, bottom, top, "Show grid",
			GetPredicate(spacePanel, SHOWGRID));
	PrefixList(contents, checkBox);
	SetVar(checkBox, PARENT, panel);
	SetVar(checkBox, REPOBJ, spacePanel);
	SetMethod(checkBox, CHANGEDVALUE, ChangeShowGrid);
    }
    return (ObjPtr) controlWindow;
}

static ObjPtr ChangeSpaceTools(toolGroup)
ObjPtr toolGroup;
/*Changes a space's tools based on toolGroup*/
{
    ObjPtr value;
    int oldValue, newValue, index;
    ObjPtr space;
    ObjPtr contents;
    ObjPtr var;
    ThingListPtr runner;

    space = GetObjectVar("ChangeSpaceTools", toolGroup, SPACE);
    if (!space)
    {
	return ObjFalse;
    }
    value = GetVar(space, EDITTOOL);
    if (value) 
    {
	oldValue = GetInt(value);
    }
    else
    {
	oldValue = -1;
    }

    value = GetValue(toolGroup);
    if (value) 
    {
	index = GetInt(value);
    }
    else
    {
	index = 0;
    }

    newValue = ST_FINGER;
    contents = GetListVar("ChangeSpaceTools", toolGroup, CONTENTS);
    runner = LISTOF(contents);
    while (runner)
    {
	if (!index)
	{
	    /*Found it*/
	    var = GetVar(runner -> thing, EDITTOOL);
	    if (var)
	    {
		newValue = GetInt(var);
	    }
	    break;
	}
	--index;
	runner = runner -> next;
    }

    if (oldValue == newValue)
    {
	return ObjFalse;
    }

    if (oldValue == ST_FLYING)
    {
	/*Transition from flying*/
	ObjPtr observer;
	ObjPtr var;
	real forward[3], up[3];

	observer = GetVar(space, OBSERVER);
	if (observer)
	{
	    /*Incorporate changes*/
	    GetAdjustedVectors(forward, up, observer);
	    var = NewRealArray(1, 3L);
	    CArray2Array(var, forward);
	    SetVar(observer, FORWARDVECTOR, var);

	    var = NewRealArray(1, 3L);
	    CArray2Array(var, up);
	    SetVar(observer, UPVECTOR, var);

	    SetVar(observer, ROLL, NULLOBJ);
	    SetVar(observer, PITCH, NULLOBJ);
	    SetVar(observer, AIRSPEED, NULLOBJ);

	    LogObserver(observer);
	}
    }

    if (newValue == ST_FLYING)
    {
	/*Transition from flying*/
	ObjPtr observer;
	ObjPtr var;

	observer = GetVar(space, OBSERVER);
	if (observer)
	{
	    /*Incorporate changes*/
	    SetVar(observer, ROLL, NewReal(0.0));
	    SetVar(observer, PITCH, NewReal(0.0));
	    SetVar(observer, AIRSPEED, NewReal(4.0 * AIRSPEEDFACTOR));

	    LogObserver(observer);
	}
    }

    SetVar(space, EDITTOOL, NewInt(newValue));
    return ObjTrue;
}

ObjPtr ReshapeVisWindow(window, ol, or, ob, ot, nl, nr, nb, nt)
ObjPtr window;
int ol, or, ob, ot;
int nl, nr, nb, nt;
/*Reshapes vis window given that ol, or, ob, and ot was the old
  viewport.  Does not redraw anything.*/
{
    ObjPtr contents;

    /*DIKEO reshape the space window, if any*/

    contents = GetVar(window, CONTENTS);
    if (contents && IsList(contents))
    {
	ReshapeList(LISTOF(contents),
		0, or - ol, 0, ot - ob,
		0, nr - nl, 0, nt - nb);
    }   
    return ObjTrue;
}

#ifdef CHANGECURSOR
static Bool metering;
static Bool pointCurrently;
static real meterX, meterY, meterZ;
static ObjPtr meterObject;

static ObjPtr IdleVisWindowCursor(visWindow, x, y)
ObjPtr visWindow;
int x, y;
/*Idles a vis window with a cursor inside*/
{
#ifdef GRAPHICS
    ObjPtr space, var;
    int left, right, bottom, top;
    ObjPtr contents;
    ThingListPtr runner;
    FuncTyp method;

    space = GetVar(visWindow, SPACE);
    if (!space)
    {
	return ObjFalse;
    }

    var = GetVar(space, EDITTOOL);
    if (!var)
    {
	return ObjFalse;
    }

    if (GetInt(var) == ST_FINGER)
    {
	FuncTyp method;
	/*Have to go through the space to find where this is picked*/
	Get2DIntBounds(space, &left, &right, &bottom, &top);

	StartSpace(space, left, right, bottom, top, PICKSPACE, 0);
    
	/*Pick the contents of the space*/
	contents = GetVar(space, CONTENTS);
    
	runner = LISTOF(contents);
	while (runner)
	{
	    if (IsObject(runner -> thing))
	    {
		PickObject(runner -> thing, PR_CENTER);
		method = GetMethod(runner -> thing, PICKPOINT);
		if (method)
		{
		    (*method)(runner -> thing);
		}
	    }
	    runner = runner -> next;
	}
    
	StopSpace(PICKSPACE);
    
	if (spacePickedObject && (bestPickVertex >= 0))
	{
	    /*There is a vertex to highlight*/
	    method = GetMethod(spacePickedObject,
			    BEGINSTUFFING);
	    if (method)
	    {
		(*method)(spacePickedObject);
		method = GetMethod(spacePickedObject,
		    STUFFSELPOINT);
		if (method)
		{
		    real px, py, pz;
		    real xnew, ynew, znew, wnew;

		    Matrix projMatrix, viewMatrix, curMatrix;
		    (*method)(spacePickedObject,
			    bestPickVertex, &px, &py, &pz);

		    StartSpace(space, left, right, bottom, top, OVERDRAWSPACE, 0);
		    mmode(MPROJECTION);
		    getmatrix(projMatrix);
		    mmode(MVIEWING);
		    getmatrix(viewMatrix);
		    MULTMATRIX(viewMatrix, projMatrix, curMatrix);
		    StopSpace(OVERDRAWSPACE);

		    xnew = px * curMatrix[0][0] + py * curMatrix[1][0] +
			   pz * curMatrix[2][0] +      curMatrix[3][0];

		    ynew = px * curMatrix[0][1] + py * curMatrix[1][1] +
			   pz * curMatrix[2][1] +      curMatrix[3][1];

		    znew = px * curMatrix[0][2] + py * curMatrix[1][2] +
			   pz * curMatrix[2][2] +      curMatrix[3][2];

		    wnew = px * curMatrix[0][3] + py * curMatrix[1][3] +
			   pz * curMatrix[2][3] +      curMatrix[3][3];

		    xnew /= wnew;
		    ynew /= wnew;
		    znew /= wnew;
		    SetSubPort(left, right, bottom, top);
		    mmode(MPROJECTION);
		    ortho2(-1.0, 1.0, -1.0, 1.0);
		    mmode(MVIEWING);
		    loadmatrix(Identity);
		    OverDraw(true);
		    EraseAll();
		    SetUIColor(UIWHITE);
		    DrawSpaceLine(-1.0, ynew, 0.0, 1.0, ynew, 0.0);
		    DrawSpaceLine(xnew, -1.0, 0.0, xnew, 1.0, 0.0);
		    OverDraw(false);
		    RestoreSubPort();
		}
	    }
	}
	else
	{
	    SetSubPort(left, right, bottom, top);
	    OverDraw(true);
	    EraseAll();
	    OverDraw(false);
	    RestoreSubPort();
	}
    }
#endif
    return ObjTrue;
}

static ObjPtr EnterVisWindow(visWindow)
ObjPtr visWindow;
/*Enters a vis window*/
{
    metering = true;
    pointCurrently = false;
}

static ObjPtr LeaveVisWindow(visWindow)
ObjPtr visWindow;
/*Leaves a vis window*/
{
    if (metering && pointCurrently)
    {
	OverDraw(true);
	EraseAll();
	OverDraw(false);
    }
    metering = false;
}
#endif

WinInfoPtr NewVisWindow(title, flags)
char *title;
long flags;
/*Creates a new visualization window with title title and flags flags*/
{
    WinInfoPtr visWindow;
    WinInfoPtr spaceWindow;
    ObjPtr space, panel, corral, contents, panelContents;
    ObjPtr button, radioGroup;
    int l, r, b, t;
    ObjPtr lights;
    ThingListPtr runner;
    ObjPtr spaceContents;

    visWindow = NewObjWindow(visWindowClass, title, 
	rgbGoodForUI ? flags : flags & ~WINRGB,
	VWINWIDTH, VWINHEIGHT, SCRWIDTH, SCRHEIGHT);

#ifdef CHANGECURSOR
    SetMethod((ObjPtr) visWindow, IDLECURSOR, IdleVisWindowCursor);
    SetMethod((ObjPtr) visWindow, ENTERCURSOR, EnterVisWindow);
    SetMethod((ObjPtr) visWindow, LEAVECURSOR, LeaveVisWindow);
#endif

    SetMinSize(visWindow, 100, 100);

    SetVar((ObjPtr) visWindow, HIDEPANEL, ObjTrue);
    DefineMenuItem((ObjPtr) visWindow, WINDOWSMENU, "Show Front Panel Controls", DoShowFrontPanelControls);
    DefineMenuItem((ObjPtr) visWindow, WINDOWSMENU, "Show Back Panel Controls", DoShowBackPanelControls);
    DefineMenuItem((ObjPtr) visWindow, WINDOWSMENU, "Show Space Controls", DoShowSpaceControls);
    DefineMenuItem((ObjPtr) visWindow, WINSIZEMENU, "Tile Full Screen", DoTileFullScreen);
    DefineMenuItem((ObjPtr) visWindow, WINSIZEMENU, "Tile Video Screen", DoTileVideoScreen);
    DefineMenuItem((ObjPtr) visWindow, WINSIZEMENU, "PHSCologram Screen", DoPhscoScreen);
    DefineMenuItem((ObjPtr) visWindow, TEXTMENU, "", 0);

    contents = GetVar((ObjPtr) visWindow, CONTENTS);
    ContentsExpectWindowSize(visWindow, VWINWIDTH, VWINHEIGHT);

    /*Create the space and its associated panels*/
    l = VWINTOOLPWIDTH;
    r = VWINWIDTH - VWINPANELWIDTH;
    b = 0;
    t = VWINHEIGHT;

    if (rgbGoodForUI)
    {
	spaceWindow = visWindow;
    }
    else
    {
	char newName[300];
	int x, y;
	ObjPtr var;
	real margins[4];
	ObjPtr panelContents, textBox;

	GetWindowOrigin(&x, &y);
	spaceWindow = NewObjWindow(spaceWindowClass, title,
		flags | WINFIXEDLOC | WINRGB | WINNOFRAME,
		r - l, t - b, x + l, y);

	SetVar((ObjPtr) visWindow, SPACEWINDOW, (ObjPtr) spaceWindow);
	var = NewList();
	PrefixList(var, (ObjPtr) spaceWindow);
	SetVar((ObjPtr) visWindow, SUBWINDOWS, var);
	SetVar((ObjPtr) spaceWindow, SUPERWINDOW, (ObjPtr) visWindow);
	margins[0] = (real) (l);
	margins[1] = (real) (VWINWIDTH - r);
	margins[2] = 0.0;
	margins[3] = 0.0;
	var = NewRealArray(1, 4L);
	CArray2Array(var, margins);
	SetVar((ObjPtr) spaceWindow, SUBWINDOWMARGIN, var);

	/*Make a dummy panel to put here*/
	panel = NewPanel(greyPanelClass, l, r, b, t);
	if (!panel)
	{
	    return (WinInfoPtr) 0;
	}
	SetVar(panel, PARENT, (ObjPtr) visWindow);
	PrefixList(contents, panel);
	panelContents = GetVar(panel, CONTENTS);
	
	r = r - l;
	l = 0;
	t = t - b;
	b = 0;

	/*And a warning text box*/
	textBox = NewTextBox(MAJORBORDER, r - MAJORBORDER, MAJORBORDER,
		t - MAJORBORDER, 0, "Yaha",
"In normal operation, you should never see this message.  If you can read this, it means \
that something has gone wrong with the way SciAn is stacking its windows.  This is most likely due \
to an X windows operation on the windows that SciAn cannot detect.\n\n\
You can probably fix the \
problem for the time being simply by clicking in the title bar of this window \
to pop it to the top.\n\n\
Please report this bug to scian-bugs@scri.fsu.edu.");
	PrefixList(panelContents, textBox);
	SetVar(textBox, PARENT, panel);
    }
    spaceContents = GetVar((ObjPtr) spaceWindow, CONTENTS);

    /*Add in the panel behind the space*/
    panel = NewPanel(spaceBackPanelClass, l, r, b, t);
    if (!panel)
    {
	return (WinInfoPtr) 0;
    }
    SetVar(panel, NAME, ConcatStrings(NewString(title), NewString(" Back Panel")));
    SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    SetVar(panel, TYPESTRING, NewString("panel"));
    SetVar(panel, HELPSTRING, NewString("This panel holds 2-dimensional objects \
and is always drawn behind the space."));
    SetVar(panel, OWNERWINDOW, (ObjPtr) spaceWindow);
#ifdef INTERACTIVE
    SetMethod(panel, PRESS, PressSpacePanel);
#endif
#ifndef BOCKA
    PrefixList(spaceContents, panel);
#endif
    
    /*Add in a space*/
    space = NewSpace(l, r, b, t);
    if (!space)
    {
	return (WinInfoPtr) 0;
    }
    SetVar(space, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    SetVar(space, NAME, ConcatStrings(NewString(title), NewString(" Space")));
    SetVar(space, OWNERWINDOW, (ObjPtr) spaceWindow);
    PrefixList(spaceContents, space);
    SetVar(space, PARENT, (ObjPtr) spaceWindow);

    /*Notify the space that the panel is behind the space*/
    SetVar(panel, SPACE, space);
    SetVar(space, BACKPANEL, panel);
    SetVar(panel, PARENT, space);
    SetMethod(panel, NEWCTLWINDOW, ShowBackSpacePanelControls);
    SetMethod(panel, SHOWCONTROLS, NewControlWindow);

    /*Add in the panel to cover the space*/
    panel = NewPanel(spacePanelClass,  l, r, b, t);
    if (!panel)
    {
	return (WinInfoPtr) 0;
    }
    SetVar(panel, NAME, ConcatStrings(NewString(title), NewString(" Front Panel")));
    SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    SetVar(panel, TYPESTRING, NewString("panel"));
    SetVar(panel, HELPSTRING, NewString("This panel holds 2-dimensional objects \
and is always drawn in front of the space."));
    SetVar(panel, OWNERWINDOW, (ObjPtr) spaceWindow);
    SetVar(panel, SPACE, space);
#ifdef INTERACTIVE
    SetMethod(panel, PRESS, PressSpacePanel);
#endif
#ifndef BOCKA
    PrefixList(spaceContents, panel);
#endif
    SetVar(panel, PARENT, space);

    /*Notify the space that the panel covers the space*/
    SetVar(space, FRONTPANEL, panel);
    SetVar(panel, FRONTPANEL, ObjTrue);
    SetMethod(panel, NEWCTLWINDOW, ShowFrontSpacePanelControls);
    SetMethod(panel, SHOWCONTROLS, NewControlWindow);

    /*Give the space to (potentially) both the windows*/
    SetVar((ObjPtr) visWindow, SPACE, space);
    SetVar((ObjPtr) spaceWindow, SPACE, space);

    /*Add in a tools panel*/
    panel = NewPanel(greyPanelClass, 0, VWINTOOLPWIDTH, 0, VWINHEIGHT);
    if (!panel)
    {
	return (WinInfoPtr) 0;
    }
    SetVar(panel, STICKINESS, NewInt(STICKYLEFT + STICKYBOTTOM + STICKYTOP));
#ifndef BOCKA
    PrefixList(contents, panel);
#endif
    SetVar(panel, PARENT, (ObjPtr) visWindow);
    SetVar(space, TOOLPANEL, panel);
    SetVar(panel, TYPESTRING, NewString("panel"));
    SetVar(panel, HELPSTRING, NewString("You can show or hide this panel by choosing the Toggle Panel \
item from the Windows menu."));
    panelContents = GetVar(panel, CONTENTS);

    /*Put tools in the space tools group*/
    radioGroup = NewRadioButtonGroup("Space Tools");
    SetVar(space, TOOLGROUP, radioGroup);
    SetVar(radioGroup, HELPSTRING,
	   NewString("This is a group of tools that allow you to modify the \
space using the mouse.  To use one of these tools, click on the icon representing \
the tool before clicking in the space.  To find out what each tool does, use \
help in context on each individual button."));
    SetVar(radioGroup, PARENT, panel);
    PrefixList(panelContents, radioGroup);
    SetVar(radioGroup, HALTHELP, ObjTrue);
    SetVar(radioGroup, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));

    /*First the 3-D tools on the left*/
    l = VWTOOLBORDER;
    t = VWINHEIGHT - VWTOOLBORDER;
 
    /*Motion/rotation tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONMOVEROTATE, UIYELLOW, "Space Selection", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the space and space panel selection tool.\n\
\n\
Click and drag within the space using the center mouse button to rotate \
the entire space around the focus point of the observer at the center of the \
space.  The space can be rotated around any axis.  Hold down the Shift key \
to constrain to rotation around a principal axis of the space.  Double-click \
to snap the space to the nearest straight-on orientation.  \
If Space Rotation Guides is turned on in the space control panel, you will see the \
virtual trackball as a wire frame sphere.  If Rotation Inertia \
is turned on in the space control panel, the space will continue to rotate if you \
give it a spin and let go while moving the mouse.\n\
\n\
Click on an object within the space to select it.  Drag after clicking to move \
the objects in the space.  If you hold down the shift key while dragging, motion \
will be constrained to one principal axis of the space.\n\
\n\
Both rotation and motion will affect other spaces controlled by the same observer.  This \
is because rotation and motion actually change the position of the observer \
within the space.\n\
\n\
Click on a 2-D object in the front or back panel, such as a text box or line, \
to select it.  Once an object is selected, an outline with handles will appear \
around the object.  You can click and drag the handled to move and resize the \
object.  You can also edit text within text objects by clicking and dragging."));
    SetVar(button, EDITTOOL, NewInt(ST_FINGER));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;

    /*Flight tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONPLANE, UIYELLOW, "Flight Simulator", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the space flight \
simulator.  When this tool is chosen, you can fly through the space like an airplane \
using the mouse.  This works best when the window is resized to fill the entire \
screen.  It is also a good idea to give the observer a wide field of view from \
within the observer control panel.\n\
\n\
To start flying, click the left mouse button in the space.  Pull the mouse back \
toward you to climb and push it forward to dive.  Move the mouse to the left or right \
to turn.  To accelerate, press the up arrow key repeatedly.  To decelerate, press the \
down arrow key.  When you are done flying, press the left mouse button once more \
in the space."));
    SetVar(button, EDITTOOL, NewInt(ST_FLYING));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;

#if 0
    /*3-D Finger Tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICON3DFINGER, UIYELLOW, "Space Selection", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the space \
selection tool.  Use the left mouse button with this tool to select \
visualization objects within the space.\n"));
    SetVar(button, EDITTOOL, NewInt(ST_3DFINGER));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
#endif

#if 0
    /*Meter tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONMETER, UIYELLOW, "Data Meter", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the data meter \
tool, which is not implemented yet.\n"));
    SetVar(button, EDITTOOL, NewInt(ST_METER));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
#endif

#if 0
    /*Now the 3-D tools on the right*/
    l = 2 * VWTOOLBORDER + SMALLICONBUTTONSIZE;
    t = VWINHEIGHT - VWTOOLBORDER;

    /*2-D finger tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONFINGER, UIYELLOW, "Panel Selection", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the space panel selection tool.\
Use this tool to select and move objects on the front or back panels of the space."));
    SetVar(button, EDITTOOL, NewInt(ST_FINGER));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
#endif

    /*Annotation tool*/
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONANNOTATION, UIYELLOW, "Draw Annotation", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the annotation drawing tool.  \
Use this button to drag out annotation boxes on the panels of the space.  \
Normally this tool draws on the front panel.  Hold down the Alt or Ctrl key while drawing to draw \
on the back panel.  When the box has been drawn, enter text by typing.  The appearance of the boxes \
can be modified by selecting them and choosing Show Controls \
from the Objects menu."));
    SetVar(button, EDITTOOL, NewInt(ST_ANNOTATION));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONRECTANGLE, UIYELLOW, "Draw Rectangle", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the rectangle drawing tool.  \
Use this button to drag out rectangular boxes on the panels of the space.  \
Normally this tool draws on the front panel.  Hold down the Alt or Ctrl key while drawing to draw \
on the back panel.  The appearance of the boxes can be modified by selecting them and choosing Show Controls \
from the Objects menu."));
    SetVar(button, EDITTOOL, NewInt(ST_RECTANGLE));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONLINE, UIYELLOW, "Draw Line", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the line drawing tool.  \
Use this button to drag out lines on the panels of the space.  \
Normally this tool draws on the front panel.  Hold down the Alt or Ctrl key while drawing to draw \
on the back panel.  The appearance of the lines can be modified by selecting them and choosing Show Controls \
from the Objects menu.  You can also add arrowheads from within this control panel."));
    SetVar(button, EDITTOOL, NewInt(ST_LINE));

    t = t - SMALLICONBUTTONSIZE - VWTOOLBORDER;
    button = NewIconButton(l, l + SMALLICONBUTTONSIZE,
			t - SMALLICONBUTTONSIZE, t,
			ICONCLOCK, UIYELLOW, "Draw Time Readout", BS_PLAIN);
    SetVar(button, STICKINESS, NewInt(STICKYLEFT + STICKYTOP));
    AddRadioButton(radioGroup, button);
    SetVar(button, HELPSTRING, NewString("This button chooses the time readout drawing tool.  \
Use this button to drag out time readouts on the panels of the space.  The time readout is \
a text box which always shows the current time in the space.  To change the \
format in which the time is displayed, show the control panel by \
selecting the readout and choosing Show Controls from the Objects menu.  \
Normally this tool draws on the front panel.  Hold down the Alt or Ctrl key while drawing to draw \
on the back panel."));
    SetVar(button, EDITTOOL, NewInt(ST_TIME_READOUT));

    /*Give the radio group a default value*/
    SetValue(radioGroup, NewInt(ST_FINGER));
    SetVar(space, EDITTOOL, NewInt(ST_FINGER));
    SetVar(radioGroup, SPACE, space);
    SetMethod(radioGroup, CHANGEDVALUE, ChangeSpaceTools);

    /*Add in an object control panel*/
    panel = NewPanel(greyPanelClass, VWINWIDTH - VWINPANELWIDTH, VWINWIDTH, 
	0, VWINHEIGHT);
    if (!panel)
    {
	return (WinInfoPtr) 0;
    }
    SetVar(panel, STICKINESS, NewInt(STICKYRIGHT + STICKYBOTTOM + STICKYTOP));
#ifndef BOCKA
    PrefixList(contents, panel);
#endif
    SetVar(panel, PARENT, (ObjPtr) visWindow);
    SetVar(space, OBJPANEL, panel);
    SetVar(panel, TYPESTRING, NewString("panel"));
    SetVar(panel, HELPSTRING, NewString("You can show or hide this panel by choosing the Toggle Panel \
item from the Windows menu."));

    contents = GetVar(panel, CONTENTS);
    if (!contents)
    {
	return (WinInfoPtr) 0;
    }

    /*Add in an icon corral*/
    corral = NewIconCorral(visCorralClass,
		MINORBORDER, VWINPANELWIDTH - MINORBORDER, 
		MINORBORDER,
		VWINHEIGHT - MINORBORDER,
		BARRIGHT + BARBOTTOM);

    if (!corral)
    {
	return (WinInfoPtr) 0;
    }
    SetVar((ObjPtr) visWindow, CORRAL, corral);

    SetVar(corral, NAME, NewString("Space Contents"));
    SetVar(corral, HELPSTRING,
	NewString("This corral shows the objects and space controllers present \
in this visualization window.  You can drag new datasets from the datasets window \
into this corral to visualize them along with the other objects.  You can also \
drag in observer, renderer, clock, and lights icons.  There can only be one \
observer, renderer, and clock in a space, so dragging in these will replace the \
existing controller.\n"));
    SetVar(corral, STICKINESS, NewInt(STICKYLEFT + STICKYRIGHT +
				     STICKYBOTTOM + STICKYTOP));
    PrefixList(contents, corral);
    SetVar(corral, PARENT, panel);

    /*Splice the space to the corral*/
    SetVar(corral, SPACE, space);
    SetVar(space, CORRAL, corral);

    /*Add an observer*/
    AddControllerToSpace(NewObserver(), space, corral, NULLOBJ);

    /*Add a renderer*/
    AddControllerToSpace(NewRenderer(), space, corral, NULLOBJ);

    /*Add some lights*/
#ifdef GRAPHICS
    if (hasRGB)
#endif
    {
	lights = NewLights();
	runner = LISTOF(lights);
	while (runner)
	{
	    AddControllerToSpace(runner -> thing, space, corral, NULLOBJ);
	    runner = runner -> next;
	}
    }

    /*Add a clock*/
    AddControllerToSpace(NewClock(), space, corral, NULLOBJ);

    return visWindow;
}

ObjPtr FindSpace(win)
WinInfoPtr win;
/*Finds the space in win*/
{
    return GetVar((ObjPtr) win, SPACE);
}

ObjPtr FindObserver(win)
WinInfoPtr win;
/*Finds the observer in win*/
{
    ObjPtr space, retVal;
    retVal = GetVar((ObjPtr) win, REPOBJ);
    if (retVal && (IsObserver(retVal)))
    {
	return retVal;
    }
    space = FindSpace(win);
    if (space)
    {
	retVal = GetObjectVar("FindObserver", space, OBSERVER);
    }
    else
    {
	ReportError("FindObserver", "No space in window");
    }
    return retVal;
}

#ifdef PROTO
void RotateOrthoWindow(WinInfoPtr window, char axis, float amount)
#else
void RotateOrthoWindow(window, axis, amount)
WinInfoPtr window;
char axis;
float amount;
#endif
/*Rotates the observer in window about orthogonal axis axis by amount, and 
  stops continuous rotation.*/
{
    ObjPtr observer;
    real av[3];

    observer = FindObserver(window);
    if (!observer) return;

    switch(axis)
    {
	case 'x':
	case 'X':
	    av[0] = 1.0;
	    av[1] = 0.0;
	    av[2] = 0.0;
	    break;
	case 'y':
	case 'Y':
	    av[0] = 0.0;
	    av[1] = 1.0;
	    av[2] = 0.0;
	    break;
	case 'z':
	case 'Z':
	    av[0] = 0.0;
	    av[1] = 0.0;
	    av[2] = 1.0;
	    break;
	default:
	    ReportError("RotateOrthoWindow", "Bad axis");
	    return;
    }
    RotateObserver(observer, av, -amount * M_PI / 180.0, false);
}

static ObjPtr ChangeWindowToRGB(window, button)
WinInfoPtr window;
int button;
/*If button is 1, changes the color mode of window to RGB*/
{
    if (button == 1)
    {
	ObjPtr radio = NULLOBJ;
	SelWindow(window);
	if (selWinInfo)
	{
	    radio = GetVar((ObjPtr) selWinInfo, CMODERADIO);
	}
	if (radio)
	{
	    SetValue(radio, NewInt(1));
	}
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

ObjPtr SetVisWindowRGB(window)
ObjPtr window;
/*Deferred message to choose whether to make window RGB or not*/
{
#ifdef GRAPHICS
    char tempBuf[400];
    WinInfoPtr errWindow;
    WinInfoPtr spaceWindow;

    /*See if the window is already RGB*/
    spaceWindow = (WinInfoPtr) GetVar(window, SPACEWINDOW);
    if (!spaceWindow)
    {
	spaceWindow = (WinInfoPtr) window;
    }
    if (spaceWindow -> flags & WINRGB)
    {
	return;
    }

    if (!hasRGB)
    {
	return;
    }

    sprintf(tempBuf, "You will see no effects from this action in window %s \
until you change it to full color mode.  Would you like to change it now?",
	((WinInfoPtr) window) -> winTitle);

    errWindow = AlertUser(UICAUTIONALERT, (WinInfoPtr) window, tempBuf,
	ChangeWindowToRGB, 2, "Cancel", "Change");
    SetVar((ObjPtr) errWindow, HELPSTRING,
	NewString("Visualization windows have two color modes: full color and color map.  \
Full color specifies full 24-bit RGB color.  Color map specifies mapped colors using a color \
table.  Some features, such as lighting, only work on windows set to full color mode.  \
If you click on the Change button, the affected window will automatically be set \
to this mode."));
#endif
}

void InitVisWindows()
/*Initializes the vis windows*/
{
    visualizeAsList = NewList();
    AddToReferenceList(visualizeAsList);

    visWindowClass = NewObject(objWindowClass, 0);
    AddToReferenceList(visWindowClass);
    SetVar(visWindowClass, CLASSID, NewInt(CLASS_VISWINDOW));
    SetVar(visWindowClass, HELPSTRING,
	NewString("This is a visualizaton window.  On the right is a panel which \
shows the visualization objects shown in the window, the controllers which \
affect the appearance of the object, and some buttons which affect the window \
as a whole or the selected icons.  If this panel is not shown, it can be brought \
back with the Toggle Panel item in the Window menu.  On the left is a \
3-dimensional space in which \
the visualization object are shown.  There is one clear panel in front of the and \
one clear panel behind the space.  These panels are used to hold 2-dimensional \
objects such as clock readouts and text boxes, which can be created and modified \
with items in the Text menu.")); 
#if 0
    /*DIKEO I have decided to give up the RGB message*/
    SetMethod(visWindowClass, SETRGBMESSAGE, SetVisWindowRGB);
#endif
    SetMethod(visWindowClass, HIDEPANEL, HideVisWindowPanel);
    SetMethod(visWindowClass, SHOWPANEL, ShowVisWindowPanel);
    SetMethod(visWindowClass, RESHAPE, ReshapeVisWindow);

    spaceWindowClass = NewObject(objWindowClass, 0);
    AddToReferenceList(spaceWindowClass);
    SetVar(spaceWindowClass, HELPSTRING,
	NewString("This is a visualizaton window.  On the right is a panel which \
shows the visualization objects shown in the window, the controllers which \
affect the appearance of the object, and some buttons which affect the window \
as a whole or the selected icons.  If this panel is not shown, it can be brought \
back with the Toggle Panel item in the Window menu.  On the left is a \
3-dimensional space in which \
the visualization object are shown.  There is one clear panel in front of the and \
one clear panel behind the space.  These panels are used to hold 2-dimensional \
objects such as clock readouts and text boxes, which can be created and modified \
with items in the Text menu.")); 

    /*Class for the corral in a vis window*/
    visCorralClass = NewObject(corralClass, 0);
    AddToReferenceList(visCorralClass);
    SetMethod(visCorralClass, DROPINCONTENTS, DropInVisCorral);
}

void KillVisWindows()
/*Kills the vis windows*/
{
    DeleteThing(visCorralClass);
    DeleteThing(spaceWindowClass);
    DeleteThing(visWindowClass);
    DeleteThing(visualizeAsList);
}
