/*ScianObjFunctions.c
  Eric Pepke
  April 9, 1992
  Definitions and routines for per-object functions
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianLists.h"
#include "ScianWindows.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianObjWindows.h"
#include "ScianButtons.h"
#include "ScianErrors.h"
#include "ScianDraw.h"
#include "ScianControls.h"
#include "ScianArrays.h"
#include "ScianScripts.h"
#include "ScianVisWindows.h"
#include "ScianIcons.h"
#include "ScianEvents.h"
#include "ScianStyle.h"
#include "ScianObjFunctions.h"
#include "ScianHelp.h"
#include "ScianFilters.h"
#include "ScianFileSystem.h"
#include "ScianSockets.h"
#include "ScianSnap.h"

typedef struct
    {
	char *name;
	void (*setupRoutine)();
	void (*finishRoutine)();
	NameTyp funcMessage;
	char *buttonHelp;
	char *menuHelp;
	char *scriptPrefix, *scriptPostfix;
	Bool isUsed;
	int menu;		/*Which menu this routine is in*/
	ObjPtr objectsToDo;	/*List of current objects to do this function on*/
    } ObjFunction;

int nObjFunctions = 0;			/*Number of object functions*/

static int menuPrefix[NMENUS];			/*Number of prefix things on each menu*/

static ObjFunction *objFunctions = NULL;
static int globalFuncNum;
static ObjPtr undoList;			/*List of undo operations*/
static ObjPtr undoClass;		/*Class of undo's*/
static ObjPtr curUndo = NULLOBJ;	/*Current undo being built up*/
static int curUndoType = 0;		/*Current type of undo being built*/

/*Operation types*/
#define UT_CHANGESNAPSHOT	1	/*Change an object's snapshot*/

#ifdef PROTO
ObjPtr NewUndo(int undoType)
#else
ObjPtr NewUndo(undoType)
int undoType;
#endif
{
    ObjPtr retVal;

    retVal = NewObject(undoClass, 0L);
    SetVar(retVal, UNDOTYPE, NewInt(undoType));

    /*Do initialization of undo*/
    switch(undoType)
    {
	case UT_CHANGESNAPSHOT:
	    SetVar(retVal, OLDSNAPSHOTS, NewList());
	    break;
    }

    return retVal;
}

#ifdef PROTO
void BeginUndo(int undoType)
#else
void BeginUndo(undoType)
int undoType;
#endif
/*Begins an undo to build up*/
{
    curUndo = NewUndo(undoType);
    curUndoType = undoType;
}

#ifdef PROTO
void SaveSnapshotForUndo(ObjPtr object)
#else
void SaveSnapshotForUndo(object)
ObjPtr object;
#endif
/*Saves a snapshot of object in the current undo*/
{
    ObjPtr list;

    if (!curUndo) return;

    list = GetVar(curUndo, OLDSNAPSHOTS);
    if (!list)
    {
	list = NewList();
	SetVar(curUndo, OLDSNAPSHOTS, list);
    }
    PrefixList(list, TakeSnapshot(object));
}

void EndUndo()
/*Ends an undo in progress*/
{
    if (!curUndo) return;
    EmptyList(undoList);
    PrefixList(undoList, curUndo);
    curUndo = NULLOBJ;
}

#ifdef PROTO
void SaveForUndo(ObjPtr object)
#else
void SaveForUndo(object)
ObjPtr object;
#endif
/*Saves object for undo later*/
{
    ObjPtr snapshot, undo, list;

    if (object)
    {
	BeginUndo(UT_CHANGESNAPSHOT);
	SaveSnapshotForUndo(object);
	EndUndo();
    }
}

void Undo()
/*Do the undo operation*/
{
    ThingListPtr runner;

    runner = LISTOF(undoList);
    if (runner && runner -> thing)
    {
	/*There is something to undo*/
	int undoType;
	ObjPtr var, undo;

	undo = runner -> thing;
	var = GetIntVar("Undo", undo, UNDOTYPE);
	if (!var) return;

	undoType = GetInt(var);
	switch(undoType)
	{
	    case UT_CHANGESNAPSHOT:
		{
		    ObjPtr oldSnapshots, oldSnap, object;
		    ThingListPtr snapRunner;

		    oldSnapshots = GetListVar("Undo", undo, OLDSNAPSHOTS);
		    if (!oldSnapshots) return;

		    /*Set up inverse undo*/
		    BeginUndo(UT_CHANGESNAPSHOT);

		    snapRunner = LISTOF(oldSnapshots);
		    while (snapRunner)
		    {
			oldSnap = snapRunner -> thing;
			object = GetVar(oldSnap, REPOBJ);

			SaveSnapshotForUndo(object);
			ApplySnapshot(oldSnap);

			snapRunner = snapRunner -> next;
		    }

		    EndUndo();
		}
		break;
	}

    }
}

#ifdef PROTO
void DefineObjFunction(char *funcName, void (*setupRoutine)(), NameTyp funcMessage,
	void (*finishRoutine)(), char *scriptPrefix, char *scriptPostfix, char *buttonHelp,
	char *menuHelp, int menu)
#endif
/*Defines an object function
    funcName	    is the name of the function for the menu and buttons
    setupRoutine    is a routine to call at the beginning, or 0
    funcMessage	    is a message to send to every selected object as a single argument
    finishRoutine   is a routine to call when finished
    scriptPrefix    is the script portion to prefix to the object names
    scriptPostfix   is the script portion to postfix to the object names
    buttonHelp	    is the help string for a button, or nothing for no button
    menuHelp	    is the help string for a menu
    menu	    is which menu to use, or 0 for none
*/
{
    if (nObjFunctions)
    {
	objFunctions = realloc(objFunctions, (nObjFunctions + 1) * sizeof(ObjFunction));
    }
    else
    {
	objFunctions = malloc((nObjFunctions + 1) * sizeof(ObjFunction));
    }

    if (funcName)
    {
	objFunctions[nObjFunctions] . name = malloc(strlen(funcName) + 1);
	strcpy(objFunctions[nObjFunctions] . name, funcName);
    }
    else
    {
	objFunctions[nObjFunctions] . name = (char *) 0;
    }
    objFunctions[nObjFunctions] . setupRoutine = setupRoutine;
    objFunctions[nObjFunctions] . funcMessage = funcMessage;
    objFunctions[nObjFunctions] . finishRoutine = finishRoutine;
    objFunctions[nObjFunctions] . menu = menu;
    AddToReferenceList(objFunctions[nObjFunctions] . objectsToDo = NewList());
    if (scriptPrefix)
    {
	objFunctions[nObjFunctions] . scriptPrefix = malloc(strlen(scriptPrefix) + 1);
	strcpy(objFunctions[nObjFunctions] . scriptPrefix, scriptPrefix);
    }
    else
    {
	objFunctions[nObjFunctions] . scriptPrefix = (char *) 0;
    }
    if (scriptPostfix)
    {
	objFunctions[nObjFunctions] . scriptPostfix = malloc(strlen(scriptPostfix) + 1);
	strcpy(objFunctions[nObjFunctions] . scriptPostfix, scriptPostfix);
    }
    else
    {
	objFunctions[nObjFunctions] . scriptPostfix = (char *) 0;
    }
    if (buttonHelp)
    {
	objFunctions[nObjFunctions] . buttonHelp = malloc(strlen(buttonHelp) + 1);
	strcpy(objFunctions[nObjFunctions] . buttonHelp, buttonHelp);
    }
    else
    {
	objFunctions[nObjFunctions] . buttonHelp = (char *) 0;
    }
    if (menuHelp)
    {
	objFunctions[nObjFunctions] . menuHelp = malloc(strlen(menuHelp) + 1);
	strcpy(objFunctions[nObjFunctions] . menuHelp, menuHelp);
    }
    else
    {
	objFunctions[nObjFunctions] . menuHelp = (char *) 0;
    }
    ++nObjFunctions;
}

static void StartObjFunction(funcNum)
int funcNum;
/*Starts object function funcNum*/
{
    globalFuncNum = funcNum;

    /*Do the first bit*/
    if (objFunctions[funcNum] . setupRoutine)
    {
	InhibitLogging(true);
	(*(objFunctions[funcNum] . setupRoutine))();
	InhibitLogging(false);
    }
}

static ObjPtr DoObjFunctionOnObject(object)
ObjPtr object;
/*Does an object function in globalFuncNum, if it is appropriate, on object.  
  Designed to be passable to ForAllSelectedObjects*/
{
    FuncTyp method;
    ObjPtr retVal = ObjFalse;

    method = GetMethod(object, objFunctions[globalFuncNum] . funcMessage);
    if (method)
    {
	InhibitLogging(true);
	retVal = (*method)(object);
	InhibitLogging(false);
    }
    return retVal;
}

static void FinishObjFunction()
/*Finishes object function funcNum*/
{
    /*Do the last bit*/
    if (objFunctions[globalFuncNum] . finishRoutine)
    {
	InhibitLogging(true);
	(*(objFunctions[globalFuncNum] . finishRoutine))();
	InhibitLogging(false);
    }
}

int NameToObjFunction(name)
char *name;
/*Converts a name to a function number, or -1*/
{
    int funcNum;

    /*Search for the named function*/
    for (funcNum = 0; funcNum < nObjFunctions; ++funcNum)
    {
	if (0 == strcmp2(name, objFunctions[funcNum] . name))
	{
	    break;
	}
    }

    if (funcNum >= nObjFunctions)
    {
	/*Not found, too bad.*/
	char nameStr[300];
	sprintf(nameStr, "Internal error: name '%s' not found", name);
	ReportError("NameToObjFunction", nameStr);
	return -1;
    }
    return funcNum;
}

static Bool firstLoggedObject;

ObjPtr LogSpacedObject(object)
ObjPtr object;
/*Logs all the objects with spaces*/
{
    if (!firstLoggedObject)
    {
	Log(",");
    }
    else
    {
	firstLoggedObject = false;
	if (objFunctions[globalFuncNum] . scriptPrefix)
	{
	    Log(objFunctions[globalFuncNum] . scriptPrefix);
	}
    }
    Log(" ");
    LogObjectName(object);
    return ObjTrue;
}

void LogObjFunction(name, list)
char *name;
ObjPtr list;
/*Logs the object function name on list*/
{
    int funcNum;

    FlushKeystrokes();

    funcNum = NameToObjFunction(name);
    if (funcNum < 0)
    {
	/*No valid function*/
	return;
    }

    /*Emit the log*/
    if (logging)
    {
	ThingListPtr runner;
	globalFuncNum = funcNum;
	firstLoggedObject = true;
	runner = LISTOF(list);
	while (runner)
	{
	    LogSpacedObject(runner -> thing);
	    runner = runner -> next;
	}

	if (!firstLoggedObject)
	{
	    if (objFunctions[funcNum] . scriptPostfix)
	    {
		Log(" ");
		Log(objFunctions[funcNum] . scriptPostfix);
	    }
	    Log("\n");
	}
    }
}

void LogSelectedObjFunction(name)
char *name;
/*Logs the object function name on all selected objects*/
{
    int funcNum;

    FlushKeystrokes();

    funcNum = NameToObjFunction(name);
    if (funcNum < 0)
    {
	/*No valid function*/
	return;
    }

    /*Emit the log*/
    if (logging)
    {
	globalFuncNum = funcNum;
	firstLoggedObject = true;
	ForAllSelectedObjects(LogSpacedObject);

	if (!firstLoggedObject)
	{
	    if (objFunctions[funcNum] . scriptPostfix)
	    {
		Log(" ");
		Log(objFunctions[funcNum] . scriptPostfix);
	    }
	    Log("\n");
	}
    }
}

int funcToDo;		/*Function to do*/

void DoAllObjFunctionsLater()
/*Deferred routine to do all the setup object functions*/
{
    int funcNum;

    for (funcNum = 0; funcNum < nObjFunctions; ++funcNum)
    {
	if (IsList(objFunctions[funcNum] . objectsToDo))
	{
	    ThingListPtr runner;

	    runner = LISTOF(objFunctions[funcNum] . objectsToDo);
	    if (runner)
	    {
		StartObjFunction(funcNum);
		while (runner)
		{
		    DoObjFunctionOnObject(runner -> thing);
		    runner = runner -> next;
		}
		FinishObjFunction();
	    }
	    EmptyList(objFunctions[funcNum] . objectsToDo);
	}
    }
}

ObjPtr AddToLaterFunctions(object)
ObjPtr object;
/*Adds object to the function to do later in funcToDo*/
{
    PostfixList(objFunctions[funcToDo] . objectsToDo, object);
    return ObjTrue;
}

void DoObjFunction(name)
char *name;
/*Does object function name on all selected objects*/
{
    funcToDo = NameToObjFunction(name);
    if (funcToDo < 0)
    {
	/*No valid function*/
	return;
    }

    LogSelectedObjFunction(name);

    /*Fill the lists with the functions*/
    ForAllSelectedObjects(AddToLaterFunctions);

    DoUniqueTask(DoAllObjFunctionsLater);
}

ObjPtr AddToDoubleClickFunction(object)
ObjPtr object;
/*Adds object to its desired double click function*/
{
    ObjPtr var;
    int funcNum;

    var = GetVar(object, DOUBLECLICK);
    if (var)
    {
	funcNum = NameToObjFunction(GetString(var));
	if (funcNum < 0)
	{
	    ReportError("AddToDoubleClickFunction", "Doubleclick object function not valid");
	}
	else
	{
	    PostfixList(objFunctions[funcNum] . objectsToDo, object);
	}
    }
    else
    {
	ReportError("AddToDoubleClickFunction", "No doubleclick object function");
    }
    return ObjTrue;
}

void DoDoubleClickFunction()
/*Does an appropriate double click function on all selected objects*/
{
    int funcNum;

    /*Distribute all the selected objects in their preferred functions*/
    ForAllSelectedObjects(AddToDoubleClickFunction);

    /*Log all the object functions*/
    for (funcNum = 0; funcNum < nObjFunctions; ++funcNum)
    {
	if (IsList(objFunctions[funcNum] . objectsToDo))
	{
	    if (LISTOF(objFunctions[funcNum] . objectsToDo))
	    {
		LogObjFunction( objFunctions[funcNum] . name,
				objFunctions[funcNum] . objectsToDo);
	    }
	}
    }

    /*Do it later*/
    DoUniqueTask(DoAllObjFunctionsLater);
}

#ifdef PROTO
Bool ObjFunctionScriptLine(char *line)
#else
Bool ObjFunctionScriptLine(line)
char *line;
#endif
/*If s is a script line, finds out if it is a valid script line for an
  object function.  If so, does it and returns true, otherwise does nothing
  and returns false.  Determines whether it is valid by matching the 
  script prefix*/
{
    int funcNum;
    register char *s, *p, *t;

    /*For all possible functions*/
    for (funcNum = 0; funcNum < nObjFunctions; ++funcNum)
    {
	/*Try to match prefix against script*/
	s = line;
	p = objFunctions[funcNum] . scriptPrefix;

	SKIPBLANKS(s);
	SKIPBLANKS(p);

	while (*p)
	{
	    if (isspace(*p))
	    {
		SKIPBLANKS(s);
		SKIPBLANKS(p);
	    }
	    else if (toupper(*p) != toupper(*s))
	    {
		/*Failure to match*/
		break;
	    }
	    else
	    {
		++s;
		++p;
	    }
	}

	if (*p == 0)
	{
	    ObjPtr list;
	    ThingListPtr runner;
	    ObjPtr object;
	    /*It's a match!  Do this function and return true*/

	    list = NewList();

	    do
	    {
		object = ERROBJ;
		s = ParseObjectArg(s, &object);

		if (object != ERROBJ)
		{
		    if (!object)
		    {
			ScriptError("An object name is expected here.");
		    }
		    else
		    {
			if (IsIcon(object) && GetVar(object, REPOBJ))
			{
			    object = GetVar(object, REPOBJ);
			}
			PostfixList(list, object);
		    }
		}

		SKIPBLANKS(s);
	    } while (*s++ == ',');
	    --s;

	    /*Check postfix*/
	    p = objFunctions[funcNum] . scriptPostfix;

	    if (p)
	    {
	    SKIPBLANKS(s);
	    SKIPBLANKS(p);

	    while (*p)
	    {
		if (isspace(*p))
		{
		    SKIPBLANKS(s);
		    SKIPBLANKS(p);
		}
		else if (toupper(*p) != toupper(*s))
		{
		    /*Failure to match*/
		    break;
		}
		else
		{
		    ++s;
		    ++p;
		}
	    }
	    }

	    if (!p || (*p == 0))
	    {
		LogObjFunction(objFunctions[funcNum] . name, list);
		StartObjFunction(funcNum);
		runner = LISTOF(list);
		while (runner)
		{
		    DoObjFunctionOnObject(runner -> thing);
		    runner = runner -> next;
		}
		FinishObjFunction();
		return true;
	    }
	}
    }
    return false;
}

void FunctionMenu(item)
int item;
/*Does an object function from an IRIS menu call*/
{
    if (item < 0 || item > nObjFunctions)
    {
	ReportError("ObjFunctionMenu", "Internal error: bad menu value");
	return;
    }

#ifdef INTERACTIVE
    if (contextHelp)
    {
	/*DIKEO Change to do menus as objects, then add context help*/
    }
    else
#endif
    {
	LogSelectedObjFunction(objFunctions[item] . name);
	StartObjFunction(item);
	ForAllSelectedObjects(DoObjFunctionOnObject);
	FinishObjFunction();
    }
}

void AddFunctionItem(window, whichMenu, functionIndex)
WinInfoPtr window;
int whichMenu;
int functionIndex;
/*Adds function with functionIndex to menu whichMenu within window*/
{
    if (functionIndex < 0) return;

    sprintf(tempStr, "%s%%x%d%%f", objFunctions[functionIndex] . name,
			functionIndex);
#ifdef GRAPHICS
#ifdef MENUS4D
    if (!window -> subMenus[whichMenu]) window -> subMenus[whichMenu] = newpup();    
    addtopup(window -> subMenus[whichMenu], tempStr, FunctionMenu);
#endif
#endif
}

static void AddFunctionMenu(window, whichMenu)
WinInfoPtr window;
int whichMenu;
/*Adds a function menu to window.  Must be called in the order of AddFunctionMenus*/
{
    int functionIndex;

#ifdef GRAPHICS
#ifdef MENUS4D
    if (!window -> subMenus[whichMenu])
    {
	window -> subMenus[whichMenu] = newpup();
    }
#endif
#endif

    /*Prefix stuff*/
    switch(whichMenu)
    {
	case OBJECTMENU:
#ifdef GRAPHICS
#ifdef MENUS4D
	    sprintf(tempStr, "%s%%m", menuName[ARRANGEMENU]);
	    addtopup(window -> subMenus[whichMenu], tempStr, window -> subMenus[ARRANGEMENU]);
#endif
#endif
	    break;
    }

    for (functionIndex = 0; functionIndex < nObjFunctions; ++functionIndex)
    {
	if (objFunctions[functionIndex] . menu == whichMenu)
	{
	    AddFunctionItem(window, whichMenu, functionIndex);
	}
    }

    /*Postfix stuff*/
    switch(whichMenu)
    {
	case OBJECTMENU:
#ifdef GRAPHICS
#ifdef MENUS4D
	    addtopup(window -> subMenus[whichMenu], "-");
	    addtopup(window -> subMenus[whichMenu], "Select All%f", DoSelectAllIcons);
	    addtopup(window -> subMenus[whichMenu], "Deselect All%f", DeselectAll);
#endif
#endif
	    break;

	case DATASETSMENU:
#ifdef GRAPHICS
#ifdef MENUS4D
	    addtopup(window -> subMenus[whichMenu], "Show Datasets%f", PopDatasetsWindow);
#endif
#endif
	    break;
    }
}

void AddFunctionMenus(window)
WinInfoPtr window;
/*Adds all the function menus to window*/
{
    AddFunctionMenu(window, ARRANGEMENU);
    AddFunctionMenu(window, DATASETSMENU);
    AddFunctionMenu(window, OBJECTMENU);
    AddFunctionMenu(window, FILEMENU);
#ifdef SOCKETS
    AddFunctionMenu(window, NETWORKMENU);
#endif
}

static WinInfoPtr testWindow = 0;

static ObjPtr TestObjFunctions(object)
ObjPtr object;
/*Tests the obj functions on object*/
{
    int k;

    if (testWindow && !ObjectWhichRepresents(testWindow, object))
    {
	return ObjFalse;
    }

    for (k = 0; k < nObjFunctions; ++k)
    {
	if (!(objFunctions[k] . isUsed))
	{
	    if (GetMethod(object, objFunctions[k] . funcMessage))
	    {
		objFunctions[k] . isUsed = true;
	    }
	}
    }
    return ObjTrue;
}

static void AdjustFunctionMenu(window, whichMenu)
WinInfoPtr window;
int whichMenu;
/*Adjusts whichMenu based on the selected objects*/
{
#ifdef PUP_GREY
    int k, item;

    for (k = 0; k < nObjFunctions; ++k)
    {
	if (objFunctions[k] . menu == whichMenu)
	{
	    objFunctions[k] . isUsed = false;
	}
    }
    testWindow = 0;	/*Enable for all windows*/
    ForAllSelectedObjects(TestObjFunctions);

    item = menuPrefix[whichMenu];
    for (k = 0; k < nObjFunctions; ++k)
    {
	if (objFunctions[k] . menu == whichMenu)
	{
	    setpup(window -> subMenus[whichMenu], item
#ifndef MENUSFROM0
 + 1
#endif
		, objFunctions[k] . isUsed ? PUP_NONE : PUP_GREY);
	    ++item;
	}
    }
#endif
}

void AdjustFunctionMenus(window)
WinInfoPtr window;
/*Enables all the function menus*/
{
    AdjustFunctionMenu(selWinInfo, OBJECTMENU);
    AdjustFunctionMenu(selWinInfo, FILEMENU);
    AdjustFunctionMenu(selWinInfo, DATASETSMENU);
    AdjustFunctionMenu(selWinInfo, ARRANGEMENU);
#ifdef SOCKETS
    AdjustFunctionMenu(selWinInfo, NETWORKMENU);
#endif
}

static void EnableFunctionMenu(window, whichMenu)
WinInfoPtr window;
int whichMenu;
/*Enables whichMenu*/
{
#ifdef PUP_GREY
    int k, item;

    item = menuPrefix[whichMenu];
    for (k = 0; k < nObjFunctions; ++k)
    {
	if (objFunctions[k] . menu == whichMenu)
	{
	     setpup(window -> subMenus[whichMenu], item + 1, PUP_NONE);
	}
    }
#endif
}

void EnableFunctionMenus(window)
WinInfoPtr window;
/*Enables all the function menus*/
{
    EnableFunctionMenu(selWinInfo, OBJECTMENU);
    EnableFunctionMenu(selWinInfo, FILEMENU);
    EnableFunctionMenu(selWinInfo, DATASETSMENU);
    EnableFunctionMenu(selWinInfo, ARRANGEMENU);
#ifdef SOCKETS
    EnableFunctionMenu(selWinInfo, NETWORKMENU);
#endif
}

void AdjustWindowButtons(window)
WinInfoPtr window;
/*Adjusts the object buttons in window*/
{
    ObjPtr var;
    ThingListPtr runner;
    int k;

    /*Figure out which object functions are valid*/
    for (k = 0; k < nObjFunctions; ++k)
    {
	objFunctions[k] . isUsed = false;
    }
    testWindow = window;
    ForAllSelectedObjects(TestObjFunctions);

    var = GetVar((ObjPtr) window, FUNCTIONBUTTONS);
    if (var)
    {
	runner = LISTOF(var);
	while (runner)
	{
	    var = GetStringVar("AdjustWindowButtons", runner -> thing, NAME);
	    if (var)
	    {
		int k;
		k = NameToObjFunction(GetString(var));
		if (k >= 0)
		{
		    ActivateButton(runner -> thing, 
			objFunctions[k] . isUsed);
		}
	    }
	    runner = runner -> next;
	}
    }
}

void AdjustObjButtons()
/*Task to change all the object buttons in all the windows*/
{
    /*Now go through all the windows adjusting the object buttons*/
    ForAllWindows(AdjustWindowButtons);    
}

ObjPtr ChangeFunctionButton(button)
ObjPtr button;
/*Changed value for a function button*/
{
    ObjPtr var;
    var = GetVar(button, NAME);
    if (var)
    {
	DoObjFunction(GetString(var));
	return ObjTrue;
    }
    else
    {
	return ObjFalse;
    }
}

static ObjPtr SetFunctionButtonValue(theButton, theValue)
ObjPtr	theButton, theValue;
{
    FuncTyp	changer;
    ObjPtr	retVal;

    if (IsInt(theValue))
    {
	SetVar(theButton, VALUE, theValue);
    }
    else if (IsReal(theValue))
    {
	SetVar(theButton, VALUE, NewInt((int) GetReal(theValue)));
    }
    else
    {
	return ObjFalse;
    }

    changer = GetMethod(theButton, CHANGEDVALUE);
    if (changer)
    {
	retVal = (* changer) (theButton);
    }
    else
    {
	retVal = ObjFalse;
    }
    ImInvalid(theButton);

    return retVal;
}

#ifdef PROTO
ObjPtr NewFunctionButton(WinInfoPtr window, int l, int r, int b, int t, char *name)
#else
ObjPtr NewFunctionButton(window, l, r, b, t, name)
WinInfoPtr window; int l; int r; int b; int t; char *name;
#endif
{
    ObjPtr retVal, var;
    int whichFunction;

    whichFunction = NameToObjFunction(name);
    if (whichFunction < 0) return NULLOBJ;

    retVal = NewButton(l, r, b, t, name);
    SetMethod(retVal, SETVAL, SetFunctionButtonValue);
    SetMethod(retVal, CHANGEDVALUE, ChangeFunctionButton);
    ActivateButton(retVal, false);
    if (objFunctions[whichFunction] . buttonHelp)
    {
	SetVar(retVal, HELPSTRING, 
	NewString(objFunctions[whichFunction] . buttonHelp));
    }

    var = GetVar((ObjPtr) window, FUNCTIONBUTTONS);
    if (!var)
    {
	var = NewList();
	SetVar((ObjPtr) window, FUNCTIONBUTTONS, var);
    }
    PrefixList(var, retVal);

    DoUniqueTask(AdjustObjButtons);

    return retVal;
}

void InitObjFunctions()
/*Initializes the object function system*/
{
    int k;

    undoClass = NewObject(NULLOBJ, 0L);
    AddToReferenceList(undoClass);

    undoList = NewList();
    AddToReferenceList(undoList);

    /*Zero the prefix counts*/
    for (k = 0; k < NMENUS; ++k)
    {
	menuPrefix[k] = 0;
    }
    menuPrefix[OBJECTMENU] = 1;

    objFunctions = NULL;

    DefineObjFunction(OF_PICK_UP, InitPickUp, PICKUP, 0,
	"drag", (char *) 0,
"Pressing this button will pick up the selected objects.  Once you have picked up \
the objects, you can drop them into another icon corral by clicking where you want to \
drop them.  This is useful as a substitute for dragging icons between windows on \
computers that do not support dragging between windows.",
"Choosing this menu will pick up the selected objects.  Once you have picked up \
the objects, you can drop them into another icon corral by clicking where you want to \
drop them.  This is useful as a substitute for dragging icons between windows on \
computers that do not support dragging between windows.", OBJECTMENU);
    DefineObjFunction(OF_OPEN, 0, OPEN, 0,
	"open", (char *) 0,
	"Pressing this button will read the selected files into the Datasets window and open new file windows \
for the selected folders.", 
	"Choosing this menu item will read the selected files into the Datasets window and open new file windows \
for the selected folders.", FILEMENU); 
    DefineObjFunction(OF_SETFORMAT, SetUpCollect, COLLECT, ProcessSetFormat,
	"set format", (char *) 0,
	"Pressing this button will open a dialog window allowing you to set the data format \
of the selected files.",
	"Choosing this menu item will open a dialog window allowing you to set the data format \
of the selected files.", FILEMENU);
    DefineObjFunction(OF_SHOW_CONTROLS, 0, SHOWCONTROLS, 0,
	"show controls", (char *) 0,
	"Pressing this button will bring up windows showing controls for each of the selected objects.",
	"Selecting this menu item will bring up windows showing controls for each of the selected objects.",
	OBJECTMENU);
    DefineObjFunction(OF_SHOWINFO, SetUpCollect, COLLECT, ProcessShowInfo,
	"show info", (char *) 0,
	"Pressing this button will open a window showing directory information for the selected files.",
	"Choosing this menu item will open a window showing directory information for the selected files.",
	FILEMENU);
    DefineObjFunction(OF_VISUALIZE, NewSerializedVisWindow, VISUALIZE, 0,
	"visualize", (char *) 0,
"Pressing this button will visualize the selected objects \
together in a new window.  Visualization objects are visualized as they are, and \
datasets are visualized using their default visualization technique.  Hold down \
the Alt or Ctrl key to have the visualizations come up turned off by default.  This \
is useful with complex visualizations if you want to change controls before \
spending the time drawing the visualizations.",
"Choosing this menu item will visualize the selected objects \
together in a new window.  Visualization objects are visualized as they are, and \
datasets are visualized using their default visualization technique.  Hold down \
the Alt or Ctrl key to have the visualizations come up turned off by default.  This \
is useful with complex visualizations if you want to change controls before \
spending the time drawing the visualizations.", DATASETSMENU);
    DefineObjFunction(OF_VISUALIZE_AS, NewVisAsWindow, VISUALIZEAS, ProcessVisAsList,
	"visobjectas", (char *) 0,
"Pressing this button will bring up a window \
showing all the basic ways of visualizing the selected datasets.  You will \
then be able to choose the visualization you want to use.",
"Choosing this menu item will bring up a window \
showing all the basic ways of visualizing the selected datasets.  You will \
then be able to choose the visualization you want to use.", DATASETSMENU);
    DefineObjFunction(OF_MODIFY, SetupModifyList, MODIFY, ProcessModifyList,
	"modify", (char *) 0,
"Pressing this button will bring up a window showing all the basic ways to \
modify the selected datasets.  You will then be able to choose the modification \
you want to use.",
"Choosing this menu item will bring up a window showing all the basic ways to \
modify the selected datasets.  You will then be able to choose which modification \
you want to use.", DATASETSMENU);
    DefineObjFunction(OF_TURNON, 0, SHOW, 0,
	"turn on", (char *) 0,
"Pressing this button will turn on all the selected objects.",
"Choosing this menu item will turn on all the selected objects.", OBJECTMENU);
    DefineObjFunction(OF_TURNOFF, 0, HIDE, 0,
	"turn on", (char *) 0,
"Pressing this button will turn off all the selected objects.",
"Choosing this menu item will turn off all the selected objects.", OBJECTMENU);
    DefineObjFunction(OF_PUSHTOBOTTOM, 0, PUSHTOBOTTOM, 0,
	"push to bottom", (char *) 0,
"Pressing this button will push the selected screen objects to the bottom of the panel \
below all the others.",
"Choosing this menu item will push the selected screen objects to the bottom of the panel \
below all the others.",
	ARRANGEMENU);
    DefineObjFunction(OF_BRINGTOTOP, 0, BRINGTOTOP, 0,
	"bring to top", (char *) 0,
"Pressing this button will bring the selected screen objects to the top of the panel \
above all the others.",
"Choosing this menu item will bring the selected screen objects to the top of the panel \
above all the others.",
	ARRANGEMENU);
    DefineObjFunction(OF_MOVETOBACKPANEL, 0, MOVETOBACKPANEL, 0,
	"move","to back panel",
"Pressing this button will move the selected screen objects to the back panel of the space.",
"Choosing this menu item will move the selected screen objects to the back panel of the space.",
	ARRANGEMENU);
    DefineObjFunction(OF_MOVETOFRONTPANEL, 0, MOVETOFRONTPANEL, 0,
	"move","to front panel",
"Pressing this button will move the selected screen objects to the front panel of the space.",
"Choosing this menu item will move the selected screen objects to the front panel of the space.",
	ARRANGEMENU);
    DefineObjFunction(OF_DUPLICATE, 0, DUPLICATE, 0,
	"duplicate", (char *) 0,
"Pressing this button will duplicate the selected objects in the current window.",
"Choosing this menu item will duplicate the selected objects in the current window.",
	OBJECTMENU);
    DefineObjFunction(OF_LOCALCOPY, 0, LOCALCOPY, 0,
	"localcopy", (char *) 0,
"Pressing this button will make a copy of the selected object local to this window.  \
If the object is shared between windows, this new copy will no longer be shared.",
"Choosing this menu item will make a copy of the selected object local to this window.  \
If the object is shared between windows, this new copy will no longer be shared.",
	OBJECTMENU);
    DefineObjFunction(OF_DELETE, 0, DELETE, 0,
	"delete", (char *) 0,
"Pressing this button will delete all the selected objects from the current \
window.",
"Choosing this menu item will delete all the selected objects from the current \
window.", OBJECTMENU);
#ifdef SOCKETS
    DefineObjFunction(OF_CONNECT_TO_PROCESS, 0, CONNECTTOPROCESS, 0,
	"connect to process", (char *) 0,
	"Pressing this button will open a connection with the selected process or processes.", 
	"Choosing this menu item will open a connection with the selected process or processes.", NETWORKMENU); 
    DefineObjFunction(OF_ADVERTISE, DoEnablePublication, ADVERTISE, 0,
	"publish", (char *) 0,
	"Pressing this button will publish the selected objects over the network.", 
	"Choosing this menu item will publish the selected objects over the network.", NETWORKMENU); 
    DefineObjFunction(OF_UNADVERTISE, 0, UNADVERTISE, 0,
	"recall", (char *) 0,
	"Pressing this button will recall the selected objects from publication over the network.", 
	"Choosing this menu item will recall the selected objects from publication over the network.", NETWORKMENU); 
#endif
}

void KillObjFunctions()
{
    RemoveFromReferenceList(undoList);
    RemoveFromReferenceList(undoClass);
    while (nObjFunctions)
    {
	--nObjFunctions;
	if (objFunctions[nObjFunctions] . name)
	{
	    free(objFunctions[nObjFunctions] . name);
	}
	if (objFunctions[nObjFunctions] . scriptPrefix)
	{
	    free(objFunctions[nObjFunctions] . scriptPrefix);
	}
 	if (objFunctions[nObjFunctions] . scriptPostfix)
	{
	    free(objFunctions[nObjFunctions] . scriptPostfix);
	}
 	if (objFunctions[nObjFunctions] . buttonHelp)
	{
	    free(objFunctions[nObjFunctions] . buttonHelp);
	}
  	if (objFunctions[nObjFunctions] . menuHelp)
	{
	    free(objFunctions[nObjFunctions] . menuHelp);
	}
	DeleteThing(objFunctions[nObjFunctions] . objectsToDo);
    }
    if (objFunctions)
    {
	free(objFunctions);
    }
}
