/*ScianObjWindows.c
  Object window stuff
  Eric Pepke
  March 28, 1990
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianLists.h"
#include "ScianWindows.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianObjWindows.h"
#include "ScianErrors.h"
#include "ScianDraw.h"
#include "ScianControls.h"
#include "ScianSpaces.h"
#include "ScianArrays.h"
#include "ScianScripts.h"
#include "ScianVisWindows.h"
#include "ScianDialogs.h"
#include "ScianIcons.h"
#include "ScianEvents.h"
#include "ScianStyle.h"
#include "ScianHelp.h"
#include "ScianObjFunctions.h"
#include "ScianLights.h"
#include "ScianPictures.h"
#include "ScianButtons.h"
#include "ScianTitleBoxes.h"
#include "ScianTextBoxes.h"
#include "ScianSliders.h"

#ifdef PROTO
void ReshapeList(ThingListPtr, int, int, int, int, int, int, int, int);
#else
void ReshapeList();
#endif

int pauseDrawing = 0;
char tempStr[TEMPSTRSIZE + 1];
ObjPtr objWindowClass;			/*Object window class*/
ObjPtr dragBuffer = NULLOBJ;		/*Objects held*/
Bool showWindows = true;		/*Show new opened windows*/

ObjPtr NewControlWindow(object)
ObjPtr object;
/*Opens a new control window for object*/
{
    FuncTyp newCtlWindow;
    ObjPtr retVal;
    ObjPtr repObj, name, repName = NULLOBJ;
    char windowName[255];

    if (IsIcon(object))
    {
	/*It's an icon, use its REPOBJ.  Do nothing if it has none.*/
	object = GetVar(object, REPOBJ);
	if (!object)
	{
	    return ObjFalse;
	}
    }

    strcpy(windowName, "");

    /*See if the object has a repObj*/
    MakeVar(object, REPOBJ);
    repObj = GetVar(object, REPOBJ);
    if (repObj)
    {
	MakeVar(repObj, NAME);
	if (repName = GetVar(repObj, NAME));
	{
	    strncpy(windowName, GetString(repName), 255);
	    windowName[255] = 0;
	}
    }
    else
    {
	/*See if the object has a main dataset*/
	MakeVar(object, MAINDATASET);
	repObj = GetVar(object, MAINDATASET);
	if (repObj)
	{
	    MakeVar(repObj, NAME);
	    if (repName = GetVar(repObj, NAME))
	    {
		strncpy(windowName, GetString(repName), 255);
		windowName[255] = 0;
	    }
	}
    }

    MakeVar(object, NAME);
    name = GetVar(object, NAME);
    if (name)
    {
	if (repName)
	{
	    strcat(windowName, " ");
	}
	strcat(windowName, GetString(name));
    }

    if (GetPredicate(object, TEMPLATEP))
    {
	strcat(windowName, " Template");
    }

    newCtlWindow = GetMethod(object, NEWCTLWINDOW);
    if (newCtlWindow)
    {
	PauseDrawing(true);
	showWindows = showControlPanels;
	retVal = (*newCtlWindow)(object, windowName);
	showWindows = true;
	PauseDrawing(false);
    }
    else
    {
	retVal = NULLOBJ;
    }
    return retVal;
}

void SetEndpoints(object, x1, y1, x2, y2)
ObjPtr object;
int x1, y1, x2, y2;
/*Sets the endpoints of an object*/
{
    int l, r, b, t;
    ObjPtr var;
    real loc[2];

    l = MIN(x1, x2) - HANDLESIZE / 2;
    r = MAX(x1, x2) + HANDLESIZE / 2;
    b = MIN(y1, y2) - HANDLESIZE / 2;
    t = MAX(y1, y2) + HANDLESIZE / 2;
    Set2DIntBounds(object, l, r, b, t);

    var = NewRealArray(1, 2L);
    loc[0] = x1;
    loc[1] = y1;
    CArray2Array(var, loc);
    SetVar(object, STARTPOINT, var);

    var = NewRealArray(1, 2L);
    loc[0] = x2;
    loc[1] = y2;
    CArray2Array(var, loc);
    SetVar(object, ENDPOINT, var);
}

Bool Get2DIntBounds(object, l, r, b, t)
ObjPtr object;
int *l, *r, *b, *t;
/*Gets the 2D integer bounds of a screen object*/
{
    ObjPtr boundsArray;
    boundsArray = GetFixedArrayVar("Get2DIntBounds", object, BOUNDS, 1, 4L);
    if (boundsArray)
    {
	real temp[4];
	Array2CArray(temp, boundsArray);
	*l = temp[0];
	*r = temp[1];
	*b = temp[2];
	*t = temp[3];
	return true;
    }
    else
    {
	return false;
    }
}

Bool Set2DIntBounds(object, l, r, b, t)
ObjPtr object;
int l, r, b, t;
/*Sets the 2D integer bounds of a screen object to l, r, b, t*/
{
    ObjPtr boundsArray;
    boundsArray = NewRealArray(1, 4L);
    if (boundsArray)
    {
	real temp[4];
	temp[0] = l;
	temp[1] = r;
	temp[2] = b;
	temp[3] = t;
	CArray2Array(boundsArray, temp);
	SetVar(object, BOUNDS, boundsArray);
	return true;
    }
    else
    {
	return false;
    }
}

void ForAllObjects(obj, routine)
ObjPtr obj;
FuncTyp routine;
/*Performs routine on all selected objects in win*/
{
    ObjPtr contents;
    FuncTyp method;

    method = GetMethod(obj, FORALLOBJECTS);
    if (method)
    {
	(*method)(obj, routine);
    }
}

static ObjPtr CloseObjWindow(theInfo)
WinInfoPtr theInfo;
/*Closes obj window theInfo*/
{  
    if (theInfo -> mainMenu)
    {
	DeleteMenus(theInfo);
    }
    theInfo -> mainMenu = 0;
    return ObjTrue;
}

#ifdef GODLIKE
static ObjPtr DrawBounds(stuff)
ObjPtr stuff;
/*Draws the bounds of stuff*/
{
    ThingListPtr runner;
    ObjPtr bounds, contents;
    if (IsList(stuff))
    {
	runner = LISTOF(stuff);
	while (runner)
	{
	    DrawBounds(runner -> thing);
	    runner = runner -> next;
	}
    }
    else
    {
	contents = GetVar(stuff, CONTENTS);
	if (contents)
	{
	    DrawBounds(contents);
	}
	bounds = GetVar(stuff, BOUNDS);
	if (bounds)
	{
	    int l, r, b, t;
	    Get2DIntBounds(stuff, &l, &r, &b, &t);
	    FrameUIRect(l, r, b, t, UIWHITE);
	}
    }
}

static ObjPtr DrawSoftBounds(stuff)
ObjPtr stuff;
/*Draws the soft bounds of stuff*/
{
    ThingListPtr runner;
    ObjPtr bounds, contents;
    if (IsList(stuff))
    {
	runner = LISTOF(stuff);
	while (runner)
	{
	    DrawSoftBounds(runner -> thing);
	    runner = runner -> next;
	}
    }
    else
    {
	contents = GetVar(stuff, CONTENTS);
	if (contents)
	{
	    DrawSoftBounds(contents);
	}
	bounds = GetVar(stuff, BOUNDS);
	if (bounds)
	{
	    int l, r, b, t;
	    ObjPtr softBounds;
	    Get2DIntBounds(stuff, &l, &r, &b, &t);
	    softBounds = GetVar(stuff, INNERSOFTMARGIN);
	    if (!softBounds)
	    {
	        FrameUIRect(l + MINORBORDER, r - MINORBORDER, b + MINORBORDER, t - MINORBORDER, UIRED);
	    }
	    softBounds = GetVar(stuff, OUTERSOFTMARGIN);
	    if (!softBounds)
	    {
	        FrameUIRect(l - MINORBORDER, r + MINORBORDER, b - MINORBORDER, t + MINORBORDER, UIRED);
	    }
	}
    }
}

static void SaveSubTemplate(stuff, file)
ObjPtr stuff;
FILE *file;
/*Saves a subtemplate for stuff and its contents into file*/
{
    ThingListPtr runner;
    ObjPtr bounds, contents, name;
    if (IsList(stuff))
    {
	runner = LISTOF(stuff);
	while (runner)
	{
	    SaveSubTemplate(runner -> thing, file);
	    runner = runner -> next;
	}
    }
    else
    {
	contents = GetVar(stuff, CONTENTS);
	if (contents)
	{
	    SaveSubTemplate(contents, file);
	}
	bounds = GetVar(stuff, BOUNDS);
	if (bounds)
	{
	    int l, r, b, t;
	    Get2DIntBounds(stuff, &l, &r, &b, &t);
	    name = GetVar(stuff, NAME);
	    if (name)
	    {
		fprintf(file, "\t{\"%s\", %d, %d, %d, %d},\n",
			GetString(name), l, r, b, t);
	    }
	}
    }
}

static ObjPtr SaveTemplateReally(owner, reply)
ObjPtr owner;
char *reply;
/*Really saves a template named reply*/
{
	FILE *file;
	char fileName[200];
	ObjPtr contents;
	contents = GetVar(GetVar(owner, OWNERWINDOW), CONTENTS);

	sprintf(fileName, "%s.tpl", reply);
	file = fopen(fileName, "w");
	if (file)
	{
	    fprintf(file, "Template %sTemplate[] =\n    {\n", reply);
	    SaveSubTemplate(contents, file);
	    fprintf(file, "\t\{\"\", 0, 0, 0, 0}\n");
	    fprintf(file, "    };\n\n");
	    fclose(file);
	}
}

static void DoSaveTemplate()
/*Saves a template for the current window*/
{
    if (selWinInfo)
    {
	char *s;
	WinInfoPtr curWindow, alertWindow;

	/*Figure out the name for the template*/
	strcpy(tempStr, selWinInfo -> winTitle);
	s = tempStr;

	while (*s)
	{
	    if (*s == ' ') *s = '_';
	    ++s;
	}
	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Enter template name:", (FuncTyp) SaveTemplateReally, tempStr);
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static ObjPtr AddObjToWindow(object, dialog)
ObjPtr object;
ObjPtr dialog;
{
    ObjPtr contents, panel;
    contents = GetVar(GetVar(dialog, OWNERWINDOW), CONTENTS);

    panel = LISTOF(contents) -> thing;
    if (panel)
    {
	contents = GetVar(panel, CONTENTS);
	PrefixList(contents, object);
	SetVar(object, PARENT, panel);
	ImInvalid(object);
    }
}

static ObjPtr NewScreenSlider(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new slider*/
{
    ObjPtr object;

    object = NewSlider(10, 10 + SLIDERWIDTH, 10, 100, PLAIN, name);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenScaleSlider(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new slider*/
{
    ObjPtr object;

    object = NewSlider(10, 10 + SLIDERWIDTH, 10, 100, SCALE, name);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenTitleBox(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new title box*/
{
    ObjPtr object;

    object = NewTitleBox(10, 100, 10, 70, name);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenRadioButton(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new radio button*/
{
    ObjPtr object;

    object = NewRadioButton(10, 100, 10, 10 + CHECKBOXHEIGHT, name);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenCheckBox(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new check box*/
{
    ObjPtr object;

    object = NewCheckBox(10, 100, 10, 10 + CHECKBOXHEIGHT, name, false);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenTextBox(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new text box*/
{
    ObjPtr object;

    object = NewTextBox(10, 100, 10, 10 + TEXTBOXHEIGHT, 0, name, name);
    AddObjToWindow(object, dialog);
}

static ObjPtr NewScreenEditableTextBox(dialog, name)
ObjPtr dialog;
char *name;
/*Makes a new text box*/
{
    ObjPtr object;

    object = NewTextBox(10, 100, 10, 10 + EDITBOXHEIGHT, EDITABLE + WITH_PIT + ONE_LINE, name, name);
    AddObjToWindow(object, dialog);
}

static void DoNewScreenSlider()
/*Makes a new slider in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenSlider, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenScaleSlider()
/*Makes a new scale slider in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenScaleSlider, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenTitleBox()
/*Makes a new title box in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenTitleBox, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenRadioButton()
/*Makes a new radio button in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenRadioButton, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenCheckBox()
/*Makes a new check box in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenCheckBox, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenTextBox()
/*Makes a new text box in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenTextBox, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

static void DoNewScreenEditableTextBox()
/*Makes a new text box in the current window*/
{
    if (selWinInfo)
    {
	WinInfoPtr curWindow, alertWindow;

	curWindow = selWinInfo;
	alertWindow = AskUser((WinInfoPtr) selWinInfo,
		"Please name the new object:", (FuncTyp) NewScreenEditableTextBox, "");
	SetVar((ObjPtr) alertWindow, OWNERWINDOW, (ObjPtr) curWindow);
    }
}

#endif

static ObjPtr DrawObjWindow(theInfo)
WinInfoPtr theInfo;
/*Draws the obj window theInfo*/
{
#ifdef GRAPHICS
    ObjPtr contents;

    /*Draw the objects within the window*/
    contents = GetVar((ObjPtr) theInfo, CONTENTS);
    if (contents)
    {
	if (IsList(contents))
	{
	    DrawList(contents);
	}
    }
#ifdef GODLIKE
    if (GetPredicate((ObjPtr) theInfo, SHOWBOUNDS))
    {
	DrawBounds(contents);
#if 0
	setlinestyle(DOTTEDLINE);
	DrawSoftBounds(contents);
	setlinestyle(SOLIDLINE);
#endif
    }
#endif
#endif
    return ObjTrue;
}

#ifdef PROTO
ObjPtr BoundsInvalid(ObjPtr object)
#else
ObjPtr BoundsInvalid(object)
ObjPtr object;
#endif
/*For an object, tests to see if it needs drawing.  Returns
  NULLOBJ	if it does not
  array[4]	giving bounds if it does
  ObjTrue	it it needs to be redrawn but does not know where
*/
{
    ObjPtr picVar;
    ObjPtr contents;
    ObjPtr myBounds;
    FuncTyp method;

    method = GetMethod(object, BOUNDSINVALID);
    if (method)
    {
	return (*method)(object);
    }

    MakeVar(object, APPEARANCE);

    picVar = GetVar(object, PICSTATE);
    if (picVar && GetInt(picVar) != FGBG)
    {
	/*Object is not good, so return the bounds*/
	MakeVar(object, BOUNDS);
	myBounds = GetVar(object, BOUNDS);
	if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
		&& DIMS(myBounds)[0] == 4)
	{
	    return myBounds;
	}
	else
	{
	    return ObjTrue;
	}
    }

    MakeVar(object, CONTENTS);
    contents = GetVar(object, CONTENTS);
    if (contents && IsList(contents))
    {
	/*Still, maybe some of the contents need to be drawn*/
	Bool firstTime;
	real boundsElements[4];
	real testElements[4];
 	ObjPtr test;
 	ThingListPtr runner;
	Bool doubleNoBounds = false;

	MakeVar(object, BOUNDS);
	myBounds = GetVar(object, BOUNDS);

	firstTime = true;
	runner = LISTOF(contents);
	while (runner)
	{
	    test = BoundsInvalid(runner -> thing);
	    if (test)
	    {
		/*Hey, the kid needs redrawing*/
		if (IsRealArray(test))
		{
		    /*It has a bounds*/
		    if (firstTime)
		    {
			Array2CArray(boundsElements, test);
		    }
		    else
		    {
			Array2CArray(testElements, test);
			if (testElements[0] < boundsElements[0])
				boundsElements[0] = testElements[0];
			if (testElements[1] > boundsElements[1])
				boundsElements[1] = testElements[1];
			if (testElements[2] < boundsElements[2])
				boundsElements[2] = testElements[2];
			if (testElements[3] > boundsElements[3])
				boundsElements[3] = testElements[3];
		    }
		}
		else
		{
		    /*It doesn't have a bounds*/
		    if (firstTime)
		    {
			/*Try to use my bounds*/
			if (myBounds && IsArray(myBounds) && RANK(myBounds) == 1
				&& DIMS(myBounds)[0] == 4)
			{
			    Array2CArray(boundsElements, myBounds);
			}
			else
			{
			    doubleNoBounds = true;
			}
		    }
		}
		firstTime = false;
	    }
	    runner = runner -> next;
	}

	/*Now return the bounds*/
	if (firstTime != true)
	{
	    if (doubleNoBounds)
	    {
		return ObjTrue;
	    }
	    else
	    {
		ObjPtr retVal;
		retVal = NewRealArray(1, 4L);
		CArray2Array(retVal, boundsElements);
		return retVal;
	    }
	}
    }

    return NULLOBJ;
}

ObjPtr MakeObjWindowDrawn(curWindow)
WinInfoPtr curWindow;
/*Method to make an obj window drawn.  Returns true iff drawing done*/
{
    ObjPtr boundsInvalid;

    boundsInvalid = BoundsInvalid((ObjPtr) curWindow);
    if (boundsInvalid)
    {
	if (IsArray(boundsInvalid))
	{
	    real elements[4];
	    Array2CArray(elements, boundsInvalid);
	    RestrictedDrawing((int) elements[0], (int) elements[1], (int) elements[2], (int) elements[3]);
	    DrawWindowInfo(curWindow);
	    UnrestrictedDrawing();
	}
	else
	{
	    DrawWindowInfo(curWindow);
	}
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

ObjPtr PressObjWindow(theInfo, x, y, flags)
WinInfoPtr theInfo;
int x, y;
long flags;
/*Does a press in object window theInfo*/
{
#ifdef INTERACTIVE
    ObjPtr contents;

    /*Press in the objects within the window*/
    contents = GetVar((ObjPtr) theInfo, CONTENTS);
    if (contents)
    {
	if (IsList(contents))
	{
	    return PressObject(contents, x, y, flags);
	}
    }
#endif
    return ObjTrue;
}

static ObjPtr DropObjWindow(window, object, x, y)
ObjPtr window, object;
int x, y;
/*Does a drop in object window theInfo*/
{
    ObjPtr contents;

    /*Press in the objects within the window*/
    contents = GetVar(window, CONTENTS);
    if (contents)
    {
	if (IsList(contents))
	{
	    return DropList(contents, object, x, y);
	}
    }
    return ObjTrue;
}

static ObjPtr KeyDownObjWindow(theInfo, theKey, flags)
WinInfoPtr theInfo;
int theKey;
long flags;
/*Does a key down in object window theInfo*/
{
    ObjPtr contents;

    /*Press in the objects within the window*/
    contents = GetVar((ObjPtr) theInfo, CONTENTS);
    if (contents)
    {
	if (IsList(contents))
	{
	    KeyDownList(contents, theKey, flags);
	}
    }
    return ObjTrue;
}

void ReshapeObject(object, ol, or, ob, ot, left, right, bottom, top)
ObjPtr object;
int ol, or, ob, ot;
int left, right, bottom, top;
/*Reshapes object, which used to exist within owner with edges ol, or, ob, ot
  to one which exists within owner with edges left, right, bottom, top.*/
{
    FuncTyp method;
    method = GetMethod(object, RESHAPE);
    if (method)
    {
	(*method)(object, ol, or, ob, ot, left, right, bottom, top);
    }
    else
    {
	ObjPtr boundsArray, locArray;
	ObjPtr stickyInt;
	real bounds[4];
	real oldWidth, oldHeight;	/*Old width and height*/
	Bool sideLocked[4];		/*True iff side is locked*/
	Bool xStretch, yStretch;	/*Stretchiness in x and y*/
	int stickiness;			/*Side stickiness of the object*/
	real oldBounds[4];		/*Old bounds of the object*/
	ObjPtr contents;		/*Contents of the object, if any*/
	real wr, hr;			/*Width and height ratios*/
	Bool isIcon = false;		/*Is an icon?*/

	wr = ((real) (right - left)) / ((real) (or - ol));
	hr = ((real) (top - bottom)) / ((real) (ot - ob));

	boundsArray = GetVar(object, BOUNDS);
	if (!boundsArray || !IsRealArray(boundsArray) || RANK(boundsArray) != 1 ||
	    DIMS(boundsArray)[0] != 4)
	{
	    /*It might be an icon*/
	    locArray = GetVar(object, ICONLOC);
	    if (!locArray && !IsRealArray(locArray) || RANK(locArray) != 1 ||
		DIMS(locArray)[0] != 2)
	    {
		return;
	    }
	    isIcon = true;
	    Array2CArray(bounds, locArray);
	    bounds[3] = bounds[2] = bounds[1];
	    bounds[1] = bounds[0];
	}
	else
	{
	    Array2CArray(bounds, boundsArray);
	}
	oldBounds[0] = bounds[0];
	oldBounds[1] = bounds[1];
	oldBounds[2] = bounds[2];
	oldBounds[3] = bounds[3];

	oldWidth = bounds[1] - bounds[0];
	oldHeight = bounds[3] - bounds[2];

	/*Get the object's stickiness*/
	stickyInt = GetVar(object, STICKINESS);
	if (stickyInt && IsInt(stickyInt))
	{
	    stickiness = GetInt(stickyInt);
	}
	else
	{
	    stickiness = 0;
	}

	if ((stickiness & STICKYLEFT) || (stickiness & FLOATINGLEFT))
	{
	    if (stickiness & FLOATINGLEFT)
	    {
		bounds[0] = (bounds[0] - ol) * wr + left;
	    }
	    else
	    {
		bounds[0] += left - ol;
	    }
	    if (!((stickiness & STICKYRIGHT) || (stickiness & FLOATINGRIGHT)))
	    {
		bounds[1] = bounds[0] + oldWidth;
	    }
	}
	if ((stickiness & STICKYRIGHT) || (stickiness & FLOATINGRIGHT))
	{
	    if (stickiness & FLOATINGRIGHT)
	    {
		bounds[1] = (bounds[1] - ol) * wr + left;
	    }
	    else
	    {
		bounds[1] += right - or;
	    }
	    if (!((stickiness & STICKYLEFT) || (stickiness & FLOATINGLEFT)))
	    {
		bounds[0] = bounds[1] - oldWidth;
	    }
	}

	if ((stickiness & STICKYBOTTOM) || (stickiness & FLOATINGBOTTOM))
	{
	    if (stickiness & FLOATINGBOTTOM)
	    {
		bounds[2] = (bounds[2] - ob) * hr + bottom;
	    }
	    else
	    {
		bounds[2] += bottom - ob;
	    }
	    if (!((stickiness & STICKYTOP) || (stickiness & FLOATINGTOP)))
	    {
		bounds[3] = bounds[2] + oldHeight;
	    }
	}
	if ((stickiness & STICKYTOP) || (stickiness & FLOATINGTOP))
	{
	    if (stickiness & FLOATINGTOP)
	    {
		bounds[3] = (bounds[3] - ob) * hr + bottom;
	    }
	    else
	    {
		bounds[3] += top - ot;
	    }
	    if (!((stickiness & STICKYBOTTOM) || (stickiness & FLOATINGBOTTOM)))
	    {
		bounds[2] = bounds[3] - oldHeight;
	    }
	}

	/*We've got a new bounds, put it back*/
	if (isIcon)
	{
	    locArray = NewRealArray(1, 2L);
	    ((real *) ELEMENTS(locArray))[0] = bounds[0];
	    ((real *) ELEMENTS(locArray))[1] = bounds[2];
	    SetVar(object, ICONLOC, locArray);
	}
	else
	{
	    boundsArray = NewRealArray(1, 4L);
	    CArray2Array(boundsArray, bounds);
	    SetVar(object, BOUNDS, boundsArray);
	}

	/*If there are some contents to this, do the reshape recursively*/
	contents = GetVar(object, CONTENTS);
	if (contents && IsList(contents))
	{
	    ReshapeList(LISTOF(contents),
		    0, (int) (oldBounds[1] - oldBounds[0]),
		    0, (int) (oldBounds[3] - oldBounds[2]),		0, (int) (bounds[1] - bounds[0]),
		    0, (int) (bounds[3] - bounds[2]));
	}
    }
}

void ReshapeList(contents, ol, or, ob, ot, left, right, bottom, top)
ThingListPtr contents;
int ol, or, ob, ot;
int left, right, bottom, top;
/*Reshapes a list of objects using ReshapeObject*/
{
    while (contents)
    {
	ObjPtr object;

	object = contents -> thing;
	ReshapeObject(object, ol, or, ob, ot, left, right, bottom, top);
	    
	contents = contents -> next;
    }
}

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

    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;
}

void ImInvalid(object)
ObjPtr object;
/*Alerts the window system that an object is no longer valid*/
{
    SetVar(object, PICSTATE, NewInt(FBBB));
}

#ifdef PROTO
void PauseDrawing(Bool whether)
#else
void PauseDrawing(whether)
Bool whether;
#endif
/*Pauses drawing for a while*/
{
    if (whether)
    {
	++pauseDrawing;
    }
    else
    {
	--pauseDrawing;
    }
}

void DrawMe(object)
ObjPtr object;
/*Redraws object in the current window undergoing interaction*/
{
#ifdef GRAPHICS
	ImInvalid(object);
	if (!pauseDrawing)
	{
		drawMouse = true;
	}
#endif
}

void InitPickUp()
/*Initializes picking up objects*/
{
    if (dragBuffer)
    {
	DeleteThing(dragBuffer);
	dragBuffer = NULLOBJ;
    }
}

ObjPtr PickUpObject(object)
ObjPtr object;
/*Picks up an object*/
{
    object = ObjectWhichRepresents(selWinInfo, object);
    if (object && IsIcon(object))
    {
	char s[256];
	if (!dragBuffer)
	{
	    iconXOff = iconYOff = 0;
	    dragBuffer = NewList();
	    AddToReferenceList(dragBuffer);
	    MySetCursor(ICONCURSOR);
	}
	s[0] = ' ';
	MakeObjectName(s + 1, object);
	Log(s);
	PostfixList(dragBuffer, object);
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

void DoPickUpObjects()
/*Picks up the objects in the icon in the selected window*/
{
    DoObjFunction(OF_PICK_UP);
}

#if 0
void DoDropObjects()
/*Drops the objects in the drag buffer*/
{
    FuncType method;
    if (!selWinInfo)
    {
	return;
    }
    if (!dragBuffer)
    {
	return;
    }
    method = GetMethod((ObjPtr) selWinInfo, DROPOBJECTS);
    if (method)
    {
	iconXOff = 0;
	iconYOff = 0;
	if (logging)
	{
	    char cmd[256];
	    sprintf(cmd, "drop %d %d\n", dropX, dropY);
	    Log(cmd);
	    InhibitLogging(true);
	}
	(*method)(selWinInfo, dragBuffer, dropX, dropY);
	DeleteThing(dragBuffer);
	dragBuffer = NULLOBJ;
	if (logging)
	{
	    InhibitLogging(false);
	}
    }
}
#endif

void ToggleShowBounds()
/*Toggles the show bounds in the current window*/
{
    if (selWinInfo)
    {
	Bool lastState;
	lastState = GetPredicate((ObjPtr) selWinInfo, SHOWBOUNDS);
	SetVar((ObjPtr) selWinInfo, SHOWBOUNDS,
			lastState ? ObjFalse : ObjTrue);
#ifdef GRAPHICS
#ifdef WINDOWS4D
	if (lastState)
	{
	    int l, r, b, t;

	    GetWindowBounds(&l, &r, &b, &t);
	    minsize(r - l + 1, t - b + 1);
	    maxsize(r - l + 1, t - b + 1);
	    winconstraints();
	}
	else
	{
	    minsize(40, 30);
	    maxsize(1280, 1024);
	    winconstraints();
	}
#endif
#endif
	ImInvalid((ObjPtr) selWinInfo);
    }
}

WinInfoPtr NewObjWindow(superClass, title, flags, minWidth, minHeight, maxWidth, maxHeight)
ObjPtr superClass;
char *title;
long flags;
int minWidth, minHeight, maxWidth, maxHeight;
/*Creates a new object window with title title and flags flags.  superClass is
  the class of the window, if NULLOBJ objWindowClass will be used*/
{
    WinInfoPtr retVal;
    WindowID theWindow;

    if (showWindows)
    {
	theWindow = NewOpenedWindow(title, minWidth, minHeight, maxWidth, maxHeight,flags);
    }
    else
    {
	theWindow = 0;
    }

    retVal = NewWinInfo(superClass ? superClass : objWindowClass, theWindow, 
		flags, title,  minWidth, minHeight, maxWidth, maxHeight);
    SetVar((ObjPtr) retVal, CONTENTS, NewList());	/*Contents*/

#ifdef SELNEWWINDOWS
    inputWindow = retVal;
#endif
#ifdef GODLIKE
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "Toggle edit template", ToggleShowBounds);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "Save Template", DoSaveTemplate);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Slider", DoNewScreenSlider);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Scale Slider", DoNewScreenScaleSlider);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Title Box", DoNewScreenTitleBox);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Radio Button", DoNewScreenRadioButton);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Check Box", DoNewScreenCheckBox);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Text Box", DoNewScreenTextBox);
	DefineMenuItem((ObjPtr) retVal, DEUSMENU, "New Editable Text Box", DoNewScreenEditableTextBox);
#endif

    return retVal;
}

static ObjPtr FindObjectObjClass(object, name)
ObjPtr object;
char *name;
/*Searches an object and its contents for an object with name*/
{
    ObjPtr retVal = NULLOBJ;
    ObjPtr objName, contents;

    /*First check to see if I am the object*/
    objName = GetVar(object, NAME);
    if (objName && IsString(objName) && 0 == strcmp2(GetString(objName), name))
    {
	if (!retVal)
	{
	    retVal = NewList();
	}
	PostfixList(retVal, object);
    }

    /*Now check my CONTENTS*/
    contents = GetVar(object, CONTENTS);
    if (contents)
    {
	ObjPtr foundObjects;
	foundObjects = FindNamedObject(contents, name);
	if (foundObjects)
	{
	    if (!retVal)
	    {
		retVal = NewList();
	    }
	    AppendList(retVal, foundObjects);
	}
    }

    return retVal;
}

static ObjPtr ForAllObjectsObjClass(object, routine)
ObjPtr object;
FuncTyp routine;
/*Does routine on all objects within object*/
{
    ObjPtr contents;

    (*routine)(object);

    /*Now check my CONTENTS*/
    contents = GetVar(object, CONTENTS);
    if (contents)
    {
	ForAllObjects(contents, routine);
    }

    return ObjTrue;
}

extern ObjPtr objClass;

void InitObjWindows()
/*Initializes the obj window system*/
{
    objWindowClass = NewObject(windowClass, 0);
    AddToReferenceList(objWindowClass);
    SetMethod(objWindowClass, CLOSE, CloseObjWindow);
    SetMethod(objWindowClass, DRAW, DrawObjWindow);
    SetMethod(objWindowClass, PRESS, PressObjWindow);
    SetMethod(objWindowClass, DROPOBJECTS, DropObjWindow);
    SetMethod(objWindowClass, KEYDOWN, KeyDownObjWindow);
    SetMethod(objWindowClass, RESHAPE, ReshapeObjWindow);
    SetMethod(objWindowClass, MAKEDRAWN, MakeObjWindowDrawn);

    /*KLUDGE*/
    SetMethod(objClass, FINDOBJECT, FindObjectObjClass);
    SetMethod(objClass, FORALLOBJECTS, ForAllObjectsObjClass);
}

static Bool repped;
static ObjPtr testObject;
static ObjPtr objectWhichRepresents;

static ObjPtr CheckRepObj(object)
ObjPtr object;
/*Checks if an object matches testObject*/
{
    if (IsIcon(object) && (GetVar(object, REPOBJ) == testObject))
    {
	objectWhichRepresents = object;
	repped = true;
	return ObjTrue;
    }
    else if ((object == testObject) && !repped)
    {
	objectWhichRepresents = object;
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

ObjPtr ObjectWhichRepresents(window, object)
WinInfoPtr window;
ObjPtr object;
/*Returns the object in window that represents object, or NULLOBJ*/
{
    if (object == (ObjPtr) window) return (ObjPtr) window;

    repped = false;
    objectWhichRepresents = NULLOBJ;
    testObject = object;

    if (object)
    {
	ForAllObjects((ObjPtr) window, CheckRepObj);
    }
    else
    {
	return NULLOBJ;
    }
    if (!objectWhichRepresents)
    {
	ObjPtr var;
	var = GetVar(object, NAME);
	if (var)
	{
	    ObjPtr list;
	    list = FindNamedObject((ObjPtr) window, GetString(var));
	    if (list && LISTOF(list))
	    {
		objectWhichRepresents = LISTOF(list) -> thing;
	    }
	}
    }
    return objectWhichRepresents;
}

ObjPtr DeleteObject(object)
ObjPtr object;
/*Deletes an object from the current window.*/
{
    ObjPtr deleteObject;

    if (!selWinInfo || !object) return ObjFalse;
    deleteObject = ObjectWhichRepresents(selWinInfo, object);
    Select(object, false);
    if (deleteObject)
    {
	FuncTyp method;
	object = deleteObject;
	method = GetMethod(object, DELETEICON);

	if (method)
	{
	    if (IsTrue((*method)(object)))
	    {
		ObjPtr contentsList;
		contentsList = GetVar(object, PARENT);
	 	if (!contentsList || !IsList(contentsList))
		{
		    contentsList = GetVar(contentsList, CONTENTS);
		}
		if (contentsList && IsList(contentsList))
		{
		    ImInvalid(object);
		    DeleteFromList(contentsList, object);
		    return ObjTrue;
		}
		else
		{
		    ReportError("DeleteObject","Internal error: cannot find contents list");
		}
	    }
	}
    }
    return ObjFalse;
}

void DoDelete()
/*Deletes objects*/
{
    ObjPtr corral;
    Bool didit = false;

    if (logging)
    {
	InhibitLogging(true);
    }
    if (selWinInfo)
    {
	ForAllSelectedObjects(DeleteObject);
    }
    if (logging)
    {
	InhibitLogging(false);
	if (didit)
	{
	    Log("delete\n");
	}
    }
}

void KillObjWindows()
/*Kills the obj window system*/
{
    DeleteThing(objWindowClass);
}

ObjPtr FindNamedObject(object, name)
ObjPtr object;
char *name;
/*Finds object with name within object.  Returns a list of matching objects*/
{
    FuncTyp method;
    method = GetMethod(object, FINDOBJECT);
    if (method)
    {
	return (*method)(object, name);
    }
    else
    {
	return NULLOBJ;
    }
}

void MakeMeCurrent(object)
ObjPtr object;
/*Makes object the current object in the local window*/
{
    if (runningScript && !scriptSelectP)
    {
	return;
    }

    if (selWinInfo)
    {
	ObjPtr oldCurrent;

	oldCurrent = GetVar((ObjPtr) selWinInfo, CURRENT);
        if (oldCurrent == object) return;
	FlushKeystrokes();
	SetVar((ObjPtr) selWinInfo, CURRENT, object);
	if (oldCurrent)
	{
	    ImInvalid(oldCurrent);
	    DeferMessage(oldCurrent, YOURENOTCURRENT);
	}
	if (object) ImInvalid(object);
    }
}

Bool AmICurrent(object)
ObjPtr object;
/*Returns true iff object is the current object in the current window*/
{
    if (selWinInfo)
    {
	return GetVar((ObjPtr) selWinInfo, CURRENT) == object ? true : false;
    }
    else
    {
	return false;
    }
}
