/*	ScianSliders.c
	routines for slider-type controls

	Jim Lyons
	4/3/91 original version
	5/29/91 EMP removed lib headers
	8/12/91 modified to use ScianFontSystem calls
	8/16/91 modified to use Get/Set2DIntBounds
	9/5/91	added help
	9/27/91	added active/inactive stuff
	10/7/91	removed blind-typing from keydown routine
	10/23/91 fixed exception end-point labeling of scales
	5/20/92 EMP changed ENTERMETHOD to CHANGEDVALUE for slider readout
	5/22/92 EMP changed slider readout to use ParseReal
	5/31/92 EMP fixed / 0 bug in scroll bars
*/

#include "Scian.h"
#include "ScianTypes.h"
#include "ScianFontSystem.h"
#include "ScianArrays.h"
#include "ScianStyle.h"
#include "ScianColors.h"
#include "ScianIDs.h"
#include "ScianHelp.h"
#include "ScianErrors.h"
#include "ScianControls.h"
#include "ScianWindows.h"
#include "ScianObjWindows.h"
#include "ScianEvents.h"
#include "ScianScripts.h"
#include "ScianMethods.h"
#include "ScianDraw.h"
#include "ScianSliders.h"

#define SB_GAP	4



/* bit definitions for highlight of scrollbar parts */
#define THUMBHL	0x01
#define INCRHL	0x02
#define DECRHL	0x04

/* repeat time for scroll bar buttons */
#define DELAY	0.2

/* EMP static method declarations, NOT prototypes */
static ObjPtr DrawSlider();
static ObjPtr TrackSlider();
static ObjPtr GetVal();
static ObjPtr SetVal();
static ObjPtr SliderKeyProc();
static ObjPtr MakeHelpString();

/* static function prototypes */
#ifdef PROTO

#else

#endif

ObjPtr sliderClass;

void InitSliders()
{
	sliderClass = NewObject(controlClass, 0);
	AddToReferenceList(sliderClass);
	SetVar(sliderClass, NAME, NewString("Slider"));
	SetMethod(sliderClass, DRAW, DrawSlider);
	SetMethod(sliderClass, PRESS, TrackSlider);
	SetMethod(sliderClass, SETVAL, SetVal);
	SetMethod(sliderClass, GETVAL, GetVal);
	SetMethod(sliderClass, KEYDOWN, SliderKeyProc);
	SetMethod(sliderClass, MAKE1HELPSTRING, MakeHelpString);
}

void KillSliders()
{
	DeleteThing(sliderClass);
}

#ifdef PROTO
ObjPtr NewSlider(int left, int right, int bottom, int top, int style, char *name)
#else
ObjPtr NewSlider(left, right, bottom, top, style, name)
int	left, right, bottom, top, style;
char *name;
#endif
{
	ObjPtr newSlider;

	if (left > right)
	{
		register int n;
		n = left; left = right; right = n;
	}
	if (bottom > top)
	{
		register int n;
		n = bottom; bottom = top; top = n;
	}
	newSlider = NewObject(sliderClass, 0);
	Set2DIntBounds(newSlider, left, right, bottom, top);

	/* set up defaults for slider parameters */
	SetVar(newSlider, STYLE, NewInt(style));
	SetVar(newSlider, NAME, NewString(name));
	SetVar(newSlider, HIGHLIGHTED, NewInt(false));
	SetVar(newSlider, LOVALUE, NewReal(0.0));
	SetVar(newSlider, HIVALUE, NewReal(1.0));
	SetVar(newSlider, STEPVALUE, NewReal(0.1));
	SetVar(newSlider, VALUE, NewReal(0.0));
	SetVar(newSlider, TRACKNOT, NewInt(false));
	SetVar(newSlider, KEYSTRING, NewString("\0"));
	SetVar(newSlider, READOUT, NULLOBJ);
	SetVar(newSlider, TYPESTRING, NewString("slider"));
	SetVar(newSlider, ACTIVATED, ObjTrue);
	switch (style)
	{
		case SCALE:
		case REVSCALE:
			SetVar(newSlider, BIGSTEP, NewReal(0.5));
			SetVar(newSlider, LILSTEP, NewReal(0.1));
			SetVar(newSlider, ANCHOR, NewReal(0.0));
			SetVar(newSlider, FORMAT, NewString("%3.1f"));
			break;
		default:
			SetVar(newSlider, STYLE, NewInt(PLAIN));
			break;
	}
	return newSlider;
}

#ifdef PROTO
ObjPtr NewScrollbar(int left, int right, int bottom, int top, char *name)
#else
ObjPtr NewScrollbar(left, right, bottom, top, name)
int	left, right, bottom, top;
char *name;
#endif
{
	ObjPtr newScrollbar;

	if (left > right)
	{
		register int n;
		n = left; left = right; right = n;
	}
	if (bottom > top)
	{
		register int n;
		n = bottom; bottom = top; top = n;
	}
	newScrollbar = NewObject(sliderClass, 0);
	Set2DIntBounds(newScrollbar, left, right, bottom, top);

	/* set up defaults for scrollbar parameters */
	SetVar(newScrollbar, STYLE, NewInt(SCROLL));
	SetVar(newScrollbar, NAME, NewString(name));
	SetVar(newScrollbar, HIGHLIGHTED, NewInt(0x00)); /* see bit defns */
	SetVar(newScrollbar, LOVALUE, NewReal(0.0));
	SetVar(newScrollbar, HIVALUE, NewReal(0.0)); /* arbitrary */
	SetVar(newScrollbar, STEPVALUE, NewReal(0.1));
	SetVar(newScrollbar, VALUE, NewReal(0.0));
	SetVar(newScrollbar, PORTIONSHOWN, NewReal(1.0));
	SetVar(newScrollbar, TRACKNOT, NewInt(false));
	SetVar(newScrollbar, KEYSTRING, NewString("\0"));
	SetVar(newScrollbar, TYPESTRING, NewString("scrollbar"));
	SetVar(newScrollbar, ACTIVATED, ObjTrue);
	return newScrollbar;
}

static ObjPtr MakeHelpString(theSlider, theClass)
ObjPtr theSlider, theClass;
{
	char tempBuf[1000]; /*** fix! ***/
	int style = GetInt(GetVar(theSlider, STYLE));
	if (style == SCROLL)
		strcpy(tempBuf, "To operate the scrollbar, drag the sliding bar with the \
mouse or click in the track to snap it to a new position. Click and hold the arrow \
buttons to scroll automatically. The size of the sliding bar in the control \
relative to the track is proportional to the area being viewed relative to the entire \
scrollable area. If the bar is as long as the track, the entire area is in view and \
the scrollbar does nothing.");
	else
	{
		strcpy(tempBuf, "To operate the slider, drag the pointer \
with the mouse or click in the track to snap it to a new position. After clicking on the \
slider, the arrow keys on the keyboard will also move the pointer. ");
		if (style == SCALE || style == REVSCALE)
			strcat(tempBuf, "The scale shows the range of values possible \
for this slider. Holding the shift key down while dragging the pointer constrains the \
slider to values on the tic marks of the scale. "); /*** fix! ***/
	}
	SetVar(theClass, HELPSTRING, NewString(tempBuf));
	return ObjTrue;
}

#ifdef PROTO
void ActivateSlider(ObjPtr slider, Bool act)
#else
void ActivateSlider(slider, act)
ObjPtr slider;
Bool act;
#endif
{
    if (act) SetVar(slider, ACTIVATED, ObjTrue);
    else
    {
        SetVar(slider, ACTIVATED, ObjFalse);
        if (AmICurrent(slider)) MakeMeCurrent(NULLOBJ);
    }
    ImInvalid(slider);
}

#ifdef PROTO
void SetTrackNot(ObjPtr theSlider, int value)
#else
void SetTrackNot(theSlider,value)
ObjPtr theSlider;
int value;
#endif
{
	SetVar(theSlider, TRACKNOT, NewInt(value));
}

#ifdef PROTO
static void Update(ObjPtr theReadout, real val)
#else
static void Update(theReadout, val)
ObjPtr theReadout;
real val;
#endif
{
	if(logging) InhibitLogging(true);
	if(theReadout)
	{
		/*EMP kluge to prevent infinite loop*/
		FuncTyp method;
		sprintf(tempStr,"%g",val);
		method = GetMethod(theReadout, CHANGEDVALUE);
		SetMethod(theReadout, CHANGEDVALUE, (FuncTyp) 0);
		SetTextBox(theReadout, tempStr);
		SetMethod(theReadout, CHANGEDVALUE, method);
	}
	if(logging) InhibitLogging(false);
}

static ObjPtr EditSlider(readout)
ObjPtr readout;
{
	ObjPtr slider;
	real val;

	if (slider = GetObjectVar("EditSlider", readout, REPOBJ))
	{
		if (logging) InhibitLogging(true);
		strcpy(tempStr, GetString(GetVar(readout, VALUE)));
		if (!ParseReal(&val, tempStr)) /* error in input */
		{
			return ObjFalse;
		}
		if (val == missingData)
		{
			DoUniqueTask(DoMissingError);
			return ObjFalse;
		}
		if (val >= plusInf || val <= minusInf)
		{
			DoUniqueTask(DoInfinityError);
			return ObjFalse;
    		}
		SetSliderValue(slider, val);
		if (logging) InhibitLogging(false);
	}
	return ObjTrue;
}

#ifdef PROTO
ObjPtr SliderReadout(ObjPtr theSlider, ObjPtr theReadout)
#else
ObjPtr SliderReadout(theSlider, theReadout)
ObjPtr theSlider, theReadout;
#endif
{
	SetVar(theSlider, READOUT, theReadout);
	SetVar(theReadout, REPOBJ, theSlider);
	SetMethod(theReadout, CHANGEDVALUE, EditSlider);
	Update(theReadout, GetSliderValue(theSlider));
	return NULLOBJ;
}

static ObjPtr GetVal(theSlider)
ObjPtr theSlider;
{
	return GetVar(theSlider, VALUE);
}

#ifdef PROTO
real GetSliderValue(ObjPtr theSlider)
#else
real GetSliderValue(theSlider)
ObjPtr theSlider;
#endif
{
	ObjPtr theVal;

	theVal = GetRealVar("GetSliderValue", theSlider, VALUE);
	if (theVal) return GetReal(theVal);
	else return 0;	/* Yow! No value! */
}

static ObjPtr SetVal(theObj, theVal)
ObjPtr theObj, theVal;
{
	if (IsReal(theVal)) SetSliderValue(theObj, GetReal(theVal));
	else if (IsInt(theVal)) SetSliderValue(theObj, (real) GetInt(theVal));
	else return ObjFalse; /* error in argument */
	if (logging) LogControl(theObj);
	return ObjTrue;
}

#ifdef PROTO
ObjPtr SetSliderValue(ObjPtr theSlider, real val)
#else
ObjPtr SetSliderValue(theSlider, val)
ObjPtr theSlider;
real val;
#endif
{
	ObjPtr theHiV, theLoV, theStepV;
	ObjPtr theReadout, theStyle;
	real hiVal, loVal, stepVal;
	int style;

	/* sanity check */
	theHiV = GetRealVar("SetSliderValue", theSlider, HIVALUE);
	theLoV = GetRealVar("SetSliderValue", theSlider, LOVALUE);
	theStepV = GetRealVar("SetSliderValue", theSlider, STEPVALUE);
	theStyle = GetIntVar("SetSliderValue", theSlider, STYLE);
	if (!theHiV || !theLoV || !theStepV || !theStyle) return NULLOBJ; /* Yow! Bad. Very bad. */
	hiVal = GetReal(theHiV);
	loVal = GetReal(theLoV);
	stepVal = GetReal(theStepV);
	style = GetInt(theStyle);
	theReadout = GetVar(theSlider, READOUT);
	if ( style != SCROLL && stepVal != 0.0)
	{
		register int n;
		n = 0.5 + val / stepVal;
		val = n * stepVal;
	}
	/* adjust range if value out of it */
	if (val > hiVal) SetVar(theSlider, HIVALUE, NewReal(val));
	if (val < loVal) SetVar(theSlider, LOVALUE, NewReal(val));
	SetVar(theSlider, VALUE, NewReal(val));
	Update(theReadout, val);
	ImInvalid(theSlider);
/*** KLUDGE so Eric can speed up drawing ***/
	{
		ObjPtr parent = GetVar(theSlider, PARENT);
		if (parent) ImInvalid(parent);
	}
	ChangedValue(theSlider);
	return NULLOBJ;
}

#ifdef PROTO
void SetPortionShown(ObjPtr theSlider, real porSh)
#else
void SetPortionShown(theSlider, porSh)
ObjPtr theSlider;
real porSh;
#endif
{
	if (porSh < 0.0) porSh = 0.0;
	
	SetVar(theSlider, PORTIONSHOWN, NewReal(porSh));
}

static ObjPtr SliderKeyProc(theSlider, key, flags)
ObjPtr theSlider;
int key;
long flags;
{
#ifdef INTERACTIVE
	ObjPtr theHiV, theLoV, theStepV, theVal, theStyle;
	real hiV, loV, stepV, val;
	int style;
	ObjPtr theReadout;

	if(!AmICurrent(theSlider)) return ObjFalse; /* keyboard input not for me */

	theHiV = GetRealVar("SliderKeyProc", theSlider, HIVALUE);
	theLoV = GetRealVar("SliderKeyProc", theSlider, LOVALUE);
	theStepV = GetRealVar("SliderKeyProc", theSlider, STEPVALUE);
	theVal= GetRealVar("SliderKeyProc", theSlider, VALUE);
	theStyle = GetIntVar("SliderKeyProc", theSlider, STYLE);
	if (!theHiV || !theLoV || !theStepV || !theVal || !theStyle)
		return ObjTrue;	/* Yow! Bad, wrong, deep doo-doo */
	theReadout = GetVar(theSlider, READOUT);
	hiV = GetReal(theHiV);
	loV = GetReal(theLoV);
	stepV = GetReal(theStepV);
	val = GetReal(theVal);
	style = GetInt(theStyle);

	/* process arrow keys only */
	if (key == FK_UP_ARROW || key == FK_RIGHT_ARROW)
	{
		/* increment slider value by stepV */
		if (stepV == 0.0) val += (hiV - loV)/100;
		else val += stepV;
		if (val <= hiV) 
		{
			SetVar(theSlider, VALUE, NewReal(val));
			Update(theReadout, val);
			ImInvalid(theSlider);
			ChangedValue(theSlider);
		}
	}
	else if (key == FK_DOWN_ARROW || key == FK_LEFT_ARROW)
	{
		/* decrement slider value by stepV */
		if (stepV == 0.0) val -= (hiV - loV)/100;
		else val -= stepV;
		if (val >= loV)
		{
			SetVar(theSlider, VALUE, NewReal(val));
			Update(theReadout, val);
			ImInvalid(theSlider);
			ChangedValue(theSlider);
		}
	}
	else return ObjFalse; /* not one of my keys -- pass it */
#endif
	return ObjTrue;
}

#ifdef PROTO
void SetSliderRange(ObjPtr theSlider, real hiVal, real loVal, real stepVal)
#else
void SetSliderRange(theSlider, hiVal, loVal, stepVal)
ObjPtr theSlider;
real hiVal, loVal, stepVal;
#endif
{
	ObjPtr theVal,theStyle;
	real val;
	int style;

	theVal = GetRealVar("SetSliderRange", theSlider, VALUE);
	theStyle = GetIntVar("SetSliderRange", theSlider, STYLE);
	if (!theVal || !theStyle) return; /* Yow! */
	val = GetReal(theVal);
	style = GetInt(theStyle);
	/* adjust range values if current value would be out of range */
	if (val < loVal) loVal = val;
	if (val > hiVal) hiVal = val;
	if (stepVal > hiVal - loVal) stepVal = hiVal - loVal;
	else if (style != SCROLL && stepVal != 0.0)
	{
		int n;
		n = ((hiVal - loVal) / stepVal) + 0.5;
		hiVal = n * stepVal + loVal;
	}
	SetVar(theSlider, HIVALUE, NewReal(hiVal));
	SetVar(theSlider, LOVALUE, NewReal(loVal));
	SetVar(theSlider, STEPVALUE, NewReal(stepVal));
	ImInvalid(theSlider);
/*** KLUDGE so Eric can speed up drawing ***/
	{
		ObjPtr parent = GetVar(theSlider, PARENT);
		if (parent) ImInvalid(parent);
	}
}

#ifdef PROTO
void SetSliderScale(ObjPtr theSlider, real bigStep, real lilStep, real anchor, char *format)
#else
void SetSliderScale(theSlider, bigStep, lilStep, anchor, format)
ObjPtr theSlider;
real bigStep, lilStep, anchor;
char *format;
#endif
{
	SetVar(theSlider, BIGSTEP, NewReal(bigStep));
	SetVar(theSlider, LILSTEP, NewReal(lilStep));
	SetVar(theSlider, ANCHOR, NewReal(anchor));
	SetVar(theSlider, FORMAT, NewString(format));
}

static ObjPtr DrawSlider(theSlider)
ObjPtr	theSlider;
{
#ifdef GRAPHICS
	int cp,cl,sl;
	int wid;
	long u[2], v[2];
	Coord x[3][2];
	ObjPtr theHiV, theLoV, theStepV, theVal, theHilite, theStyle, thePorSh;
	ObjPtr theBigStep, theLilStep, theAnchor, theFormat;
	int left, right, bottom, top, style;
	int i, j, nl, nh, m;
	int active, highlight;
	real hiV, loV, stepV, val, porSh, bigStep, lilStep, anchorV, sV, siz, r;
	real anchorC, hiC, loC;	/* Coordinate of scale hi, lo, and anchor point */
	real minC = -1e6, maxC = 0; /*** don't ask... */
	char *format, theNumber[10];

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

	/* get the style and value parameters */
	theHiV = GetRealVar("DrawSlider", theSlider, HIVALUE);
	theLoV = GetRealVar("DrawSlider", theSlider, LOVALUE);
	theStepV = GetRealVar("DrawSlider", theSlider, STEPVALUE);
	theVal= GetRealVar("DrawSlider", theSlider, VALUE);
	theHilite = GetIntVar("DrawSlider", theSlider, HIGHLIGHTED);
	theStyle = GetIntVar("DrawSlider", theSlider, STYLE);
	if (!theHiV || !theLoV || !theStepV || !theVal || !theHilite || !theStyle)
		return NULLOBJ;	/* Yow! Bad, wrong, doo-doo */
	hiV = GetReal(theHiV);
	loV = GetReal(theLoV);
	stepV = GetReal(theStepV);
	val = GetReal(theVal);
	highlight = GetInt(theHilite);
	active = GetPredicate(theSlider, ACTIVATED);
	style = GetInt(theStyle);
	switch (style)
	{
		case PLAIN:
			break;

		case SCALE:
		case REVSCALE:
			theBigStep = GetRealVar("DrawSlider", theSlider, BIGSTEP);
			theLilStep = GetRealVar("DrawSlider", theSlider, LILSTEP);
			theAnchor = GetRealVar("DrawSlider", theSlider, ANCHOR);
			theFormat = GetStringVar("DrawSlider", theSlider, FORMAT);
			if (!theBigStep || !theLilStep || !theAnchor || !theFormat)
				return NULLOBJ;	/* Yow! Missing things! */
			bigStep = GetReal(theBigStep);
			lilStep = GetReal(theLilStep);
			anchorV = GetReal(theAnchor);
			format = GetString(theFormat);
			SetupFont("Helvetica", SCALEFONTSIZE); /***/
			break;

		case SCROLL:
			thePorSh = GetRealVar("DrawSlider", theSlider, PORTIONSHOWN);
			if (thePorSh) porSh = GetReal(thePorSh);
			else return NULLOBJ; /* Yow! Something's rong */
			/* normalize portion shown */
			porSh = porSh/(porSh + hiV - loV);
			break;

		default:
			style = PLAIN;
			SetVar(theSlider, STYLE, NewInt(PLAIN));
			break;
	}

	/* so draw it already */
	if (right - left > top - bottom)
	{
		/* horizontal orientation */
		cl = (bottom + top) / 2;
		switch(style)
		{
			case SCALE:
				DrawSunkenRect(left, right, cl-TRK_WID/2, cl+TRK_WID/2,
					active ? UIPGREEN : UIBACKGROUND);
				loC = left + CTL_WID/2;
				hiC = right - CTL_WID/2;
				r = (hiC - loC) / (hiV - loV);
				anchorC = loC + r*(anchorV - loV);
				cp = loC + r*(val - loV) + 0.5;
				/* draw scale over slider */
				sl = top;
				u[0] = loC; u[1] = sl;
				v[0] = hiC; v[1] = sl;
				SetUIColor(active ? UITEXT : UIGREYTEXT);
				bgnline(); v2i(u); v2i(v); endline();
				nl = (loV - anchorV)/bigStep - 1;
				nh = (hiV - anchorV)/bigStep + 1;
				for (i=nl; i<=nh; ++i)
				{
					sV = i*bigStep + anchorV; /* starting value for each step */
					if (sV >= loV && sV <= hiV)
					{
						u[0] = v[0] = anchorC + r*(sV - anchorV) + 0.5;
						v[1] = sl + BIGTICK;
						bgnline(); v2i(u); v2i(v); endline();

						/* draw number next to tick */
						sprintf(theNumber, format, sV);
						wid = StrWidth(theNumber);
						DrawString(u[0] - wid/2, sl + BIGTICK + 3, theNumber);

						/* remember min and max coord of labels */
						if (minC < -1e5) minC = u[0] - wid/2;
						maxC = u[0] + wid/2;
					}
					if (i == nh) break;
					v[1] = sl + LILTICK;
					m = bigStep/lilStep + 0.5;
					for (j=1; j<m; ++j)
					{
						u[0] = v[0] = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
						if (v[0] > hiC + 1) break;
						if (v[0] >= loC) 
						{
							bgnline(); v2i(u); v2i(v); endline();
						}
					}
				}
				/* check if end points need labels */
				sprintf(theNumber, format, loV);
				wid = StrWidth(theNumber);
				if (loC + wid/2 < minC) /* label low end of scale */
				{
					DrawString(loC - wid/2, sl + BIGTICK + 3, theNumber);
				}
				sprintf(theNumber, format, hiV);
				wid = StrWidth(theNumber);
				if (hiC - wid/2 > maxC) /* label high end of scale */
				{
					DrawString(hiC - wid/2, sl + BIGTICK + 3, theNumber);
				}
				DrawCtlUp(cp-CTL_WID/2, cp+CTL_WID/2, bottom, sl-2,
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case REVSCALE:
				DrawSunkenRect(left, right, cl-TRK_WID/2, cl+TRK_WID/2,
					active ? UIPGREEN : UIBACKGROUND);
				loC = left + CTL_WID/2;
				hiC = right - CTL_WID/2;
				r = (hiC - loC) / (hiV - loV);
				anchorC = loC + r*(anchorV - loV);
				cp = loC + r*(val - loV) + 0.5;
				/* draw scale under slider */
				sl = bottom;
				u[0] = loC; u[1] = sl;
				v[0] = hiC; v[1] = sl;
				SetUIColor(active ? UITEXT : UIGREYTEXT);
				bgnline(); v2i(u); v2i(v); endline();
				nl = (loV - anchorV)/bigStep - 1;
				nh = (hiV - anchorV)/bigStep + 1;
				for (i=nl; i<=nh; ++i)
				{
					sV = i*bigStep + anchorV;	/* "starting" value for each step */
					if (sV >= loV && sV <= hiV)
					{
						u[0] = v[0] = anchorC + r*(sV - anchorV) + 0.5;
						v[1] = sl - BIGTICK;
						bgnline(); v2i(u); v2i(v); endline();

						/* draw number next to tick */
						sprintf(theNumber, format, sV);
						wid = StrWidth(theNumber);
						DrawString(u[0] - wid/2, sl - BIGTICK - 3, theNumber);
					}
					if (i == nh) break;
					v[1] = sl - LILTICK;
					m = bigStep/lilStep + 0.5;
					for (j=1; j<m; ++j)
					{
						u[0] = v[0] = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
						if (v[0] > hiC) break;
						if (v[0] >= loC)
						{
							bgnline(); v2i(u); v2i(v); endline();
						}
					}
				}
				DrawCtlDown(cp-CTL_WID/2, cp+CTL_WID/2, sl+2, top,
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case SCROLL:
				wid = top - bottom;
				loC = left + wid + EDGE + SB_GAP;
				hiC = right - wid - EDGE - SB_GAP;
				siz = (hiC - loC)*porSh;	/* size of thumb */
				if (siz < wid) siz = wid;	/* min size is square */
				hiC -= siz; /* lower limit of posn of thumb */
				if (hiV <= loV) sV = loC;
				else sV = loC + (val-loV)*(hiC - loC)/(hiV - loV) + 0.5;
				/* draw left-button */
				DrawRaisedRect(left, left+wid, bottom, top, 
					DECRHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				/* (draw left arrow on top) */
				FillUITri(left + 0.28*wid, cl,
					left + 0.61*wid, cl - wid/3,
					left + 0.61*wid,cl + wid/3,
					active ? UITEXT : UIGREYTEXT);

				/* draw right-button */
				DrawRaisedRect(right-wid, right, bottom, top, 
					INCRHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				/* (draw right arrow on top) */
				FillUITri(right - 0.28*wid, cl,
					right - 0.61*wid, cl - wid/3,
					right - 0.61*wid, cl + wid/3,
					active ? UITEXT : UIGREYTEXT);

				/* draw track */
				DrawSunkenRect(left+wid+SB_GAP, right-wid-SB_GAP, bottom, top,
					active ? UIPGREEN : UIBACKGROUND);

				/* draw thumb */
				DrawRaisedRect(sV, sV+siz, bottom+EDGE, top-EDGE,
				 THUMBHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case PLAIN:
			default:
				DrawSunkenRect(left, right, cl-TRK_WID/2, cl+TRK_WID/2,
					active ? UIPGREEN : UIBACKGROUND);
				loC = left + CTL_WID/2;
				hiC = right - CTL_WID/2;
				r = (hiC - loC) / (hiV - loV);
				cp = loC + r*(val - loV) + 0.5;
				DrawRaisedRect(cp-CTL_WID/2, cp+CTL_WID/2, bottom, top,
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;
		}
	}
	else
	{
		/* vertical orientation */
		cl = (left + right)/2;
		switch(style)
		{
			case SCALE:
				DrawSunkenRect(cl - TRK_WID/2, cl + TRK_WID/2, bottom, top,
					active ? UIPGREEN : UIBACKGROUND);
				loC = bottom + CTL_WID/2;
				hiC = top - CTL_WID/2;
				r = (hiC - loC)/(hiV - loV);
				anchorC = r*(anchorV - loV) + loC;
				cp = loC + r*(val-loV) + 0.5;
				/* draw scale on left of slider */
				sl = left;
				u[1] = loC; u[0] = sl;
				v[1] = hiC; v[0] = sl;
				SetUIColor(active ? UITEXT : UIGREYTEXT);
				bgnline(); v2i(u); v2i(v); endline();
				nl = (loV - anchorV)/bigStep - 1;
				nh = (hiV - anchorV)/bigStep + 1;
				for (i=nl; i<=nh; ++i)
				{
					sV = i*bigStep + anchorV; /* starting value for each step */
					if (sV >= loV && sV <= hiV)
					{
						u[1] = v[1] = anchorC + r*(sV - anchorV) + 0.5;
						v[0] = sl - BIGTICK;
						bgnline(); v2i(u); v2i(v); endline();

						/* draw number next to tick */
						sprintf(theNumber, format, sV);
						wid = StrWidth(theNumber);
						DrawString(sl - BIGTICK - 3 - wid, v[1] - 3, theNumber);

						/* remember min and max coords of labels */
						if (minC < -1e5) minC = v[1] - 3;
						maxC = v[1] - 3 + SCALEFONTSIZE;
					}
					if (i == nh) break;
					v[0] = sl - LILTICK;
					m = bigStep/lilStep + 0.5;
					for (j=1; j<m; ++j)
					{
						u[1] = v[1] = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
						if (v[1] > hiC + 1) break;
						if (v[1] >= loC)
						{
							bgnline(); v2i(u); v2i(v); endline();
						}
					}
				}
				/* check if end points need labels */
				sprintf(theNumber, format, loV);
				wid = StrWidth(theNumber);
				if (loC - 3 + SCALEFONTSIZE < minC) /* label low end of scale */
				{
printf("minC = %g\n",minC); /***/
					DrawString(sl - BIGTICK - 3 - wid, loC - 3, theNumber);
				}
				if (hiC - 3 > maxC) /* label high end of scale */
				{
					sprintf(theNumber, format, hiV);
					wid = StrWidth(theNumber);
					DrawString(sl - BIGTICK - 3 - wid, hiC - 3, theNumber);
				}
				DrawCtlLeft(sl+2, right, cp-CTL_WID/2, cp+CTL_WID/2, 
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case REVSCALE:
				DrawSunkenRect(cl - TRK_WID/2, cl + TRK_WID/2, bottom, top,
					active ? UIPGREEN : UIBACKGROUND);
				loC = bottom + CTL_WID/2;
				hiC = top - CTL_WID/2;
				r = (hiC - loC)/(hiV - loV);
				anchorC = r*(anchorV - loV) + loC;
				cp = loC + r*(val-loV) + 0.5;
				/* draw scale on right of slider */
				sl = right;
				u[1] = loC; u[0] = sl;
				v[1] = hiC; v[0] = sl;
				SetUIColor(active ? UITEXT : UIGREYTEXT);
				bgnline(); v2i(u); v2i(v); endline();
				nl = (loV - anchorV)/bigStep - 1;
				nh = (hiV - anchorV)/bigStep + 1;
				for (i=nl; i<=nh; ++i)
				{
					sV = i*bigStep + anchorV;	/* "starting" value for each step */
					if (sV >= loV && sV <= hiV)
					{
						u[1] = v[1] = anchorC + r*(sV - anchorV) + 0.5;
						v[0] = sl + BIGTICK;
						bgnline(); v2i(u); v2i(v); endline();

						/* draw number next to tick */
						sprintf(theNumber, format, sV);
						wid = StrWidth(theNumber);
						DrawString(sl + BIGTICK + 3, v[1] - 3, theNumber);
					}
					if (i == nh) break;
					v[0] = sl + LILTICK;
					m = bigStep/lilStep + 0.5;
					for (j=1; j<m; ++j)
					{
						u[1] = v[1] = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
						if (v[1] > hiC) break;
						if (v[1] >= loC)
						{
							bgnline(); v2i(u); v2i(v); endline();
						}
					}
				}
				DrawCtlRight(left, sl-2, cp-CTL_WID/2, cp+CTL_WID/2, 
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case SCROLL:
				wid = right - left;
				loC = bottom + wid + EDGE + SB_GAP;
				hiC = top - wid - EDGE - SB_GAP;
				siz = (hiC - loC)*porSh; /* size of thumb */
				if (siz < wid) siz = wid;	/* min size is square */
				hiC -= siz; /* limit position of thumb */
				if (hiV == loV) sV = loC;
				else sV = loC + (val-loV)*(hiC - loC)/(hiV - loV) + 0.5;
				/* draw up-button */
				DrawRaisedRect(left, right, top - wid, top, 
					INCRHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				/* (draw up arrow on top) */
				FillUITri(cl, top - 0.28*wid,
					cl - wid/3, top - 0.61*wid,
					cl + wid/3, top - 0.61*wid,
					active ? UITEXT : UIGREYTEXT);

				/* draw down-button */
				DrawRaisedRect(left, right, bottom, bottom + wid, 
					DECRHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				/* (draw down arrow on top) */
				FillUITri(cl, bottom + 0.28*wid,
					cl - wid/3, bottom + 0.61*wid,
					cl + wid/3, bottom + 0.61*wid,
					active ? UITEXT : UIGREYTEXT);

				/* draw track */
				DrawSunkenRect(left, right, bottom + wid + SB_GAP, top - wid - SB_GAP,
					active ? UIPGREEN : UIBACKGROUND);
				/* draw thumb */
				DrawRaisedRect(left + EDGE, right - EDGE, sV, sV + siz,
					 THUMBHL & highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;

			case PLAIN:
			default:
				DrawSunkenRect(cl - TRK_WID/2, cl + TRK_WID/2, bottom, top,
					active ? UIPGREEN : UIBACKGROUND);
				loC = bottom + CTL_WID/2;
				hiC = top - CTL_WID/2;
				r = (hiC - loC)/(hiV - loV);
				cp = loC + r*(val-loV) + 0.5;
				DrawRaisedRect(left, right, cp-CTL_WID/2, cp+CTL_WID/2,
					highlight ? UIHIBACKGROUND : UIBACKGROUND);
				break;
		}
	}
	if (!active) FillUIGauzeRect(left, right, bottom, top, UIBACKGROUND);
#endif
	return NULLOBJ;
}

static ObjPtr TrackSlider(theSlider, mouseX, mouseY, flags)
ObjPtr theSlider;
int mouseX, mouseY;
long flags;
{
#ifdef INTERACTIVE
	int left, right, bottom, top, cp;
	ObjPtr theHiV, theLoV, theStepV, theLilStep;
	int style, mX, mY, offset;
	real hiV, loV, stepV, val, curVal, lilStep, valSave;
	ObjPtr theStyle, theValue;
	ObjPtr theReadout;
	struct tms tbuf;
	long prevTime = 0L;
	Bool hilit = false, tracking;

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

	/* return if mouse outside slider rectangle */
	if (mouseX < left - 1 || mouseX > right + 1 || mouseY < bottom -1
			|| mouseY > top + 1) return ObjFalse;
	if (TOOL(flags) == T_HELP)
	{
		ContextHelp(theSlider);
		return ObjTrue;
	}

	/* return if not active */
	if (!GetPredicate(theSlider, ACTIVATED)) return ObjTrue;

	MakeMeCurrent(theSlider); /* make slider current for keyboard input */

	/* get range and value of slider */
	theHiV = GetRealVar("TrackSlider", theSlider, HIVALUE);
	theLoV = GetRealVar("TrackSlider", theSlider, LOVALUE);
	theStepV = GetRealVar("TrackSlider", theSlider, STEPVALUE);
	theValue = GetRealVar("TrackSlider", theSlider, VALUE);
	theStyle = GetIntVar("TrackSlider", theSlider, STYLE);
	if (!theHiV || !theLoV || !theStepV || !theValue || !theStyle) return ObjTrue; /* Yow! */
	hiV = GetReal(theHiV);
	loV = GetReal(theLoV);
	stepV = GetReal(theStepV);
	curVal = valSave = val = GetReal(theValue);
	theReadout = GetVar(theSlider, READOUT);
	tracking = !GetPredicate(theSlider, TRACKNOT) && !(flags & F_OPTIONDOWN);
	style = GetInt(theStyle);
	if (style == SCALE || style == REVSCALE) 
	{
		theLilStep = GetRealVar("TrackSlider", theSlider, LILSTEP);
		if (theLilStep) lilStep = GetReal(theLilStep);
		else lilStep = 0;
	}
	if (right - left > top - bottom)
	{
		/* horizontal orientation */
	  if (style == SCROLL)
	  {
		int wid = top - bottom;

		/* which part is mouse in? */
		if (mouseX > left && mouseX < left + wid) /* decr button */
		{
			/* follow mouse */
			while (Mouse(&mX, &mY))
			{
				if (mX < left - SLOP || mX > left + wid + SLOP
					|| mY < bottom - SLOP || mY > top + SLOP)
				{
					/* outside slop rect */
					if (!hilit) continue;
					SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
					DrawMe(theSlider);
					hilit = false;
				}
				else if (times(&tbuf) - prevTime > DELAY*HEARTBEAT)
				{
					/* decrement val by stepV */
					prevTime = times(&tbuf);
					if ((val -= stepV) < loV) val = loV;
					if (!hilit || val != curVal)
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(DECRHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;
						hilit = true;
					}
				}
			}
		}
		else if (mouseX < right && mouseX > right - wid) /* incr button */
		{
			/* follow mouse */
			while (Mouse(&mX, &mY))
			{
				if (mX < right - wid - SLOP || mX > right + SLOP
					|| mY < bottom - SLOP || mY > top + SLOP)
				{
					/* outside slop rect */
					if (!hilit) continue;
					SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
					DrawMe(theSlider);
					hilit = false;
				}
				else if (times(&tbuf) - prevTime > DELAY*HEARTBEAT)
				{
					/* increment val by stepV */
					prevTime = times(&tbuf);
					if ((val += stepV) > hiV) val = hiV;
					if (!hilit || val != curVal)
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(INCRHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;
						hilit = true;
					}
				}
			}
		}
		else /* track or thumb */
		{
			ObjPtr thePorSh;
			real porSh, loC, hiC, siz, posn;

			thePorSh = GetRealVar("TrackSlider", theSlider, PORTIONSHOWN);
			if (thePorSh) porSh = GetReal(thePorSh);
			else return ObjTrue; /* Yow! Oh, well. */
			/* normalize portion shown */
			porSh = porSh/(porSh + hiV - loV);
			loC = left + wid + EDGE + SB_GAP;
			hiC = right - wid - EDGE - SB_GAP;
			siz = porSh*(hiC - loC);
			if (siz < wid) siz = wid; /* min size */
			hiC -= siz; /* max posn of thumb */
			if (hiV <= loV) posn = loC;
			else posn = (hiC - loC)*(val - loV)/(hiV - loV) + loC;
			if (mouseX < posn || mouseX > posn + siz) /* in track */
			{
				/* jump thumb to click position, then follow */
				offset = -siz/2;
				if (hiC <= loC)
				{
				    val = loV;
				}
				else
				{
				    val = loV + (hiV - loV)*(mouseY + offset - loC)/(hiC - loC);
				}
				if (val < loV) val = loV;
				else if (val > hiV) val = hiV;
				SetVar(theSlider, VALUE, NewReal(curVal = val));
			}
			else offset = posn - mouseX;
			SetVar(theSlider, HIGHLIGHTED, NewInt(THUMBHL));
			DrawMe(theSlider);
			/* now follow mouse with thumb */
			while (Mouse(&mX, &mY))
			{
				if (mX < left + wid - SLOP || mX > right - wid + SLOP
					|| mY < bottom - SLOP || mY > top + SLOP)
				{
					/* outside slop rect */
					if (hilit)
					{
						SetVar(theSlider, VALUE, theValue);
						SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val = valSave;
						hilit = false;
					}
					if (!Mouse(&mX, &mY)) break;
				}
				else
				{
					/* follow with thumb */
					if (hiC <= loC)
					{
					    val = loV;
					}
					else
					{
					    val = loV + (hiV - loV)*(mX + offset - loC)/(hiC - loC);
					}
					if (val < loV) val = loV;
					else if (val > hiV) val = hiV;
					if (!hilit || val != curVal)	/* value has changed */
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(THUMBHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;	/* remember current value */
						hilit = true;
					}
				}
			}
		}
	  }
	  else /* not a scrollbar */
	  {
		cp = ((val-loV)/(hiV - loV))*(right - left - CTL_WID)
				+ left + CTL_WID/2;
		if (mouseX < cp - CTL_WID/2 - 1 || mouseX > cp + CTL_WID/2 + 1)
		{
			/* click in track; jump control to click position */
			offset = 0;
			val = loV + (hiV - loV)*
			 (mouseX - left - CTL_WID/2)/(right - left - CTL_WID);
			if (val < loV) val = loV;
			else if (val > hiV) val = hiV;
			SetVar(theSlider, VALUE, NewReal(curVal = val));
		}
		else offset = cp - mouseX;

		/* now follow the mouse */
		while (Mouse(&mX, &mY))
		{
			if (mX < left - SLOP || mX > right + SLOP
				|| mY < bottom - SLOP || mY > top + SLOP)
			{
				/* outside slop rect */
				if (hilit)
				{
					SetVar(theSlider, VALUE, theValue);
					SetVar(theSlider, HIGHLIGHTED, NewInt(false));
					Update(theReadout, valSave);
					DrawMe(theSlider);
					if (tracking) ChangedValue(theSlider);
					curVal = val = valSave;
					hilit = false;
				}
				if (!Mouse(&mX, &mY)) break;
			}
			else
			{
				/* follow mouse with slider */
				val = loV + (hiV - loV)*
				  (mX + offset - left - CTL_WID/2)/(right - left - CTL_WID);
				if (val < loV) val = loV;
				else if (val > hiV) val = hiV;
				else if (stepV != 0.0)
				{
					int n;
					n = 0.5 + val/stepV;
					val = n*stepV;
				}
				else if (flags & F_CONSTRAIN && 
					(style == SCALE || style == REVSCALE) && lilStep != 0)
				{
					/* constrain value to little steps of scale */
					int n;
					n = 0.5 + val/lilStep;
					val = n*lilStep;
				}
				if (!hilit || val != curVal)
				{
					SetVar(theSlider, VALUE, NewReal(val));
					SetVar(theSlider, HIGHLIGHTED, NewInt(true));
					Update(theReadout, val);
					DrawMe(theSlider);
					if (tracking) ChangedValue(theSlider);
					curVal = val;
					hilit = true;
				}
			}
		}
	  }
	}
	else
	{
	  /* vertical orientation */
	  if (style == SCROLL)
	  {
		int wid = right - left;

		/* which part is mouse in? */
		if (mouseY > bottom && mouseY < bottom + wid) /* decr button */
		{
			/* track mouse */
			while (Mouse(&mX, &mY))
			{
				if (mX < left - SLOP || mX > right + SLOP
					|| mY < bottom - SLOP || mY > bottom + wid + SLOP)
				{
					/* outside slop rect */
					if (!hilit) continue;
					SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
					DrawMe(theSlider);
					hilit = false;
				}
				else if (times(&tbuf) - prevTime > DELAY*HEARTBEAT)
				{
					/* decrement val by stepV */
					prevTime = times(&tbuf);
					if ((val -= stepV) < loV) val = loV;
					if (!hilit || val != curVal)
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(DECRHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;
						hilit = true;
					}
				}
			}
		}
		else if (mouseY < top && mouseY > top-wid) /* incr button */
		{
			/* track mouse */
			while (Mouse(&mX, &mY))
			{
				if (mX < left - SLOP || mX > right + SLOP
					|| mY < top - wid - SLOP || mY > top + SLOP)
				{
					/* outside slop rect */
					if (!hilit) continue;
					SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
					DrawMe(theSlider);
					hilit = false;
				}
				else if (times(&tbuf) - prevTime > DELAY*HEARTBEAT)
				{
					/* increment val by stepV */
					prevTime = times(&tbuf);
					if ((val += stepV) > hiV) val = hiV;
					if (!hilit || val != curVal)
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(INCRHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;
						hilit = true;
					}
				}
			}
		}
		else /* in thumb or track */
		{
			ObjPtr thePorSh;
			real porSh, loC, hiC, siz, posn;

			thePorSh = GetRealVar("TrackSlider", theSlider, PORTIONSHOWN);
			if (thePorSh) porSh = GetReal(thePorSh);
			else return ObjTrue; /* Yow! Oh, well. */
			
			loC = bottom + wid + EDGE + SB_GAP;
			hiC = top - wid - EDGE - SB_GAP;
			/* normalize portion shown */
			porSh = porSh/(porSh + hiV - loV);
			siz = (hiC - loC)*porSh; /* size of thumb */
			if (siz < wid) siz = wid; /* minimum size */
			if (siz > hiC - loC) siz = hiC-loC; /* maximum size */
			hiC -= siz; /* upper limit of posn */
			if (hiV <= loV) posn = loC;
			else posn = (hiC - loC)*(val - loV)/(hiV - loV) + loC;
			if (mouseY < posn || mouseY > posn + siz) /* in track */
			{
				/* jump thumb to click position */
				offset = -siz/2;
				if (hiC <= loC)
				{
				    val = loV;
				}
				else
				{
				    val = loV + (hiV - loV)*(mouseY + offset - loC)/(hiC - loC);
				}
				if (val < loV) val = loV;
				else if (val > hiV) val = hiV;
				SetVar(theSlider, VALUE, NewReal(curVal = val));
			}
			else offset = posn - mouseY;
			SetVar(theSlider, HIGHLIGHTED, NewInt(THUMBHL));
			DrawMe(theSlider);
			/* now follow mouse with thumb */
			while (Mouse(&mX, &mY))
			{
				if (mX < left - SLOP || mX > right + SLOP
					|| mY < bottom + wid - SLOP || mY > top - wid + SLOP)
				{
					/* outside slop rect */
					if (hilit)
					{
						SetVar(theSlider, VALUE, theValue);
						SetVar(theSlider, HIGHLIGHTED, NewInt(0x00));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val = valSave;
						hilit = false;
					}
					if (!Mouse(&mX, &mY)) break;
				}
				else
				{
					/* track mouse with thumb */
					if (hiC <= loC)
					{
					    val = loV;
					}
					else
					{
					    val = loV + (hiV - loV)*(mY + offset - loC)/(hiC - loC);
					}
					if (val < loV) val = loV;
					else if (val > hiV) val = hiV;
					if (!hilit || val != curVal)
					{
						SetVar(theSlider, VALUE, NewReal(val));
						SetVar(theSlider, HIGHLIGHTED, NewInt(THUMBHL));
						DrawMe(theSlider);
						if (tracking) ChangedValue(theSlider);
						curVal = val;
						hilit = true;
					}
				}
			}
		}
	  }
	  else /* not a scrollbar */
	  {
		cp = ((val - loV)/(hiV - loV))*(top - bottom - CTL_WID)
				+ bottom + CTL_WID/2 + 0.5;
		if (mouseY < cp - CTL_WID/2 - 1 || mouseY > cp + CTL_WID/2 + 1)
		{
			/* click in track; jump control to click position */
			offset = 0;
			val = loV + (hiV - loV)*
			 (mouseY - bottom - CTL_WID/2)/(top - bottom - CTL_WID);
			if (val < loV) val = loV;
			else if (val > hiV) val = hiV;
			SetVar(theSlider, VALUE, NewReal(curVal = val));
		}
		else offset = cp - mouseY;
		/* now follow the mouse */
		while (Mouse(&mX, &mY))
		{
			if (mX < left - SLOP || mX > right + SLOP
				|| mY < bottom - SLOP || mY > top + SLOP)
			{
				/* outside slop rect */
				if (hilit)
				{
					SetVar(theSlider, VALUE, theValue);
					SetVar(theSlider, HIGHLIGHTED, NewInt(false));
					Update(theReadout, valSave);
					DrawMe(theSlider);
					if (tracking) ChangedValue(theSlider);
					curVal = val = valSave;
					hilit = false;
				}
				if (!Mouse(&mX, &mY)) break;
			}
			else
			{
				/* follow mouse with slider */
				val = loV + (hiV - loV)*
				  (mY + offset - bottom - CTL_WID/2)/(top - bottom - CTL_WID);
				if (val < loV) val = loV;
				else if (val > hiV) val = hiV;
				else if (stepV != 0.0)
				{
					int n;
					n = 0.5 + (val - loV)/stepV;
					val = loV + n*stepV;
				}
				else if (flags & F_CONSTRAIN && 
					(style == SCALE || style == REVSCALE) && lilStep != 0)
				{
					/* constrain value to little steps of scale */
					int n;
					n = 0.5 + val/lilStep;
					val = n*lilStep;
				}
				if (!hilit || val != curVal)
				{
					SetVar(theSlider, VALUE, NewReal(val));
					SetVar(theSlider, HIGHLIGHTED, NewInt(true));
					Update(theReadout, val);
					DrawMe(theSlider);
					if (tracking) ChangedValue(theSlider);
					curVal = val;
					hilit = true;
				}
			}
		}
	  }
	}
	SetVar(theSlider, HIGHLIGHTED, NewInt(false));
	DrawMe(theSlider);
	if (val != valSave)	/* value has changed */
	{
		Update(theReadout, val);
		ChangedValue(theSlider);
		if (logging) LogControl(theSlider);
	}
#endif
	return ObjTrue;
}
