/* ScianButtons.c:	John R. Murray, 3-30-90
		creates and deals with button objects
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianMethods.h"
#include "ScianLists.h"
#include "ScianErrors.h"
#include "ScianIcons.h"
#include "ScianArrays.h"
#include "ScianButtons.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianScripts.h"
#include "ScianControls.h"
#include "ScianEvents.h"
#include "ScianFontSystem.h"
#include "ScianStyle.h"
#include "ScianDraw.h"
#include "ScianTextBoxes.h"
#include "ScianObjFunctions.h"
#include "ScianSnap.h"

/* globals */
ObjPtr	buttonClass;			/* class of buttons */
ObjPtr	iconButtonClass;		/* class of icon buttons */
ObjPtr	iconLabeledButtonClass;		/* class of labeled icon buttons */
ObjPtr	radioButtonClass;		/* class of radio buttons */
ObjPtr	oldRadioButtonClass;		/* class of radio buttons */
ObjPtr	checkBoxClass;			/* class of check boxes */
ObjPtr	radioGroupClass;		/* class of '90.. er, um.. */


Bool inhibitSaveForUndo = false;

/* temporary defs to get something going */

/* none */

/* local constants */
#define BUTTONDEPTH	4	/* button "height"=width (pixels) of border) */
#define LABELFONTFUDGE	1	/* fudge strings up (or <0 = down) to center */
#define RADIOSPACER	((int) BUTTONFONTSIZE)
				/* distance in pixels from button to text */

/* values of object variables */
#define MARKERX		0	/* value of var MARKERTYPE for mark = 'X' */
#define MARKERCHECK	1	/* value of var MARKERTYPE for mark = check */
#define MARKERBLOT	2	/* value of var MARKERTYPE for mark = dot */
#define MARKERSPLAT	3	/* value of var MARKERTYPE for mark = splat */

static ObjPtr DrawButtonObj(theButton, theButtonOwner)
ObjPtr	theButton, theButtonOwner;
{

#ifdef GRAPHICS
    int left, right, bottom, top;
    int		depth;
    Bool	hilight, active;
    ObjPtr	theLabel;
    char	*label;
    ObjPtr	theColor;
    int		color;

    if (!Get2DIntBounds(theButton, &left, &right, &bottom, &top))
    {
	return NULLOBJ;
    }

    if (IsDrawingRestricted(left, right, bottom, top))
    {
	return ObjFalse;
    }

    hilight = GetPredicate(theButton, HIGHLIGHTED);
    active = GetPredicate(theButton, ACTIVATED);

    theLabel = GetVar(theButton, NAME);
    if (theLabel)
    {
	label = GetString(theLabel);
    }
    else
    {
	label = (char *) NIL;
    }

    theColor = GetVar(theButton, COLOR);
    if (!theColor)
    {
	color = UIBACKGROUND;
    }
    else
    {
	color = GetPredicate(theButton, VALUE) ? GetInt(theColor) : UIBACKGROUND;
    }

    if (!active)
    {
	setpattern(GREYPAT);
	DrawRaisedRect(left, right, bottom, top, hilight ? UIHIBACKGROUND : color);
	setpattern(SOLIDPAT);
    }
    else
    {
	DrawRaisedRect(left, right, bottom, top, hilight ? UIHIBACKGROUND : color);
    }

    if (label)
    {
	if (active)
	    SetUIColor(UITEXT);
	else
	    SetUIColor(UIGREYTEXT);

	SetupFont(BUTTONFONT, BUTTONFONTSIZE);
	DrawAString(CENTERALIGN, (left + right) / 2,
		   (top+bottom)/2 + LABELFONTFUDGE - ((int) BUTTONFONTSIZE/2),
		   label);
    }
    return ObjTrue;
#endif /* GRAPHICS */
}

ObjPtr	NewIconButton(left, right, bottom, top, icon, color, name, style)
int	left, right, bottom, top;
int	icon, color;
char *name;
int style;
/*Makes a new icon button with bounds left, right, bottom, top,
   with icon icon, and color color when on (otherwise drawn in UITEXT) */
{
    ObjPtr	retVal;
    ObjPtr	iconPtr;
    ObjPtr	hilightPtr;


    retVal = NewObject(iconButtonClass, 0);
    iconPtr = NewInt(icon);
    SetVar(retVal, WHICHICON, iconPtr);
    Set2DIntBounds(retVal, left, right, bottom, top);
    hilightPtr = NewInt(false);
    SetVar(retVal, HIGHLIGHTED, hilightPtr);
    SetVar(retVal, VALUE, NewInt(false));
    SetVar(retVal, COLOR, NewInt(color));
    SetVar(retVal, ACTIVATED, NewInt(true));
    if (style != BS_PLAIN && style != BS_PITTED)
    {
	ReportError("NewIconButton", "Illegal button style");
	SetVar(retVal, STYLE, NewInt(BS_PLAIN));
    }
    else
    {
	SetVar(retVal, STYLE, NewInt(style));
    }
    if (name)
    {
	SetVar(retVal, NAME, NewString(name));
    }
    return retVal;
}

/* DRAW method for icon buttons */

ObjPtr	DrawIconButtonObj(theButton, theButtonOwner)
ObjPtr	theButton, theButtonOwner;
{

#ifdef GRAPHICS
    int left, right, bottom, top;
    int		depth;
    Bool	hilight, active;
    ObjPtr	theIcon;
    int		label;
    ObjPtr	boundsArray;
    real	bounds[4];
    char	buttonLabel[128];
    int		value;
    ObjPtr	theColor;
    int		color;
    int		style;
    FuncTyp	extraDraw;
    int		diFlags;

    if (!Get2DIntBounds(theButton, &left, &right, &bottom, &top))
    {
	return NULLOBJ;
    }

    if (IsDrawingRestricted(left, right, bottom, top))
    {
	return ObjFalse;
    }

    hilight = GetPredicate(theButton, HIGHLIGHTED);
    active = GetPredicate(theButton, ACTIVATED);
    value = GetInt(GetIntVar("DrawIconButtonObj", theButton, VALUE));
    style = GetInt(GetIntVar("DrawIconButtonObj", theButton, STYLE));

    color = UIGREEN;
    diFlags = active ? DI_DRAWFORE | DI_DRAWBACK | DI_SMALLTEXT
		     : DI_DRAWFORE | DI_DRAWBACK | DI_SMALLTEXT | DI_GREY;
    if (value)
    {
	if (GetVar(theButton, COLOR))
	{
	    color = GetInt(GetIntVar("DrawIconButtonObj" ,theButton, COLOR));
	}
	/* if no COLOR, color remains UIGREEN */
    }
    else if (hilight)
    {
	color = UIHIBACKGROUND;
    }
    else
    {
	switch(style)
	{
	    case BS_PLAIN:
		color = UIBACKGROUND;
		break;
	    case BS_PITTED:
		color = UIICONBACK;
		break;
	    default:
		ReportError("DrawIconButtonObj", "Illegal STYLE");
	}
    }

    if (style == BS_PLAIN)
    {
	if (!active) setpattern(GREYPAT);
	DrawRaisedRect(left, right, bottom, top,
	    hilight ? UIHIBACKGROUND : UIBACKGROUND);
	if (!active) setpattern(SOLIDPAT);
    }
    DrawIcon((left + right) / 2, (top + bottom) / 2,
	GetInt(GetIntVar("DrawIconButtonObj", theButton, WHICHICON)),
	(char *) NIL, (char *) NIL, color, diFlags);

    if (active)
    {
	extraDraw = GetMethod(theButton, ICONEXTRADRAW);
	if (extraDraw)
	{
	    (* extraDraw) (theButton, (int) ((left + right) / 2), (int) ((top + bottom) / 2));
	}
    }

    return ObjTrue;
#endif /* GRAPHICS */
}

ObjPtr	NewIconLabeledButton(left, right, bottom, top, icon, color, name, style)
int	left, right, bottom, top;
int	icon, color;
char *name;
int style;
/*Makes a new icon button with bounds left, right, bottom, top,
   with icon icon, color color when on (otherwise drawn in UITEXT)
   and label name (name is also the name of the object) */
{
    ObjPtr	retVal;
    ObjPtr	iconPtr, hilightPtr;

    retVal = NewObject(iconLabeledButtonClass, 0);
    Set2DIntBounds(retVal, left, right, bottom, top);
    iconPtr = NewInt(icon);
    SetVar(retVal, WHICHICON, iconPtr);
    hilightPtr = NewInt(false);
    SetVar(retVal, HIGHLIGHTED, hilightPtr);
    SetVar(retVal, VALUE, NewInt(false));
    SetVar(retVal, ACTIVATED, NewInt(true));
    SetVar(retVal, COLOR, NewInt(color));
    if (style != BS_PLAIN && style != BS_PITTED)
    {
	ReportError("NewIconLabelledButton", "Illegal button style");
	SetVar(retVal, STYLE, NewInt(BS_PLAIN));
    }
    else
    {
	SetVar(retVal, STYLE, NewInt(style));
    }
    if (name)
    {
	SetVar(retVal, NAME, NewString(name));
	SetVar(retVal, LABEL, NewString(name));
    }
    else
    {
	SetVar(retVal, LABEL, NewString(""));
    }
    return retVal;
}

ObjPtr	DrawIconLabeledButtonObj(theButton, theButtonOwner)
ObjPtr	theButton, theButtonOwner;
{

#ifdef GRAPHICS
    int left, right, bottom, top;
    int		depth;
    Bool	hilight, active;
    int		icon;
    int		offset;
    char	*label;
    char	buttonLabel[128];
    int		value;
    ObjPtr	theColor;
    int		color;
    int		style;
    FuncTyp	extraDraw;
    int		diFlags;

    if (!Get2DIntBounds(theButton, &left, &right, &bottom, &top))
    {
        return NULLOBJ;
    }

    if (IsDrawingRestricted(left, right, bottom, top))
    {
	return ObjFalse;
    }

    hilight = GetPredicate(theButton, HIGHLIGHTED);
    value = GetInt(GetIntVar("DrawIconLabeledButtonObj", theButton, VALUE));
    active = GetPredicate(theButton, ACTIVATED);
    style = GetInt(GetIntVar("DrawIconLabeledButtonObj", theButton, STYLE));

    if (GetVar(theButton, COLOR))
    {
	color = GetInt(GetIntVar("DrawIconLabeledButtonObj" ,theButton, COLOR));
    }
    else
    {
	color = UIGREEN;
    }

    if (GetVar(theButton, WHICHICON))
    {
	icon = GetInt(GetIntVar("DrawIconLabeledButtonObj",theButton, WHICHICON));
    }
    else
    {
	icon = ICONQUESTION;
    }

    if (GetVar(theButton, LABEL))
    {
	label = GetString(GetStringVar("DrawIconLabeledButtonObj",theButton, LABEL));
	offset = ICONTEXTOFFSET / 2;
    }
    else
    {
	label = (char *) NIL;
	offset = 0;
    }

    color = UIGREEN;
    if (value)
    {
	if (GetVar(theButton, COLOR))
	{
	    color = GetInt(GetIntVar("DrawIconButtonObj" ,theButton, COLOR));
	}
	/* if no COLOR, color remains UIGREEN */
    }
    else if (hilight)
    {
	color = UIHIBACKGROUND;
    }
    else
    {
	switch(style)
	{
	    case BS_PLAIN:
		color = UIBACKGROUND;
		break;
	    case BS_PITTED:
		color = UIICONBACK;
		break;
	    default:
		ReportError("DrawIconButtonObj", "Illegal STYLE");
	}
    }

    diFlags = active ? DI_DRAWFORE | DI_DRAWBACK
		     : DI_DRAWFORE | DI_DRAWBACK | DI_GREY;
    switch (style)
    {
	case BS_PLAIN:
	    diFlags = diFlags | DI_SMALLTEXT;
	    break;
	case BS_PITTED:
	    break;
	default:
	    ReportError("DrawIconLabelledButtonObj", "Illegal STYLE");
    }

    if (style == BS_PLAIN)
    {
	if (!active) setpattern(GREYPAT);
	DrawRaisedRect(left, right, bottom, top,
	    hilight ? UIHIBACKGROUND : UIBACKGROUND);
	if (!active) setpattern(SOLIDPAT);
    }
    DrawIcon((left + right) / 2, (top + bottom) / 2 + offset,
	icon, label, (char *) NIL, color, diFlags);

    if (active)
    {
	extraDraw = GetMethod(theButton, ICONEXTRADRAW);
	if (extraDraw)
	{
	    (* extraDraw) (theButton, (int) ((left + right) / 2), (int) ((top + bottom) / 2) + offset);
	}
    }

    return ObjTrue;

#endif /* GRAPHICS */
}

static ObjPtr SetButtonValuePretend(theButton, theValue)
ObjPtr	theButton, theValue;
{
    FuncTyp	changer;

    /* value is ignored, button just gets pressed */

    InhibitLogging(true);
    ChangedValue(theButton);
    InhibitLogging(false);

    ImInvalid(theButton);
    if (logging) LogControl(theButton);
    return ObjTrue;
}

static ObjPtr SetButtonValueReally(theButton, theValue)
ObjPtr	theButton, theValue;
{
    FuncTyp	changer;

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

    ImInvalid(theButton);
    if (logging) LogControl(theButton);
    return ObjTrue;
}

#ifdef PROTO
Bool	MakeButtonToggled(ObjPtr theButton, Bool flag)
#else
Bool	MakeButtonToggled(theButton, flag)
ObjPtr theButton;
Bool flag;
#endif
{
    if (flag)
    {
	return SetMethod(theButton, SETVAL, SetButtonValueReally);
    }
    else
    {
	return SetMethod(theButton, SETVAL, SetButtonValuePretend);
    }
}

Bool ToggleButtonActivated(ObjPtr theButton)
{
    SetVar(theButton, ACTIVATED, NewInt(!GetPredicate(theButton, ACTIVATED)));
    ImInvalid(theButton);
    return true;
}

#ifdef PROTO
Bool ActivateButton(ObjPtr theButton, Bool flag)
#else
Bool ActivateButton(theButton, flag)
ObjPtr theButton;
Bool flag;
#endif
{
    if (flag == GetInt(GetIntVar("ActivateButton", theButton, ACTIVATED)))
    {
	return false;
    }
    SetVar(theButton, ACTIVATED, NewInt(flag));
    ImInvalid(theButton);
    return true;
}

/* SetRadioButtonGroupValue: Radio button group SETVAL method */

static ObjPtr SetRadioButtonGroupValue(theGroup, theValue)
ObjPtr	theGroup, theValue;
{
    ObjPtr	buttonList;	/* the Radio's list of buttons */
    ThingListPtr	runner;		/* list runner */
    int		buttonNo;	/* counts things as runner runs */
    ObjPtr	oneThing;	/* one thing in the list */
    int		value;		/* value of theValue */

    if (IsInt(theValue))
    {
	value = GetInt(theValue);
    }
    else if (IsReal(theValue))
    {
	value = GetReal(theValue);
    }
    else
    {
	return ObjFalse;
    }
    SetVar(theGroup, VALUE, NewInt(value));

    /* run down list of buttons and set all but one false. */
    buttonList = GetListVar("SetRadioButtonGroupValue", theGroup, CONTENTS);
    if (!buttonList)
    {
	return ObjFalse;
    }
    
    runner = LISTOF(buttonList);
    buttonNo = 0;

    /* find the button in the CONTENTS list */
    while (runner && buttonNo < value)
    {
	runner = runner -> next;
	++buttonNo;
    }

    if (!runner)
    {
	ReportError("SetRadioButtonGroupValue","tried to set to illegal value");
	SetVar(theGroup, VALUE, NewInt(-1));
	ChangedValue(theGroup);
	ImInvalid(theGroup);
	return ObjFalse;
    }

    oneThing = runner -> thing;

    InhibitLogging(true);
    ButtonPressed(oneThing);
    ChangedValue(theGroup);
    InhibitLogging(false);

    if (logging) LogControl(theGroup);

    ImInvalid(theGroup);
    return ObjTrue;
}

ObjPtr	NewButton(left, right, bottom, top, label)
int	left, right, bottom, top;
char *label;
/*Makes a new panel with bounds left, right, bottom, top, and label label*/
{
    ObjPtr	retVal;
    ObjPtr	labelPtr;
    ObjPtr	hilightPtr;

    retVal = NewObject(buttonClass, 0);
    if(retVal)
    {
	labelPtr = NewString(label);
	Set2DIntBounds(retVal, left, right, bottom, top);
	hilightPtr = NewInt(false);
	SetVar(retVal, NAME, labelPtr);
	SetVar(retVal, HIGHLIGHTED, hilightPtr);
	SetVar(retVal, VALUE, NewInt(false));
	SetVar(retVal, ACTIVATED, NewInt(true));
	return retVal;
    }
    else
    {
	return ObjFalse;
    }
}

static ObjPtr DrawRadioButtonObj(theButton)
ObjPtr	theButton;
{
#ifdef GRAPHICS
    int left, right, bottom, top;
    Bool	hilight, active;
    Bool	value;
    int		marker;
    char	*label;

    if (!Get2DIntBounds(theButton, &left, &right, &bottom, &top))
    {
        return NULLOBJ;
    }

    if (IsDrawingRestricted(left, right, bottom, top))
    {
	return ObjFalse;
    }

    hilight = GetPredicate(theButton, HIGHLIGHTED);
    active = GetPredicate(theButton, ACTIVATED);
    marker = GetInt(GetIntVar("DrawRadioButtonObj", theButton, MARKERTYPE));
    label = GetString(GetStringVar("DrawRadioButtonObj",theButton, NAME));
    value = GetInt(GetIntVar("DrawRadioButtonObj", theButton, VALUE));

    if (!active)
	setpattern(GREYPAT);

    DrawRaisedRect(left, left + (top - bottom), bottom, top, hilight ? UIHIBACKGROUND : UIBACKGROUND);
    if (value)
    {
	switch(marker)
	{
	    case MARKERX:
		DrawMarkerX(left+BUTTONDEPTH, left+(top-bottom) - BUTTONDEPTH,
				bottom + BUTTONDEPTH, top - BUTTONDEPTH);
		break;
	    case MARKERCHECK:
		DrawMarkerCheck(left+BUTTONDEPTH, left+(top-bottom)-BUTTONDEPTH,
				bottom + BUTTONDEPTH, top - BUTTONDEPTH);
		break;
	    case MARKERBLOT:
		DrawMarkerBlot(left+BUTTONDEPTH,left+(top-bottom)-BUTTONDEPTH,
                                bottom + BUTTONDEPTH, top - BUTTONDEPTH);
                break;
	    case MARKERSPLAT:
		DrawMarkerSplat(left+BUTTONDEPTH,left+(top-bottom)-BUTTONDEPTH,
                                bottom + BUTTONDEPTH, top - BUTTONDEPTH);
                break;
	}
    }
    if (!active)
	setpattern(SOLIDPAT);

    if (label)
    {
	if (active)
	    SetUIColor(UITEXT);
	else
	    SetUIColor(UIGREYTEXT);

	SetupFont(BUTTONFONT, BUTTONFONTSIZE);
        DrawAString(LEFTALIGN, left + (top - bottom) + RADIOSPACER,
                   (top+bottom)/2 + LABELFONTFUDGE - (int) BUTTONFONTSIZE/2,
                   label);
    }
#endif /* GRAPHICS */
}

#ifdef PROTO
static Bool TrackMouse(ObjPtr obj, int whichButton)
#else
static Bool TrackMouse(obj, whichButton)
ObjPtr	obj;
int	whichButton;
#endif
{
    int		mX, mY;		/* track mouse coordinates */
    Bool	isDown;		/* return value from Mouse, true if down */
    int		left, right, bottom, top; /* non-array version */

    if (!Get2DIntBounds(obj, &left, &right, &bottom, &top))
    {
        return false;
    }

    /* the buggy version? */
    while (Mouse(&mX, &mY))
    {
	if (mX < left || mX > right || mY < bottom || mY > top)
	{
	    /* mouse outside rectangle */
	    if (GetPredicate(obj, HIGHLIGHTED))
	    {
		SetVar(obj, HIGHLIGHTED, NewInt(false));/* turn highlight off */
		DrawMe(obj);		/* ..and redraw */
	    }
	}
	else
	{
	    /* mouse inside rectangle */
	    if (!GetPredicate(obj, HIGHLIGHTED))
	    {
		SetVar (obj, HIGHLIGHTED, NewInt(true));/* turn highlight on */
		DrawMe(obj);		/* ..and redraw */
	    }
	}
    }
    /* always turn highlight off and redraw on exit */
    SetVar(obj, HIGHLIGHTED, NewInt(false));
    ImInvalid(obj);
    /* is this obtuse or what? return true iff mouseup while in rectangle */
    return !(mX < left || mX > right || mY < bottom || mY > top);
}

static ObjPtr ButtonPressedButton(theButton)
/* ButtonPressedButton: BUTTONPRESSED Method for vanilla buttons.
	It only toggles the VALUE token of the button. returns true if
	successful, false if not.
*/
ObjPtr	theButton;
{
    ObjPtr	theValue;	/* pointer to object's VALUE int */
    int		value;		/* value of it's VALUE int */

    theValue = GetIntVar("ButtonPressedButton",theButton, VALUE);
    if (!theValue)
    {
	return ObjFalse;
    }
    value = GetInt(theValue);
    theValue = NewInt(value ? false : true);
    SetValue(theButton, theValue);

    return ObjTrue;
}

static ObjPtr ButtonPressedRadioButton(theButton)
ObjPtr theButton;
{
    ObjPtr	buttonList;	/* the Radio's list of buttons */
    ObjPtr	parent;		/* button's parent */
    ThingListPtr	runner;		/* list runner */
    ObjPtr	oneThing;	/* ont thing in the list */
    int		buttonNo;

    /* run down list of buttons and set them all false. */
    parent = GetVar(theButton, PARENT);
    if (!parent)
    {
	ReportError("ButtonPressedRadioButton","Radio button has no PARENT!\n");
	return ObjFalse;
    }

    buttonList = GetListVar("ButtonPressedRadioButton", parent, CONTENTS);
    if (!buttonList)
    {
	return ObjFalse;
    }
    else
    {
	runner = LISTOF(buttonList);
	SetVar(parent, VALUE, NewInt(-1));
	buttonNo = 0;
	while (runner)
	{
    	    ThingPtr oneThing;

	    oneThing = runner -> thing;
	    if (GetPredicate(oneThing, VALUE))
	    {
		SetValue(oneThing, NewInt(false));
		/* note there's a quirk here if old button = new button */
	    }
	    if (runner -> thing == theButton)
	    {
		SetVar(parent, VALUE, NewInt(buttonNo));
	    }
	    ++buttonNo;
	    runner = runner -> next;
	}
    }

    SetValue(theButton, NewInt(true));
    ChangedValue(parent);
    return ObjTrue;
}

/* PressButton: button PRESS method. if mouse down in my rectangle, track
	the mouse and (if up in my rectangle) change the value of the button.
	returns true if mouse down in my rectangle, regardless of whether mouse
	up happened in it.
*/
static ObjPtr PressButton(theButton, mouseX, mouseY, flags)
ObjPtr	theButton;
int	mouseX, mouseY;
long	flags;

{

#ifdef INTERACTIVE
    int left, right, bottom, top;

    if (!Get2DIntBounds(theButton, &left, &right, &bottom, &top))
    {
        return ObjFalse;
    }

    /* test if mouse in my rectangle */
    if (mouseX < left || mouseX > right ||
	mouseY < bottom || mouseY > top)
    {
	/* mouse out of my rectangle, do nothing, return */
	return ObjFalse;
    }

    if (TOOL(flags) == T_HELP)
    {
	ContextHelp(theButton);
	return ObjTrue;
    }

    if (!GetPredicate(theButton, ACTIVATED))
    {
	return ObjFalse;
    }

    /* have to inhibit Undo saves for radio buttons. */
    if (!inhibitSaveForUndo)
    {
	SaveForUndo(theButton);
    }

    MakeMeCurrent(theButton);

    if (!TrackMouse(theButton, flags))
    {
	/* mouse up outside of this button, do nothing but draw me */
	ImInvalid(theButton);
	return ObjTrue; /*return true if mousedown in me,regardless of mouseup*/
    }

    ButtonPressed(theButton);

    /* we hope that logging is handled in the SETVAL method! */
    return ObjTrue;
#endif /* INTERACTIVE */
}

ObjPtr	NewRadioButton(left, right, bottom, top, label)
int	left, right, bottom, top;
char	*label;
/*Makes a new panel with bounds left, right, bottom, top, and label label,
	beloging to buttonGroup */
{
    ObjPtr	retVal;
    ObjPtr	labelPtr;
    ObjPtr	hilightPtr;

    retVal = NewObject(radioButtonClass, 0);
    if (retVal)
    {
	Set2DIntBounds(retVal, left, right, bottom, top);
	labelPtr = NewString(label);
	SetVar(retVal, NAME, labelPtr);
	hilightPtr = NewInt(false);
	SetVar(retVal, HIGHLIGHTED, hilightPtr);
	SetVar(retVal, VALUE, NewInt(false));
	SetVar(retVal, ACTIVATED, NewInt(true));
	return retVal;
    }
    else
    {
	return ObjFalse;
    }
}

/* Makes a new check box with bounds left, right, bottom, top, and label label*/

#ifdef PROTO
ObjPtr	NewCheckBox(int left, int right, int bottom, int top, char *label,
		    Bool initValue)
#else
ObjPtr	NewCheckBox(left, right, bottom, top, label, initValue)
int	left, right, bottom, top;
Bool	initValue;
char *label;
#endif
{
    ObjPtr	retVal;
    ObjPtr	labelPtr;
    ObjPtr	hilightPtr;

    retVal = NewObject(checkBoxClass, 0);
    if (retVal)
    {
	Set2DIntBounds(retVal, left, right, bottom, top);
	labelPtr = NewString(label);
	SetVar(retVal, NAME, labelPtr);
	hilightPtr = NewInt(false);
	SetVar(retVal, HIGHLIGHTED, hilightPtr);
	SetVar(retVal, VALUE, NewInt(initValue));
	SetVar(retVal, ACTIVATED, NewInt(true));
	return retVal;
    }
    else
    {
	return ObjFalse;
    }
}

static ObjPtr ReshapeRadioButtonGroup(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.*/
{
	ObjPtr boundsArray;
	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*/

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

	boundsArray = GetVar(object, BOUNDS);
	if (!boundsArray || !IsArray(boundsArray) || RANK(boundsArray) != 1 ||
	    DIMS(boundsArray)[0] != 4)
	{
	    return;
	}
	Array2CArray(bounds, boundsArray);
	Array2CArray(oldBounds, boundsArray);
	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*/
	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),
		    (int) oldBounds[0], (int) oldBounds[1] ,
		    (int) oldBounds[2], (int) oldBounds[3],
		    (int) bounds[0], (int) bounds[1],
		    (int) bounds[2], (int) bounds[3]);
	}
}


static ObjPtr DrawRadioButtonGroup(theGroup)
ObjPtr	theGroup;
{
#ifdef GRAPHICS
    ObjPtr	theList;

    theList = GetListVar("DrawRadioButtonGroup", theGroup, CONTENTS);
    if (!theList)
    {
	return NULLOBJ;
    }

    DrawList(theList);
    return NULLOBJ;
#endif /* GRAPHICS */
}

static ObjPtr PressRadioButtonGroup(theGroup, mX, mY, flags)
ObjPtr theGroup;
int mX, mY;
long flags;
{
#ifdef INTERACTIVE
    int		left, right, bottom, top;
    ObjPtr	theList;
    ObjPtr	valuePtr;		/* radio group value ptr */
    int		oldValue;		/* group's old value */
    FuncTyp	bpmethod;		/* BUTTONPRESSED method */
    ObjPtr	pressVal;		/* thing returned by PressList */

    theList = GetListVar("PressRadioButtonGroup", theGroup, CONTENTS);
    if (!theList)
    {
	return ObjFalse;
    }

    if (!Get2DIntBounds(theGroup, &left, &right, &bottom, &top))
    {
        return ObjFalse;
    }

    valuePtr = GetIntVar("PressRadioButtonGroup", theGroup, VALUE);
    if (!valuePtr)
    {
	return ObjFalse;
    }
    oldValue = GetInt(valuePtr);

    /* test if mouse in my rectangle */
    if (mX < left || mX > right ||
	mY < bottom || mY > top)
    {
	/* mouse out of my rectangle, do nothing, return */
	return ObjFalse;
    }

    SaveForUndo(theGroup);

    inhibitSaveForUndo = true;
    pressVal = PressList(theList, mX, mY, flags);
    inhibitSaveForUndo = false;

    if(IsTrue(pressVal))
    {
	/* if really changed */
	if (oldValue != GetInt(GetIntVar("PressRadioButtonGroup",theGroup, VALUE)))
	{
	    InhibitLogging(true);
	    ButtonPressed(theGroup);
	    InhibitLogging(false);
	}
	if (logging) LogControl(theGroup);
	return ObjTrue;
    }
    else
    {
	if (TOOL(flags) == T_HELP)
	{
	    /* it was a help-press in the group bounds (that missed buttons) */
	    ContextHelp(theGroup);
	    return ObjTrue;
	}
	else
	{
	    return ObjFalse;
	}
    }
#endif /* INTERACTIVE */
}

ObjPtr	NewRadioButtonGroup(name)
char *name;
/* creates an empty radio button group */
{
    ObjPtr	retVal;

    retVal = NewObject(radioGroupClass, 0);
    if (retVal)
    {
	SetVar(retVal, CONTENTS, NewList());
	SetVar(retVal, VALUE, NewInt(-1));
	SetVar(retVal, NAME, NewString(name));
	SetVar(retVal, ACTIVATED, NewInt(true));
	return retVal;
    }
    else
    {
	return ObjFalse;
    }
}

void	AddRadioButton(theGroup, theButton)
ObjPtr	theGroup, theButton;
/* adds a radio button object to an existing radio button group */
{
    ObjPtr	groupBoundsArray;
    int		bounds[4], groupBounds[4];
    ObjPtr	theList;
    int		i;	/* temporary? loop counter */

    if (!Get2DIntBounds(theButton,&bounds[0],&bounds[1],&bounds[2],&bounds[3]))
    {
	return;
    }

    theList = GetListVar("AddRadioButton", theGroup, CONTENTS);
    if(!theList)
    {
	return;
    }
    groupBoundsArray = GetVar(theGroup, BOUNDS);
    if(!groupBoundsArray)
    {
	/*Try to make bounds. Initial value is bounds of first button*/
	Set2DIntBounds(theGroup, bounds[0], bounds[1], bounds[2], bounds[3]);
    }
    else
    {
	Get2DIntBounds(theGroup, &groupBounds[0], &groupBounds[1],
			&groupBounds[2], &groupBounds[3]);
	/* if necessary, modify the BOUNDS of the button group to include
	   the new button
	*/
	groupBounds[0] = bounds[0]<groupBounds[0] ? bounds[0] : groupBounds[0];
	groupBounds[1] = bounds[1]>groupBounds[1] ? bounds[1] : groupBounds[1];
	groupBounds[2] = bounds[2]<groupBounds[2] ? bounds[2] : groupBounds[2];
	groupBounds[3] = bounds[3]>groupBounds[3] ? bounds[3] : groupBounds[3];
	Set2DIntBounds(theGroup, groupBounds[0], groupBounds[1],
			groupBounds[2], groupBounds[3]);
    }
    PostfixList(theList, theButton);
    SetVar(theButton, PARENT, theGroup);
    SetMethod(theButton, BUTTONPRESSED, ButtonPressedRadioButton);
    SetMethod(theButton, SETVAL, SetButtonValueReally);
    /*EMP don't log individual buttons, only groups*/
    SetVar(theButton, INHIBITLOGGING, ObjTrue);
}

void	InitButtons()
/* sets up button stuff */
{
    buttonClass = NewObject(controlClass, 0);
    AddToReferenceList(buttonClass);
    SetMethod(buttonClass, DRAW, DrawButtonObj);
    SetMethod(buttonClass, PRESS, PressButton);
    SetMethod(buttonClass, BUTTONPRESSED, ButtonPressedButton);
    SetMethod(buttonClass, SETVAL, SetButtonValuePretend);
    SetVar(buttonClass, TYPESTRING, NewString("button"));
    SetVar(buttonClass, HELPSTRING, NewString(
"To press the button, click on it with the left mouse button. If you move the \
mouse away from the button before releasing, the button will not be pressed."));

    iconButtonClass = NewObject(buttonClass, 0);
    AddToReferenceList(iconButtonClass);
    SetMethod(iconButtonClass, DRAW, DrawIconButtonObj);
    SetMethod(iconButtonClass, SETVAL, SetButtonValuePretend);
    SetVar(iconButtonClass, TYPESTRING, NewString("icon button"));

    iconLabeledButtonClass = NewObject(iconButtonClass, 0);
    AddToReferenceList(iconLabeledButtonClass);
    SetMethod(iconLabeledButtonClass, DRAW, DrawIconLabeledButtonObj);
    SetVar(iconLabeledButtonClass,TYPESTRING,NewString("icon button"));

    radioGroupClass = NewObject(controlClass, 0);
    AddToReferenceList(radioGroupClass);
    SetMethod(radioGroupClass, DRAW, DrawRadioButtonGroup);
    SetMethod(radioGroupClass, PRESS, PressRadioButtonGroup);
    SetMethod(radioGroupClass, SETVAL, SetRadioButtonGroupValue);
    SetMethod(radioGroupClass, RESHAPE, ReshapeRadioButtonGroup);
    SetVar(radioGroupClass, TYPESTRING, NewString("radio button group"));
    SetVar(radioGroupClass, HELPSTRING, NewString(
"A radio button group provides a way to select one choice from among several. \
Selecting one of the buttons automatically deselects the others in the group."));

    radioButtonClass = NewObject(buttonClass, 0);
    AddToReferenceList(radioButtonClass);
    SetMethod(radioButtonClass, DRAW, DrawRadioButtonObj);
    SetVar(radioButtonClass, MARKERTYPE, NewInt(MARKERSPLAT));
    SetVar(radioButtonClass, TYPESTRING, NewString("radio button"));

    oldRadioButtonClass = NewObject(radioButtonClass, 0);
    AddToReferenceList(oldRadioButtonClass);
    SetVar(oldRadioButtonClass, MARKERTYPE, NewInt(MARKERX));

    checkBoxClass = NewObject(buttonClass, 0);
    AddToReferenceList(checkBoxClass);
    SetMethod(checkBoxClass, DRAW, DrawRadioButtonObj);
    SetMethod(checkBoxClass, SETVAL, SetButtonValueReally);
    SetVar(checkBoxClass, MARKERTYPE, NewInt(MARKERCHECK));
    SetVar(checkBoxClass, TYPESTRING, NewString("check box"));
}

void KillButtons()
/* kills button stuff */
{
    DeleteThing(checkBoxClass);
    DeleteThing(radioButtonClass);
    DeleteThing(radioGroupClass);
    DeleteThing(iconButtonClass);
    DeleteThing(buttonClass);
}

