//
//	MiscValueCell.m -- a TextFieldCell subclass that adds arrow buttons
//	   Written by David Fedchenko. Copyright 1994 by David Fedchenko.
//	       		 Version 2.0  All rights reserved.
//
//		This notice may not be removed from this source code.
//
//	This object is included in the MiscKit by permission from the author
//	and its use is governed by the MiscKit license, found in the file
//	"LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
//	for a list of all applicable permissions and restrictions.
//	

#import "MiscValueCell.h"
#import "MiscValueField.h"

@implementation MiscValueCell

- initTextCell:(const char *)sz
    {
    [super initTextCell:sz];
    
    actualValue = 0;
    limitMinValue = 0;
    limitMaxValue = 100;
    limitMinBound = 0;
    limitMaxBound = 100;
    fExpandLow = NO;
    fExpandHigh = NO;
    sizeStep = 1;
    sizeAltStep = 10;
    fLoop = fValidRect = NO;
    
    // Set up the NXImages so they can draw without the files.
    // This only works it's magic for cells set up in IB since
    // a regular initialization will look for the images.
    if (![NXImage findImageNamed:"MVCUpImage"])
        {
        char buf[MAXPATHLEN + 1];
        NXImage * idImage;
        
        [[NXBundle bundleForClass:[self class]] getPath:buf 
            forResource:"MVCUp" ofType:"tiff"];
        idImage = [[[NXImage alloc] init] setDataRetained:YES];
        [idImage loadFromFile:buf];
        [idImage setName:"MVCUpImage"];
        [[NXBundle bundleForClass:[self class]] getPath:buf 
            forResource:"MVCDown" ofType:"tiff"];
        idImage = [[[NXImage alloc] init] setDataRetained:YES];
        [idImage loadFromFile:buf];
        [idImage setName:"MVCDownImage"];
        }
    
    idUp = [[[[ButtonCell allocFromZone:[self zone]]
                          initIconCell:"MVCUpImage"]
                          setTarget:self]
                          setAction:@selector(increment:)];
    [[idUp setContinuous:YES]
           sendActionOn:NX_MOUSEDOWNMASK | NX_PERIODICMASK];
    
    idDown = [[[[ButtonCell allocFromZone:[self zone]]
                            initIconCell:"MVCDownImage"]
                            setTarget:self]
                            setAction:@selector(decrement:)];
    [[idDown setContinuous:YES]
             sendActionOn:NX_MOUSEDOWNMASK | NX_PERIODICMASK];
    
    [[[self setBezeled:YES] setEditable:YES] setContinuous:NO];
    
    return self;
    }

- copyFromZone:(NXZone *)zone
    {
    MiscValueCell * idNew = [super copyFromZone:zone];
    
    if (idNew)
        {
        if ((idNew->idUp = [idUp copyFromZone:zone]))
            {
            [idNew->idUp setTarget:idNew];
            }
        
        if ((idNew->idDown = [idDown copyFromZone:zone]))
            {
            [idNew->idDown setTarget:idNew];
            }
        }
    
    return idNew;
    }

- drawInside:(const NXRect *)aRect inView:controlView
    {
    if (!fValidRect)
        {
        rectFrame = *aRect;
        rectFrame.size.width -= 17;
        }
    
    [super drawInside:&rectFrame inView:controlView];
    
    return self;
    }

- drawSelf:(const NXRect *)cellFrame inView:controlView
    {
    rectFrame = *cellFrame;
    rectFrame.size.width -= 17;
    
    fValidRect = YES;
    [super drawSelf:&rectFrame inView:controlView];
    fValidRect = NO;
    
    rectFrame.origin.x += rectFrame.size.width + 1;
    rectFrame.size.width = 16;
    rectFrame.size.height /= 2;
    [idUp drawSelf:&rectFrame inView:controlView];
    
    rectFrame.origin.y += rectFrame.size.height;
    [idDown drawSelf:&rectFrame inView:controlView];
    
    return self;
    }

- resetCursorRect:(const NXRect *)cellFrame inView:aView
    {
    NXRect rect = *cellFrame;
    
    rect.size.width -= 17;
    [super resetCursorRect:&rect inView:aView];
    
    return self;
    }

- calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
    {
    NXRect rect = *aRect;
    
    rect.size.width -= 17;
    [super calcCellSize:theSize inRect:&rect];
    
    theSize->height = MAX(21, theSize->height);
    theSize->width += 17;
    
    return self;
    }

- edit:(const NXRect *)aRect inView:controlView editor:textObj
    delegate:anObject event:(NXEvent *)theEvent
    {
    NXRect  rect = *aRect;
    NXPoint p = theEvent->location;
    id      idWhich;
    BOOL    fR;
    BOOL    fSpin;
    int     oldMask;
    float   delay;
    float   interv;
    
    rect.size.width -= 17;
    
    [controlView convertPoint:&p fromView:nil];
    
    if (NXPointInRect(&p, &rect))
        {
        [super edit:&rect inView:controlView editor:textObj
            delegate:anObject event:theEvent];
        }
    else
        {
        rect = *aRect;
        rect.origin.x += rect.size.width - 16;
        rect.size.width = 16;
        rect.size.height /= 2;
        fAltKeyDown = theEvent->flags & NX_ALTERNATEMASK;
        
        if (p.y < aRect->origin.y + (aRect->size.height / 2))
            {
            idWhich = idUp;
            }
        else
            {
            rect.origin.y += rect.size.height;
            idWhich = idDown;
            }
        
        oldMask = [[controlView window] addToEventMask:NX_MOUSEDRAGGEDMASK];
        [controlView lockFocus];
        [idWhich getPeriodicDelay:&delay andInterval:&interv];
        fSpin = YES;
        do
            {
            [idWhich highlight:&rect inView:controlView lit:YES];
            fR = [idWhich trackMouse:theEvent inRect:&rect ofView:controlView];
            [idWhich highlight:&rect inView:controlView lit:NO];
            [controlView display:&rect :1];
            [idWhich setPeriodicDelay:0.0 andInterval:interv];
            if (fR)
                {
                fSpin = NO;
                }
            else
                {
                theEvent = [NXApp
                            getNextEvent:NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK];
                if (theEvent->type == NX_MOUSEUP)
                    {
                    fSpin = NO;
                    }
                }
            } while (fSpin);
        [idWhich setPeriodicDelay:delay andInterval:interv];
        [controlView unlockFocus];
        [[controlView window] setEventMask:oldMask];
        
        [NXApp sendAction:action to:target from:controlView];
        }
    
    return self;
    }

- getDrawRect:(NXRect *)theRect
    {
    theRect->size.width -= 17;
    [super getDrawRect:theRect];
    
    return self;
    }

- getTitleRect:(NXRect *)theRect
    {
    theRect->size.width -= 17;
    [super getTitleRect:theRect];
    
    return self;
    }

- highlight:(const NXRect *)cellFrame inView:aView lit:(BOOL)flag
    {
    NXRect rect = *cellFrame;
    
    rect.size.width -= 17;
    [super highlight:&rect inView:aView lit:flag];
    
    return self;
    }

- select:(const NXRect *)aRect inView:aView editor:aTextObject
    delegate:anObject start:(int)selStart length:(int)selLength
    {
    NXRect rect = *aRect;
    
    rect.size.width -= 17;
    [super select:&rect inView:aView editor:aTextObject
        delegate:anObject start:selStart length:selLength];
    
    return self;
    }

- setBezeled:(BOOL)flag
    {
    [super setBezeled:flag];
    
    [idUp setBordered:flag];
    [idDown setBordered:flag];
    
    return self;
    }

- setBordered:(BOOL)flag
    {
    [super setBordered:flag];
    
    [idUp setBordered:NO];
    [idDown setBordered:NO];
    
    return self;
    }

- setEditable:(BOOL)flag
    {
    [super setEditable:flag];
    
    if (!flag)
        {
        [self setSelectable:YES];
        }

    return self;
    }


- setIntValue:(int)anInt
    {
    [self setDoubleValue:(double)anInt];
    
    return self;
    }

- setFloatValue:(float)aFloat
    {
    [self setDoubleValue:(double)aFloat];
    
    return self;
    }

- setDoubleValue:(double)aDouble
    {
    const char *    sz;
    id      sl = stringList;
    
    fLoop = YES; // so setStringValue doesn't get loopy
    
    if (!sl)
        {
	if ([[self controlView] class] == [MiscValueField class])
	    {
	    sl = [[self controlView] stringList];
	    }
        }
    
    if (sl)
	{
	int indx = (int)aDouble;
	int cnt = (int)limitMaxValue;
        
	cnt = [sl count] - 1;
	
	indx = MAX(indx, 0);
	indx = MIN(indx, cnt);
	actualValue = indx;
        
	sz = (char *)[sl stringAt:indx];
	if (sz)
	    {
	    [super setStringValue:sz];
	    }
	else
	    {
	    [super setDoubleValue:actualValue];
	    }
	}
    else
        {
	actualValue = MIN(aDouble, limitMaxValue);
	actualValue = MAX(actualValue, limitMinValue);
    
	if (fExpandLow && (actualValue < limitMinBound))
            {
	    limitMinBound = actualValue;
	    }
    
	if (fExpandHigh && (actualValue > limitMaxBound))
            {
	    limitMaxBound = actualValue;
	    }
        
        [super setDoubleValue:actualValue];
        }
    
    fLoop = NO;
    
    return self;
    }

- setStringValue:(const char *)aString
    {
    double  value;
    
    [super setStringValue:aString];
    
    if (!fLoop)
        {
        sscanf(aString, "%lf", &value);
        [self setDoubleValue:value];
        }
    
    return self;
    }

-(int) intValue
    {
    return (int)actualValue;
    }

-(float) floatValue
    {
    return (float)actualValue;
    }

-(double) doubleValue
    {
    return actualValue;
    }


- increment:sender
    {
    double  val;
    id      sl = stringList;
    
    if (!sl)
        {
	if ([[self controlView] class] == [MiscValueField class])
	    {
	    sl = [[self controlView] stringList];
	    }
        }
    
    if (sl)
        {
	[self setIntValue:[self intValue] + ((fAltKeyDown) ? sizeAltStep : 1)];
	}
    else
        {
	val = [self doubleValue] + ((fAltKeyDown) ? sizeAltStep : sizeStep);
    
	if (fExpandHigh && (val > limitMaxBound))
            {
	    val = limitMaxBound;
	    }
	[self setDoubleValue:val];
	}
    
    if ([self isContinuous])
        {
        [NXApp sendAction:action to:target from:[self controlView]];
        }
    
    return self;
    }

- decrement:sender
    {
    double  val;
    id      sl = stringList;
    
    if (!sl)
        {
	if ([[self controlView] class] == [MiscValueField class])
	    {
	    sl = [[self controlView] stringList];
	    }
        }
    
    if (sl)
        {
	[self setIntValue:[self intValue] - ((fAltKeyDown) ? sizeAltStep : 1)];
	}
    else
        {
	val = [self doubleValue] - ((fAltKeyDown) ? sizeAltStep : sizeStep);
    
	if (fExpandLow && (val < limitMinBound))
            {
	    val = limitMinBound;
	    }
	[self setDoubleValue:val];
	}
    
    if ([self isContinuous])
        {
        [NXApp sendAction:action to:target from:[self controlView]];
        }
    
    return self;
    }


- setMinValue:(double)value
    {
    limitMinValue = value;
    
    return self;
    }

- setMaxValue:(double)value
    {
    limitMaxValue = value;
    
    return self;
    }

- setMinBoundary:(double)value
    {
    limitMinBound = value;
    
    return self;
    }

- setMaxBoundary:(double)value
    {
    limitMaxBound = value;
    
    return self;
    }

-(double) minValue
    {
    return limitMinValue;
    }

-(double) maxValue
    {
    return limitMaxValue;
    }

-(double) minBoundary
    {
    return limitMinBound;
    }

-(double) maxBoundary
    {
    return limitMaxBound;
    }

- setStepSize:(double)size
    {
    sizeStep = size;
    
    return self;
    }

- setAltStepSize:(double)size
    {
    sizeAltStep = size;
    
    return self;
    }

-(double) stepSize
    {
    return sizeStep;
    }

-(double) altStepSize
    {
    return sizeAltStep;
    }

- setExpandMin:(BOOL)flag
    {
    fExpandLow = flag;
    
    [self setDoubleValue:actualValue];
    
    return self;
    }

- setExpandMax:(BOOL)flag
    {
    fExpandHigh = flag;
    
    [self setDoubleValue:actualValue];
    
    return self;
    }

-(BOOL) expandMin
    {
    return fExpandLow;
    }

-(BOOL) expandMax
    {
    return fExpandHigh;
    }


- setStringList:anObject
    {
    if (anObject && [anObject respondsTo:@selector(stringAt:)] &&
	[anObject respondsTo:@selector(count)])
        {
	stringList = anObject;
	[self setEditable:NO];
	[self setIntValue:(int)actualValue];
	}
    
    return self;
    }

- stringList
    {
    return stringList;
    }


- write:(NXTypedStream *)stream
    {
    [super write:stream];
    
    NXWriteTypes(stream, "ddd", &actualValue, &sizeStep, &sizeAltStep);
    NXWriteTypes(stream, "dd", &limitMinValue, &limitMaxValue);
    NXWriteTypes(stream, "dd", &limitMinBound, &limitMaxBound);
    NXWriteType(stream, @encode(BOOL), &fExpandLow);
    NXWriteType(stream, @encode(BOOL), &fExpandHigh);
    NXWriteObject(stream, idUp);
    NXWriteObject(stream, idDown);
    
    return self;
    }

- read:(NXTypedStream *)stream
    {
    [super read:stream];
    
    NXReadTypes(stream, "ddd", &actualValue, &sizeStep, &sizeAltStep);
    NXReadTypes(stream, "dd", &limitMinValue, &limitMaxValue);
    NXReadTypes(stream, "dd", &limitMinBound, &limitMaxBound);
    NXReadType(stream, @encode(BOOL), &fExpandLow);
    NXReadType(stream, @encode(BOOL), &fExpandHigh);
    idUp = NXReadObject(stream);
    idDown = NXReadObject(stream);
    
    return self;
    }

- awake
    {
    [self setDoubleValue:actualValue];
    
    return self;
    }

- awakeFromNib
    {
    [self awake];
    
    return self;
    }

@end
