
/* Generated by Interface Builder */

#import "Gauge.h"
#import <appkit/graphics.h>
#import <appkit/Window.h>

#define GAUGEQUANTTICS \
	if (GaugeDat.ticQuanta){ \
		GaugeDat.ticSize = \
			(((int)(GaugeDat.ticSize/GaugeDat.ticQuanta))+1) \
			*GaugeDat.ticQuanta; \
		GaugeDat.startValue = \
			(((int)(GaugeDat.startValue/GaugeDat.ticSize))-1) \
			*GaugeDat.ticSize; \
	}

@implementation Gauge

/**********************************************************************/
/* Factory Methods 
/**********************************************************************/

+ newFrame: (NXRect *) r
{
	self = [super newFrame:r];
	GaugeDat.backGray = NX_LTGRAY;
	GaugeDat.faceGray = NX_WHITE;
	GaugeDat.ticGray = NX_BLACK;
	GaugeDat.textGray = NX_BLACK;
	GaugeDat.ptGray = NX_DKGRAY;
	[self changeBMSize];
	GaugeDat.startValue = 0.0;
	GaugeDat.ticSize = 0.1;
	GaugeDat.value = 0.5;
	GaugeDat.numMajor = 11;
	GaugeDat.numMinor = 1;
	GaugeDat.ticQuanta = 0.0;	/* tic can be any size */
	GaugeDat.aThreshHi = 0.9;
	GaugeDat.aThreshLo =0.1; 
	GaugeDat.labelTicPat = "%1.1f";	/* one sig dig on each side */
	GaugeDat.ticFontName = "Helvetica";
	GaugeDat.titleFontName = "Helvetica";
	GaugeFlags.titleOn = NO;
	GaugeFlags.ticsOn = YES;
	GaugeFlags.labelTicsOn = NO;
	GaugeFlags.autoScaleOn = YES;
	GaugeFlags.jumpChange = YES;
	GaugeFlags.overFlow = NO;
	GaugeFlags.autoRenderBit = YES;	/*always re render on changes */
	GaugeFlags.borderType =	NX_NOBORDER;
	return self;
}
/**********************************************************************/
/* Private Methods 
/**********************************************************************/
- reScaleGauge:sender
/* return self if overrun or rescale
/*	else return nil;
/* This gets called if value is out of thresholds and auto scale is on.
/*
/* Autoscaling with no labels will cause the number of tics to change
/*	when blowing up or shrinking tic mark values otherwise ...
/*
/* This will rescale the tic marks or start values under thse rules
/* Underrun:
/*	if .aThreshLo > 0.0 
/*	then move low point so we are centered
/*	else if aThreshHi < 1.0 
/*	then rescale tic marks to blow up
/*	
/* Overrun
/*	if .aThreshHi < 1.0
/*	then move the tic marks to shrink
/*	else if .aThreshLo > 0.0
/*	then move up low so this is centered
/*
/* Bugs
/*	no way to tell it fixed increment scaling 
/*	no wayt to autoscale and cap 
/*
/*							 */
{
	double tStart;
	double tSize;
	
	GaugeFlags.overFlow = NO;
	if ((GaugeDat.value < GAUGEHI) && (GaugeDat.value > GAUGELO))
		return nil;
	/* if not autoscaling or both bounds locked then ... */
	if (!GaugeFlags.autoScaleOn ||
			((GaugeDat.aThreshLo <= 0.0) && (GaugeDat.aThreshHi >= 1.0))){
		if ((GaugeDat.value < GAUGELO) || (GaugeDat.value > GAUGEHI)){
			GaugeFlags.overFlow = YES;
			if (GaugeDat.value > GAUGEMAX) 	GaugeDat.value = GAUGEMAX;
			else if (GaugeDat.value < GaugeDat.startValue)
				GaugeDat.value = GaugeDat.startValue;
			[self sendAction:action to:target];
			return self;
		} else return nil;
	}
	/* we know we have at least one valid threshold if we got this far */
	/* and we have an overrun or underrun condition */
	if (GaugeDat.value > GAUGEHI){ /* overrun */
		if (!GaugeFlags.labelTicsOn){
			GaugeDat.numMajor += GaugeDat.numMajor -1 ;
		}else if (GaugeDat.aThreshHi < 1.0){ /* scale tics */
			GaugeDat.ticSize = GAUGESIZE*2 / (GaugeDat.numMajor-1);
		}else{	/* move start */
			GaugeDat.startValue += GAUGESIZE / 2;
		}
	}else{ 		/* underrun */
		if (!GaugeFlags.labelTicsOn ){
			if (GaugeDat.numMajor > 2)
				GaugeDat.numMajor=((GaugeDat.numMajor-1)/2)+1;
		}else if (GaugeDat.aThreshLo > 0.0){ /*move the start down */
			GaugeDat.startValue -= GAUGESIZE / 2;
		}else{ 	/* change tic size */
			GaugeDat.ticSize = GAUGESIZE/2 / (GaugeDat.numMajor-1);
		}
	}
	GAUGEQUANTTICS
 
	return self;
}

/**********************************************************************/
/* Public Methods 
/**********************************************************************/
- changeBMSize
{
	NXRect	r;
	
	[self getBounds:&r];
	if (GaugeDat.bm) [GaugeDat.bm free];
	// Create offscreen window for face. Note that the defer: argument
	// has to be NO for windows which will remain offscreen. Deferred
	// windows only get created on a orderFront: (or any other method
	// that causes them to come on screen).

	GaugeDat.bm = [Window newContent:&r 
			style:NX_PLAINSTYLE
			backing:NX_RETAINED 
			buttonMask:0 
			defer:NO];  
	GaugeDat.bmgState = (int)[GaugeDat.bm gState];  
	CONDITIONAL_REFRESH;
	return self;
}
- sizeTo:(NXCoord)tW :(NXCoord)tH
{
	fprintf(stderr,"in sizeTo\n");
	[self changeBMSize];
	return self;
}
- refreshBackground:sender;
/* redraw the bitmap - subclasses should override */
/* Subclasses should re image the bitmap and then do [self display] */
{
	[self display];
	return self;
}

/*____________________________________________________________________*/

- setBackGray:(float)bg Face:(float)fg Tic:(float)tg Text:(float)teg 
			Point:(float)pg
{
	GaugeFlags.autoRenderBit = NO;
	[self setBackgroundGray:bg];
	[self setFaceGray:fg];
	[self setTicGray:tg];
	[self setTextGray:teg];
	GaugeFlags.autoRenderBit = YES;
	CONDITIONAL_REFRESH;
	[self setPointerGray:pg];
	return self;
}
- setBackgroundGray:(float)tC
{
	if ((tC > 1.0) || (tC < 0.0)) return nil;
	GaugeDat.backGray = tC;
	CONDITIONAL_REFRESH;
	return self;
}
- setFaceGray:(float)tC
{
	if ((tC > 1.0) || (tC < 0.0)) return nil;
	GaugeDat.faceGray = tC;
	CONDITIONAL_REFRESH;
	return self;
}
- setTicGray:(float)tC
{
	if ((tC > 1.0) || (tC < 0.0)) return nil;
	GaugeDat.ticGray = tC;
	if (GaugeFlags.ticsOn) 	CONDITIONAL_REFRESH;
	return self;
}
- setTextGray:(float)tC
{
	if ((tC > 1.0) || (tC < 0.0)) return nil;
	GaugeDat.textGray = tC;
	if (GaugeFlags.labelTicsOn || GaugeFlags.titleOn) 
		CONDITIONAL_REFRESH;
	return self;
}
- setPointerGray:(float)tC
{
	if ((tC > 1.0) || (tC < 0.0)) return nil;
	GaugeDat.ptGray = tC;
	[self display];
	return self;
}
- (float)backgroundGray
{
	return GaugeDat.backGray;
}
- (float)faceGray
{
	return GaugeDat.faceGray;
}
- (float)ticGray
{
	return GaugeDat.ticGray;
}
- (float)textGray
{
	return GaugeDat.textGray;
}
- (float)pointerGray
{
	return GaugeDat.ptGray;
}

/*____________________________________________________________________*/


- setLowThresh:(double)lT HighThresh:(double)hT AndQuanta:(double)qT
{
	if ((lT < 1.0) && (lT >= 0.0)) GaugeDat.aThreshLo = lT;
	if ((hT > 0.0) && (hT <= 1.0)) GaugeDat.aThreshHi = hT;
	GaugeDat.ticQuanta = qT;
	return self;
}
- setTic:(double)sTic AndStart:(double)aStart;
/* this is so we can do auto scale with only one bitmap redraw */
{
	if (sTic <= 0.0) return nil;
	GaugeDat.ticSize = sTic;
	GaugeDat.startValue = aStart;
	GAUGEQUANTTICS
	if ((GaugeDat.value > GAUGEHI) || (GaugeDat.value < GAUGELO)){
			[self reScaleGauge:self];
	} else GaugeFlags.overFlow = NO;
	CONDITIONAL_REFRESH;
	return self;
}
- setTicSize:(double)sTic
{
	[self setTic:sTic AndStart:GaugeDat.startValue];
	return self;
}
- setStartValue:(double)aStart
{
	[self setTic:GaugeDat.ticSize AndStart:aStart];
	return self;
}	
- setDoubleValue:(double)aD
/* if autoscale and out of range then do it ... else load value and display */
{
	GaugeDat.value = aD;
	if ((aD <= GAUGELO) || (aD >= GAUGEHI)){
		if ([self reScaleGauge:self] != nil)
			CONDITIONAL_REFRESH;
		else [self display];
	}else{
		GaugeFlags.overFlow = NO;
		[self display];
	}
	return self;
}
- setFloatValue:(float)aF
{
	[self setDoubleValue:(double)aF];
	return self;
}
- takeDoubleValueFrom:sender;
{
	[self setDoubleValue:[sender doubleValue]];
	return self;
}
- takeFloatValueFrom:sender;
{
	[self setDoubleValue:(double)[sender floatValue]];
	return self;
}
- (double)maxValue
{
	return GAUGEMAX;
}
- (double)ticSize
{
	return GaugeDat.ticSize;
}
- (double)startValue
{
	return GaugeDat.startValue;
}
- (double)doubleValue
{
	return GaugeDat.value;
}
- (float)floatValue
{
	return (float)GaugeDat.value;
}
- (BOOL)overFlowDetected
{
	return GaugeFlags.overFlow;
}

/*____________________________________________________________________*/

- setMajorTics:(int)nTic
{
	if (nTic <= 1) return nil;
	GaugeDat.numMajor = nTic;
	CONDITIONAL_REFRESH;
	return self;
}
- setMinorTics:(int)nTic
{
	if (nTic < 0) return nil;
	GaugeDat.numMinor = nTic;
	CONDITIONAL_REFRESH;
	return self;
}
- setTitle:(char *)nTitle
{
	GaugeDat.title = nTitle;
	CONDITIONAL_REFRESH;
	return self;
}
- (int)majorTics
{
	return GaugeDat.numMajor;
}
- (int)minorTics
{
	return GaugeDat.numMinor;
}
- (char *)title
{
	return GaugeDat.title;
}

/*____________________________________________________________________*/

- setTitleEnable:(BOOL)aFlag
{
	if (aFlag == GaugeFlags.titleOn) return nil;
	GaugeFlags.titleOn = aFlag;
	CONDITIONAL_REFRESH;
	return self;
}
- setTicsEnable:(BOOL)aFlag
{
	if (aFlag == GaugeFlags.ticsOn) return nil;
	GaugeFlags.ticsOn = aFlag;
	CONDITIONAL_REFRESH;
	return self;
}
- setLabelTicsEnable:(BOOL)aFlag
{
	if (aFlag == GaugeFlags.labelTicsOn) return nil;
	GaugeFlags.labelTicsOn = aFlag;
	CONDITIONAL_REFRESH;
	return self;
}
- setAutoScaleEnable:(BOOL)aFlag
{
	GaugeFlags.autoScaleOn = aFlag;
	return self;
}
- setJumpChangeEnable:(BOOL)aFlag
{
	GaugeFlags.jumpChange = aFlag;
	return self;
}
- setBorderType:(int)aType
{
	if (	(aType == NX_NOBORDER) || (aType == NX_LINE) ||
		(aType == NX_BEZEL)    || (aType == NX_RIDGE)){
		GaugeFlags.borderType = aType;
		CONDITIONAL_REFRESH;
	}
	return self;
}
- (BOOL)titleEnabled;
{
	return GaugeFlags.titleOn;
}
- (BOOL)ticsEnabled
{
	return GaugeFlags.ticsOn;
}
- (BOOL)labelTicsEnabled
{
	return GaugeFlags.labelTicsOn;
}
- (BOOL)autoScaleEnabled
{
	return GaugeFlags.autoScaleOn;
}
- (BOOL)jumpChangeEnabled
{
	return GaugeFlags.jumpChange;
}
- (int)borderType
{
	return GaugeFlags.borderType;
}


/*____________________________________________________________________*/

- awake
{
    NXRect r;
    
    [self getBounds:&r];
    return [self changeBMSize];
}

- read:(NXTypedStream*)aStream
{
	fprintf(stderr,"Gauge.m read:\n");
	[super read:aStream];
	NXReadTypes(aStream,"{ii}",&GaugeFlags);
	NXReadTypes(aStream,"{fffff@idddiiddd****}",&GaugeDat);
	NXReadTypes(aStream,"@:",&target,&action);
	return self;
}

- write:(NXTypedStream*)aStream
{
	fprintf(stderr,"Gauge.m: write:\n");
	[super write:aStream];
	NXWriteTypes(aStream,"{ii}",&GaugeFlags);
	NXWriteTypes(aStream,"{fffff@idddiiddd****}",&GaugeDat);
	NXWriteTypes(aStream,"@:",&target,&action);
	return self;
}


/* I wish the control class would do this automaticlly */
- setTarget:aTarget				{ target = aTarget; return self; }
- setAction:(SEL)anAction		{ action = anAction; return self; }


@end
