// -------------------------------------------------------------------------------------
//	BooleanFormatter.h
//  This software is without warranty of any kind.  Use at your own risk.
// -------------------------------------------------------------------------------------

#import <objc/objc.h>
#import <appkit/appkit.h>
#import <mach/mach.h>
#import <dbkit/dbkit.h>
#import "BoolFormatter.h"

// -------------------------------------------------------------------------------------
// NXRect abbreviations
#define	X					origin.x
#define	Y					origin.y
#define	W					size.width
#define	H					size.height

// -------------------------------------------------------------------------------------
// drawCell image flags
#define	hideIMAGEMASK		1
#define	hideAltIMAGEMASK	2
#define	hideIMAGE			([drawCell tag] & hideIMAGEMASK)
#define	hideAltIMAGE		([drawCell tag] & hideAltIMAGEMASK)
#define setHideIMAGE(X)		[drawCell setTag:(hideAltIMAGE | ((X)? 0 : hideIMAGEMASK))];
#define setHideAltIMAGE(X)	[drawCell setTag:(hideIMAGE | ((X)? 0 : hideAltIMAGEMASK))];

// -------------------------------------------------------------------------------------
@implementation BoolFormatter

// -------------------------------------------------------------------------------------
// initialization

/* init */
- init
{

	/* init */
	[super init];
    selectionMode = NX_HIGHLIGHTMODE;
    newValue = [[DBValue allocFromZone:[self zone]] init];
	
	/* drawing cell */
	drawCell = [[ButtonCell allocFromZone:[self zone]] initIconCell:""];
	[drawCell setBordered:NO];
	[drawCell setIconPosition:NX_ICONONLY];
	[drawCell setType:NX_TOGGLE];
	[drawCell setIcon:"NXswitch"];
	[drawCell setAltIcon:"NXswitchH"];
	[drawCell setTag:0];
	
    return self;
}

/* free */
- free
{
    [newValue free];
	[drawCell free];
    return[super free];
}

// -------------------------------------------------------------------------------------
// set attributes

/* set mode */
- setMode:(int)newMode
{
	selectionMode = newMode;
	return self;
}

/* return mode */
- (int)mode
{
	return selectionMode;
}

// -------------------------------------------------------------------------------------
// images

/* set unselected image */
- setImage:anImage
{
	setHideIMAGE(anImage);
	return [drawCell setImage:anImage];
}

/* return unselected image */
- image
{
	return hideIMAGE? (id)nil : [drawCell image];
}

/* set unselected image */
- setIcon:(const char*)anIcon
{
	setHideIMAGE(anIcon && *anIcon);
	return [drawCell setIcon:anIcon];
}

/* return unselected image */
- (const char*)icon
{
	return hideIMAGE? (char*)nil : [drawCell icon];
}

/* set selected image */
- setAltImage:anImage
{
	setHideAltIMAGE(anImage);
	return [drawCell setAltImage:anImage];
}

/* return selected image */
- altImage
{
	return hideAltIMAGE? (id)nil : [drawCell altImage];
}

/* set unselected image */
- setAltIcon:(const char*)anIcon
{
	setHideAltIMAGE(anIcon && *anIcon);
	return [drawCell setAltIcon:anIcon];
}

/* return unselected image */
- (const char*)altIcon
{
	return hideAltIMAGE? (char*)nil : [drawCell altIcon];
}

// -------------------------------------------------------------------------------------
// drawing

/* draw DBTableView field cell */
- drawFieldAt:(u_int)row :(u_int)col inside:(NXRect*)frame inView:view
	withAttributes:(id <DBTableVectors>)ra :(id <DBTableVectors>)ca
	usePositions:(BOOL)ur :(BOOL)uc;
{
	BOOL	boolState;

	/* cache value and set state */
	[self getValueAt:row:col withAttributes:ra:ca usePositions:ur:uc];
	boolState = ([value isNull] || (*[value stringValue] == 'N'))? NO : YES;
	[drawCell setState:boolState];
	
	/* draw image (if there is an image to draw) */
	if (!((boolState && hideAltIMAGE) || (!boolState && hideIMAGE))) {
		[drawCell setAlignment:[(([ra formatter]==self)?ra:ca) contentAlignment]];
		[drawCell drawInside:frame inView:[NXApp focusView]];
	}

    return self;
}

// -------------------------------------------------------------------------------------
// event monitoring

/* grab mouse-down to check for toggle value */
- mouseDown:(NXEvent*)e at:(int)row:(int)col inside:(NXRect*)frame inView:(View*)view
    withAttributes:(id <DBTableVectors>)ra:(id <DBTableVectors>)ca
    usePositions:(BOOL)ur:(BOOL)uc
{
	id	lv, vw;

	/* ignore if not a double click */
    if (e->data.mouse.click < 2) return (id)nil;

	if (selectionMode == NX_RADIOMODE) {	// radio mode (not fully tested)
		int ndx;
		
		/* clear all other row values */
		[newValue setStringValue:"NO"];
		for (ndx = 0; ndx < [dataSource rowCount]; ndx++) {
			if (ndx == row) continue;
			[self getValueAt:ndx:col withAttributes:ra:ca usePositions:ur:uc];
			if ((*[value stringValue] != 'N') &&
				(![self setValueAt:ndx:col withAttributes:ra:ca usePositions:ur:uc]))
					return (id)nil;
		}

		/* get requested row value and re-set it to true */
		[self getValueAt:row:col withAttributes:ra:ca usePositions:ur:uc];
		if ([value isNull] || (*[value stringValue] == 'N')) {
			[newValue setStringValue:"YES"];
			[self setValueAt:row:col withAttributes:ra:ca usePositions:ur:uc];
		}
		
	} else {	// list mode
	
		/* toggle value */
		[self getValueAt:row:col withAttributes:ra:ca usePositions:ur:uc];
		if ([value isNull] || (*[value stringValue] == 'N')) [newValue setStringValue:"YES"];
		else [newValue setStringValue:"NO"];
		[self setValueAt:row:col withAttributes:ra:ca usePositions:ur:uc];
		
	}

	/* redraw field (just tell DBTableView, if possible) */
	for (lv = (id)nil, vw = view; vw && (lv != vw); lv = vw, vw = [vw superview]) {
		if ([vw isKindOf:[DBTableView class]]) {
			u_int r = ur? row : (u_int)[ra identifier];
			[vw rowsChangedFrom:r to:r];
			return self;
		}
	}
	
	/* slow redraw */
	[self drawFieldAt:row:col inside:frame inView:view
		withAttributes:ra:ca usePositions:ur:uc];
	[view display];
	[[view window] flushWindow];

	return self;
}

// -------------------------------------------------------------------------------------
// changing values

/* set field value */
- setValueAt:(int)row :(int)col
	withAttributes:(id <DBTableVectors>)ra :(id <DBTableVectors>)ca
    usePositions:(BOOL)ur :(BOOL)uc
{
	u_int	c = uc? col : (u_int)[ca identifier];
	u_int	r = ur? row : (u_int)[ra identifier];

	/* rows only */
	if (ur && !uc) {
		if ([delegate respondsTo:@selector(formatterWillChangeValueFor:at:to:sender:)] &&
			![delegate formatterWillChangeValueFor:(id)c at:r to:newValue sender:self])
			return (id)nil;
		[dataSource setValueFor:(id)c at:r from:newValue];
		if ([delegate respondsTo:@selector(formatterDidChangeValueFor:at:to:sender:)])
			[delegate formatterDidChangeValueFor:(id)c at:r to:newValue sender:self];
		return self;
	}
	
	/* columns only */
	if (!ur && uc) {
		if ([delegate respondsTo:@selector(formatterWillChangeValueFor:at:to:sender:)] &&
			![delegate formatterWillChangeValueFor:(id)r at:c to:newValue sender:self])
			return (id)nil;
		[dataSource setValueFor:(id)r at:c from:newValue];
		if ([delegate respondsTo:@selector(formatterDidChangeValueFor:at:to:sender:)])
			[delegate formatterDidChangeValueFor:(id)r at:c to:newValue sender:self];
		return self;
	}
	
	/* default */
	if (![delegate formatterWillChangeValueFor:(id)r:(id)c to:newValue sender:self])
		return (id)nil;
	[dataSource setValueFor:(id)r:(id)c from:newValue];
	if ([delegate respondsTo:@selector(formatterDidChangeValueFor::to:sender:)])
		[delegate formatterDidChangeValueFor:(id)r:(id)c to:newValue sender:self];

    return self;
}

// -------------------------------------------------------------------------------------
// archiving

/* archive object to stream */
- write:(NXTypedStream*) stream
{
    [super write:stream];
    return self;
}

/* read object from stream */
- read:(NXTypedStream*) stream
{
    [super read:stream];
    return self;
}

@end
