/*	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"
#include "ScianTextBoxes.h"
#include "ScianSymbols.h"

#define SB_GAP	4
#define MAXNUMWID	37	/*** maximum allowed width of number on scale ***/


/* 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();


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);
    if (style == SCALE || style == REVSCALE)
    {
	    int left, right, bottom, top;

	    Get2DIntBounds(theSlider, &left, &right, &bottom, &top);
	    if (right - left < top - bottom)
	    /* vertical orientation */
	    {
		    if (style == SCALE) ImInvalidBounds(theSlider, 
		      left - BIGTICK - 3 - MAXNUMWID, right, bottom, top);
		    else ImInvalidBounds(theSlider, left, 
		      right + BIGTICK + 3 + MAXNUMWID, bottom, top);
	    }
	    else /*horizontal*/
	    {
		    if (style == SCALE) ImInvalidBounds(theSlider, left,
		      right, bottom, top + BIGTICK + 3 + SCALEFONTSIZE);
		    else ImInvalidBounds(theSlider, left, right,
		      bottom - BIGTICK - 3 - SCALEFONTSIZE, top);
	    }
    }
    else
    {
	    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)
    {
	SaveForUndo(theSlider);
	
	/* 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)
    {
	SaveForUndo(theSlider);
	
	/* 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));
    if (style == SCALE || style == REVSCALE)
    {
	    int left, right, bottom, top;

	    Get2DIntBounds(theSlider, &left, &right, &bottom, &top);
	    if (right - left < top - bottom)
	    /* vertical orientation */
	    {
		    if (style == SCALE) ImInvalidBounds(theSlider, 
		      left - BIGTICK - 3 - MAXNUMWID, right, bottom, top);
		    else ImInvalidBounds(theSlider, left, 
		      right + BIGTICK + 3 + MAXNUMWID, bottom, top);
	    }
	    else /*horizontal*/
	    {
		    if (style == SCALE) ImInvalidBounds(theSlider, left,
		      right, bottom, top + BIGTICK + 3 + SCALEFONTSIZE);
		    else ImInvalidBounds(theSlider, left, right,
		      bottom - BIGTICK - 3 - SCALEFONTSIZE, top);
	    }
    }
    else
    {
	    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;
    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:
			    if (IsDrawingRestricted(left, right, bottom,
			       top + BIGTICK + 3 + SCALEFONTSIZE)) return ObjFalse;
			    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;
			    SetUIColor(active ? UITEXT : UIGREYTEXT);
			    DrawLine(loC, sl, hiC, sl);
			    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)
				    {
					    int u = anchorC + r*(sV - anchorV) + 0.5;
					    DrawLine(u, sl, u, sl + BIGTICK);

					    /* draw number next to tick */
					    sprintf(theNumber, format, sV);
					    DrawAString(CENTERALIGN, u, sl + BIGTICK + 3, theNumber);

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

		    case REVSCALE:
			    if (IsDrawingRestricted(left, right,
			       bottom - BIGTICK + 3 + SCALEFONTSIZE, top)) return ObjFalse;
			    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;
			    SetUIColor(active ? UITEXT : UIGREYTEXT);
			    DrawLine(loC, sl, hiC, sl);
			    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)
				    {
					    int u = anchorC + r*(sV - anchorV) + 0.5;
					    DrawLine(u, sl, u, sl - BIGTICK);

					    /* draw number next to tick */
					    sprintf(theNumber, format, sV);
					    DrawAString(CENTERALIGN, u, sl - BIGTICK - 3, theNumber);
				    }
				    if (i == nh) break;
				    m = bigStep/lilStep + 0.5;
				    for (j=1; j<m; ++j)
				    {
					    int u = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
					    if (u > hiC) break;
					    if (u >= loC)
					    {
						    DrawLine(u, sl, u, sl - LILTICK);
					    }
				    }
			    }
			    DrawCtlDown(cp-CTL_WID/2, cp+CTL_WID/2, sl+2, top,
				    highlight ? UIHIBACKGROUND : UIBACKGROUND);
			    break;

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

			    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:
			    if (IsDrawingRestricted(left, right, bottom, 
				    top)) return ObjFalse;
			    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:
			    if (IsDrawingRestricted(left - BIGTICK - 3 - MAXNUMWID, right,
				    bottom, top)) return ObjFalse;
			    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;
			    SetUIColor(active ? UITEXT : UIGREYTEXT);
			    DrawLine(sl, loC, sl, hiC);
			    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)
				    {
					    int u = anchorC + r*(sV - anchorV) + 0.5;
					    DrawLine(sl, u, sl - BIGTICK, u);

					    /* draw number next to tick */
					    sprintf(theNumber, format, sV);
					    DrawAString(RIGHTALIGN, sl - BIGTICK - 3, u - 3, theNumber);

					    /* remember min and max coords of labels */
					    if (minC < -1e5) minC = u - 3;
					    maxC = u - 3 + SCALEFONTSIZE;
				    }
				    if (i == nh) break;
				    m = bigStep/lilStep + 0.5;
				    for (j=1; j<m; ++j)
				    {
					    int u = anchorC + r*(sV - anchorV + j*lilStep) + 0.5;
					    if (u > hiC + 1) break;
					    if (u >= loC)
					    {
						    DrawLine(sl, u, sl - LILTICK, u);
					    }
				    }
			    }
			    /* check if end points need labels */
			    sprintf(theNumber, format, loV);
			    wid = StrWidth(theNumber);
			    if (loC - 3 + SCALEFONTSIZE < minC) /* label low end of scale */
			    {
				    DrawAString(RIGHTALIGN, sl - BIGTICK - 3, loC - 3, theNumber);
			    }
			    if (hiC - 3 > maxC) /* label high end of scale */
			    {
				    sprintf(theNumber, format, hiV);
				    wid = StrWidth(theNumber);
				    DrawAString(RIGHTALIGN, sl - BIGTICK - 3, hiC - 3, theNumber);
			    }
			    DrawCtlLeft(sl+2, right, cp-CTL_WID/2, cp+CTL_WID/2, 
				    highlight ? UIHIBACKGROUND : UIBACKGROUND);
			    break;

		    case REVSCALE:
			    if (IsDrawingRestricted(left, right + BIGTICK + 3 + MAXNUMWID,
				    bottom, top)) return ObjFalse;
			    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;
			    SetUIColor(active ? UITEXT : UIGREYTEXT);
			    DrawLine(sl, loC, sl, hiC);
			    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)
				    {
					    int u = anchorC + r*(sV - anchorV) + 0.5;
					    DrawLine(sl, u, sl + BIGTICK, u);

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

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

			    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:
			    if (IsDrawingRestricted(left, right, bottom, 
				    top)) return ObjFalse;
			    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 */

    SaveForUndo(theSlider);

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