//
//	MiscSliderCell.m -- a TextFieldCell subclass that adds a SliderCell
//	   Written by David Fedchenko. Copyright 1994 by David Fedchenko.
//	       		 Version 1.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 "MiscSliderCell.h"
#import "MiscSliderField.h"

@implementation MiscSliderCell

- initTextCell:(const char *)sz
    {
    [super initTextCell:sz];

    layout = MSC_LEFT;
    split = 0.5;
    limitMinValue = 0;
    limitMaxValue = 100;
    limitMinBound = limitMinValue;
    limitMaxBound = limitMaxValue;
    fExpandMin = fExpandMax = NO;
    
    fValidRect = NO;
    
    idSlider = [[[[SliderCell allocFromZone:[self zone]] init]
                              setTarget:self]
                              setAction:@selector(moved:)];
    [[idSlider setContinuous:YES]
               sendActionOn:NX_MOUSEDOWNMASK | NX_MOUSEDRAGGEDMASK];
    [[idSlider setMinValue:limitMinValue] setMaxValue:limitMaxValue];
    
    [[[self setBezeled:YES] setEditable:YES] setContinuous:NO];
    
    return self;
    }

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

- textRect:(NXRect *)rect
    {
    float width = rect->size.width;
    
    switch (layout)
        {
	case MSC_LEFT:
    	    rect->size.width *= split;
	    rect->size.width = MIN(width - MSC_MINWIDTH, rect->size.width);
            rect->size.width -= (MSC_SPACING / 2);
	    break;
	
	case MSC_RIGHT:
	    rect->size.width *= split;
	    rect->size.width = MAX(MSC_MINWIDTH, rect->size.width);
	    rect->size.width = MIN(width - MSC_MINWIDTH, rect->size.width);
            rect->size.width -= (MSC_SPACING / 2);
	    rect->origin.x = width - rect->size.width;
	    break;
	    
	case MSC_BELOWLEFT:
	case MSC_BELOWCENTER:
	case MSC_BELOWRIGHT:
	    // the view is flipped
	    rect->origin.y += MSC_WIDTH + MSC_SPACING;
	    // fall through
	    
	case MSC_ABOVELEFT:
	case MSC_ABOVECENTER:
	case MSC_ABOVERIGHT:
	    // the view is flipped
	    rect->size.height -= MSC_WIDTH + MSC_SPACING;
	    rect->size.width *= split;
	    rect->size.width = MAX(MSC_MINWIDTH, rect->size.width);
	    rect->size.width = MIN(width, rect->size.width);
	    switch (layout)
	        {
		case MSC_ABOVECENTER:
		case MSC_BELOWCENTER:
		    rect->origin.x += (width - rect->size.width) / 2;
		    break;
		
		case MSC_ABOVERIGHT:
		case MSC_BELOWRIGHT:
		    rect->origin.x += width - rect->size.width;
		    break;
		}
	    break;
        }
    
    return self;
    }

- sliderRect:(NXRect *)rect
    {
    float width = rect->size.width;
    
    switch (layout)
        {
	case MSC_LEFT:
	    rect->size.width *= (1 - split);
	    rect->size.width = MAX(MSC_MINWIDTH, rect->size.width);
	    rect->size.width = MIN(width - MSC_MINWIDTH, rect->size.width);
            rect->size.width -= (MSC_SPACING / 2);
	    rect->origin.x = width - rect->size.width;
	    rect->origin.y += (rect->size.height - MSC_WIDTH) / 2;
	    rect->size.height = MSC_WIDTH;
	    break;
	
	case MSC_RIGHT:
	    rect->size.width *= (1 - split);
	    rect->size.width = MAX(MSC_MINWIDTH, rect->size.width);
	    rect->size.width = MIN(width - MSC_MINWIDTH, rect->size.width);
            rect->size.width -= (MSC_SPACING / 2);
	    rect->origin.y += (rect->size.height - MSC_WIDTH) / 2;
	    rect->size.height = MSC_WIDTH;
	    break;
	    
	case MSC_BELOWLEFT:
	case MSC_BELOWCENTER:
	case MSC_BELOWRIGHT:
	    // the view is flipped
	    rect->size.height = MSC_WIDTH;
	    break;
	    
	case MSC_ABOVELEFT:
	case MSC_ABOVECENTER:
	case MSC_ABOVERIGHT:
	    // the view is flipped
	    rect->origin.y += rect->size.height - MSC_WIDTH;
	    rect->size.height = MSC_WIDTH;
	    break;
        }
    
    return self;
    }

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

- drawSelf:(const NXRect *)cellFrame inView:controlView
    {
    NXRect rectSlider;
    
    rectFrame = *cellFrame;
    rectSlider = *cellFrame;
    [self textRect:&rectFrame];
    [self sliderRect:&rectSlider];
    
    fValidRect = YES;
    [super drawSelf:&rectFrame inView:controlView];
    fValidRect = NO;
    
    [idSlider drawSelf:&rectSlider inView:controlView];
    
    return self;
    }

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

- calcCellSize:(NXSize *)theSize inRect:(const NXRect *)aRect
    {
    [super calcCellSize:theSize inRect:aRect];
    
    switch (layout)
	{
	case MSC_LEFT:
	case MSC_RIGHT:
	    theSize->width = MSC_MINWIDTH * 2 + MSC_SPACING;
	    break;
	    
	case MSC_ABOVELEFT:
	case MSC_ABOVECENTER:
	case MSC_ABOVERIGHT:
	case MSC_BELOWLEFT:
	case MSC_BELOWCENTER:
	case MSC_BELOWRIGHT:
	    theSize->width = MSC_MINWIDTH;
	    theSize->height += MSC_WIDTH + MSC_SPACING;
	    break;
	}

    return self;
    }

- edit:(const NXRect *)aRect inView:controlView editor:textObj
    delegate:anObject event:(NXEvent *)theEvent
    {
    NXRect  rectT = *aRect;
    NXRect  rectS = *aRect;
    NXPoint p = theEvent->location;
    
    [self textRect:&rectT];
    [self sliderRect:&rectS];
    
    [controlView convertPoint:&p fromView:nil];
    
    if (NXPointInRect(&p, &rectT))
        {
        [super edit:&rectT inView:controlView editor:textObj
            delegate:anObject event:theEvent];
        }
    else if (NXPointInRect(&p, &rectS))
        {
	id sl = stringList;
	
	if (!sl)
            {
	    if ([[self controlView] class] == [MiscSliderField class])
	        {
		sl = [[self controlView] stringList];
		}
	    }
    
	if (sl)
	    {
	    [idSlider setMinValue:0];
	    [idSlider setMaxValue:[sl count]];
	    }
	
        [controlView lockFocus];
        [idSlider trackMouse:theEvent inRect:NULL ofView:controlView];
        [controlView unlockFocus];
        
        [NXApp sendAction:action to:target from:controlView];
        }
    
    return self;
    }

- getDrawRect:(NXRect *)theRect
    {
    [self textRect:theRect];
    [super getDrawRect:theRect];
    
    return self;
    }

- getTitleRect:(NXRect *)theRect
    {
    [self textRect:theRect];
    [super getTitleRect:theRect];
    
    return self;
    }

- highlight:(const NXRect *)cellFrame inView:aView lit:(BOOL)flag
    {
    NXRect rect = *cellFrame;
    
    [self textRect:&rect];
    [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;
    
    [self textRect:&rect];
    [super select:&rect inView:aView editor:aTextObject
        delegate:anObject start:selStart length:selLength];
    
    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] == [MiscSliderField 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
        {
        aDouble = MIN(aDouble, limitMaxValue);
        aDouble = MAX(aDouble, limitMinValue);
        
        if (!fFeedback)
            {
      	    if (fExpandMin && (aDouble < limitMinBound))
	        {
	        limitMinBound = aDouble;
	        [idSlider setMinValue:aDouble];
	        }
	    
	    if (fExpandMax && (aDouble > limitMaxBound))
	        {
	        limitMaxBound = aDouble;
	        [idSlider setMaxValue:aDouble];
	        }
	    }
        
        if (fInt)
            {
	    aDouble = rint(aDouble);
	    }
    
        actualValue = aDouble;
        [super setDoubleValue:aDouble];
        if (!fFeedback)
            {
	    [idSlider setDoubleValue:aDouble];
	    [[self controlView] updateCell:self];
            }
        }
    
    fLoop = NO;
    
    return self;
    }

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

- takeDoubleValueFrom:sender
    {
    [self setDoubleValue:[sender doubleValue]];
    
    return self;
    }

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

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

-(double) doubleValue
    {
    return actualValue;
    }


- moved:sender
    {
    fFeedback = YES;
    [self setDoubleValue:[idSlider doubleValue]];
    fFeedback = NO;
    
    if ([self isContinuous])
        {
        [NXApp sendAction:action to:target from:[self controlView]];
        }
    
    return self;
    }


- setMinValue:(double)value
    {
    limitMinValue = value;
    
    if (!fExpandMin && !stringList)
        {
	[idSlider setMinValue:value];
	[self setDoubleValue:MAX(actualValue, value)];
	}
    
    return self;
    }

- setMaxValue:(double)value
    {
    limitMaxValue = value;

    if (!fExpandMax && !stringList)
        {
	[idSlider setMaxValue:value];
	[self setDoubleValue:MIN(actualValue, value)];
	}
    
    return self;
    }

- setMinBoundary:(double)value
    {
    limitMinBound = value;

    if (fExpandMin && !stringList)
        {
	[idSlider setMinValue:value];
	[self setDoubleValue:MAX(actualValue, value)];
	}
    
    return self;
    }

- setMaxBoundary:(double)value
    {
    limitMaxBound = value;

    if (fExpandMin && !stringList)
        {
	[idSlider setMinValue:value];
	[self setDoubleValue:MIN(actualValue, value)];
	}
    
    return self;
    }

-(double) minValue
    {
    return limitMinValue;
    }

-(double) maxValue
    {
    return limitMaxValue;
    }

-(double) minBoundary
    {
    return limitMinBound;
    }

-(double) maxBoundary
    {
    return limitMaxBound;
    }

- setExpandMin:(BOOL)flag
    {
    fExpandMin = flag;
    
    if (flag)
        {
        if (!stringList)
            {
	    [idSlider setMinValue:limitMinBound];
	    [self setDoubleValue:MAX(actualValue, limitMinBound)];
            }
	}
    else
        {
        if (!stringList)
            {
            [idSlider setMinValue:limitMinValue];
	    [self setDoubleValue:actualValue];
            }
	}
    
    return self;
    }

- setExpandMax:(BOOL)flag
    {
    fExpandMax = flag;
    
    if (flag)
        {
        if (!stringList)
            {
	    [idSlider setMaxValue:limitMaxBound];
	    [self setDoubleValue:MIN(actualValue, limitMaxBound)];
            }
	}
    else
        {
        if (!stringList)
            {
            [idSlider setMaxValue:limitMaxValue];
	    [self setDoubleValue:actualValue];
            }
	}
    
    return self;
    }

-(BOOL) expandMin
    {
    return fExpandMin;
    }

-(BOOL) expandMax
    {
    return fExpandMax;
    }

- setPosition:(int)where
    {
    layout = where;
    
    return self;
    }

- setSplit:(int)percent
    {
    split = percent / 100.0;
    
    return self;
    }

- setIntegerOnly:(BOOL)flag
    {
    fInt = flag;
    
    return self;
    }

-(int) position
    {
    return layout;
    }

-(int) split
    {
    return (int)(split * 100);
    }

-(BOOL) integerOnly
    {
    return fInt;
    }


- setStringList:anObject
    {
    if (anObject && [anObject respondsTo:@selector(stringAt:)] &&
        [anObject respondsTo:@selector(count)])
        {
        stringList = anObject;
	[self setEditable:NO];
        [self setDoubleValue:actualValue];
        }
    else
        {
        [self setMinBoundary:limitMinBound];
        [self setMaxBoundary:limitMaxBound];
        [self setMinValue:limitMinValue];
        [self setMaxValue:limitMaxValue];
        }
    
    return self;
    }

- stringList
    {
    return stringList;
    }


- write:(NXTypedStream *)stream
    {
    [super write:stream];
    
    NXWriteTypes(stream, "ifd", &layout, &split, &actualValue);
    NXWriteTypes(stream, "dd", &limitMinValue, &limitMaxValue);
    NXWriteTypes(stream, "dd", &limitMinBound, &limitMaxBound);
    NXWriteType(stream, @encode(BOOL), &fExpandMin);
    NXWriteType(stream, @encode(BOOL), &fExpandMax);
    NXWriteType(stream, @encode(BOOL), &fInt);
    NXWriteObject(stream, idSlider);
    
    return self;
    }

- read:(NXTypedStream *)stream
    {
    [super read:stream];
    
    NXReadTypes(stream, "ifd", &layout, &split, &actualValue);
    NXReadTypes(stream, "dd", &limitMinValue, &limitMaxValue);
    NXReadTypes(stream, "dd", &limitMinBound, &limitMaxBound);
    NXReadType(stream, @encode(BOOL), &fExpandMin);
    NXReadType(stream, @encode(BOOL), &fExpandMax);
    NXReadType(stream, @encode(BOOL), &fInt);
    idSlider = NXReadObject(stream);
    
    return self;
    }

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

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

@end
