/* MiscTreeBrowserCell	-- Cell for MiscTreeBrowser
 *
 */

// IMPORTS

#import <dpsclient/psops.h>
#import <objc/hashtable.h>
#import <appkit/Application.h>
#import <appkit/Text.h>
#import <appkit/NXImage.h>
#import <appkit/Font.h>
#import <stdlib.h>
#import <stdio.h>

#import <misckit/MiscTreeBrowserCell.h>
#import <misckit/MiscTreeBrowserMatrix.h>
#import <misckit/MiscTreeBrowserProtocols.h>
#import <misckit/TB_images.h>

// CLASS VARIABLES

static NXImage *openImage = nil;
static NXImage *closedImage = nil;
static NXImage *leafImage = nil;

// IMPLEMENTATION

@implementation MiscTreeBrowserCell

// PRIVATE METHODS

+ initialize
{
	if ([MiscTreeBrowserCell class] == self)
	{
		openImage = _MISC_TBOpen_tiff_fn();
		closedImage = _MISC_TBClosed_tiff_fn();
		leafImage = _MISC_TBLeaf_tiff_fn();
	}

	return self;
}

- _dragImageSize:(NXSize *)aSize
/*"
Sets aSize to the size of the dragged image (or (0,0) if none) and returns
the dragged image
"*/
{
	NXImage *dragImage = [self isDraggable] ? [self dragImage] : nil;

	if (dragImage)
	{
		[dragImage getSize:aSize];
	}
	else
	{
		aSize->width = aSize->height = 0.0;
	}

	return dragImage;
}

/*"
Returns opener image; this can be open, leaf or closed.
"*/
- _openerImage
{
	return ([node childCount] == 0)
		? leafImage
		: [self isOpen] ? openImage : closedImage;
}


- edit:(const NXRect *)aRect inView:aView editor:textObject delegate:anObject
 event:(NXEvent *)theEvent
{
	return [super edit:aRect
				  inView:aView
				  editor:textObject
				  delegate:anObject
				  event:theEvent];
}


// PUBLIC METHODS

- init
{
	[super init];

	//  contents = "";
	[self setType:NX_TEXTCELL];
	[self setEditable:NO];

	return self;
}

- setNode:anObject
/*" Set %node to anObject and calculate the new %indentLevel value. Set
'draggable' flag if node supports dragging. If node is draggable, make it
the target for double-clicking on the drag image."*/
{
	node = anObject;
	hcFlags.draggable = [node conformsTo:@protocol(MiscTreeBrowserDragging)];
	[self setOpen:NO];

	indentLevel = 0;
	while ((anObject = [anObject parent]))
	{
		indentLevel++;
	}

	return self;
}

- node
{
  return node;
}

- (unsigned int)indentLevel
{
	return indentLevel;
}

- nodeAtIndentLevel:(unsigned int)aLevel
{
	int level = [self indentLevel];
	id aNode;

	if (aLevel > level)
	{
		aNode = nil;
	}
	else
	{
		aNode = [self node];
		while (aLevel < level)
		{
			aNode = [aNode parent];
			aLevel++;
		}
	}

	return aNode;
}

- (const char *)stringValue
{
	return [node stringValue];
}

- setStringValue:(const char *)aString
{
	[node setStringValue:aString];

	return self;
}

- (BOOL)isOpen
{
	return (BOOL)hcFlags.open;
}

- (BOOL)isLeaf
{
	return ([node childCount] > 0) ? NO : YES;
}

- (BOOL)isDraggable
{
	return (BOOL)hcFlags.draggable;
}

- setOpen:(BOOL)flag
{
	hcFlags.open = [self isLeaf] ? YES : flag;

	return self;
}

- (BOOL)isEditing
{
	return (BOOL)hcFlags.editing;
}

- setEditing:(BOOL)flag
{
	hcFlags.editing = flag;

	return self;
}

- sendDoubleAction
/*"
If the node responds to %{-doubleClick:} send it, with the cell as the
parameter
"*/
{
	if ([node respondsTo:@selector(doubleClick:)])
	{
		[node doubleClick:self];
	}

	return self;
}

- (NXImage *)dragImage
{
	return [self isDraggable] ? [node dragImage] : [self _openerImage];
}

- getOpenerRect:(NXRect *)cellFrame inView:controlView
{
    NXSize imageSize;

	[[self _openerImage] getSize:&imageSize];

	cellFrame->origin.x += [controlView nodeXOffsetForIndent:[self indentLevel]];
	cellFrame->origin.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) -
		(NX_HEIGHT(cellFrame) - imageSize.height) / 2.0;
	cellFrame->size = imageSize;

	return self;
}

- getTitleRect:(NXRect *)cellFrame inView:controlView
{
    NXSize imageSize, dragImageSize;
	NXCoord	ascender, descender, lineHeight;
	NXCoord xOffset, yOffset;

	NXTextFontInfo(support, &ascender, &descender, &lineHeight);
	[[self _openerImage] getSize:&imageSize];
	[self _dragImageSize:&dragImageSize];

	xOffset = [controlView nodeXOffsetForIndent:[self indentLevel]] +
		imageSize.width + 2.0;
	yOffset = (cellFrame->size.height - lineHeight +0.5) / 2.0;

	cellFrame->origin.x += xOffset;
	cellFrame->origin.y +=  yOffset;
	cellFrame->size.height = lineHeight;
	cellFrame->size.width -= (xOffset + dragImageSize.width + 2.0);

	return self;
}

- getDragImageRect:(NXRect *)cellFrame inView:controlView
/*"
Given a frame cellFrame in a view controlView, sets cellFrame to the
rectangle in the frame in which the drag image (if any) should be drawn.
Returns the drag image, or nil if there is no drag image (in which case
cellFrame is unchanged).
"*/
{
	NXImage *dragImage;
    NXSize dragImageSize;
	NXCoord xOffset;

	dragImage = [self _dragImageSize:&dragImageSize];

	if (dragImage)
	{
		xOffset = cellFrame->size.width - dragImageSize.height - 1.0;

		cellFrame->origin.x += xOffset;
		cellFrame->origin.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) -
			(NX_HEIGHT(cellFrame) - dragImageSize.height) / 2.0;
		cellFrame->size = dragImageSize;
	}

	return dragImage;
}

- drawInside:(const NXRect *)cellFrame inView:controlView
{
	NXRect rect;
	NXCoord	ascender, descender, lineHeight;
	NXImage *image;
	NXCoord	baseX, baseY;
	const char *stringValue = [self stringValue];

								// get text info; should use shared textcell!
	NXTextFontInfo(support, &ascender, &descender, &lineHeight);
	baseX = NX_X(cellFrame);
	baseY = NX_Y(cellFrame) + lineHeight - descender;

								// draw cell background
	if (cFlags1.highlighted)
	{
		NXRect rect = *cellFrame;
		NXCoord height = rect.size.height;
								// white background highlights cell
		PSsetgray(NX_WHITE);
		NXRectFill(cellFrame);

		if (!hcFlags.editing)
		{						// if not editing, grey borders above'n'below
			PSsetgray(NX_DKGRAY);
			rect.size.height = 1.0;
			NXRectFill(&rect);
			rect.origin.y += (height - 1.0);
			NXRectFill(&rect);
		}
	}
	else
	{
		NXColor backgroundColor = [controlView cellColor];

		PSsetrgbcolor(NXRedComponent(backgroundColor),
					  NXGreenComponent(backgroundColor),
					  NXBlueComponent(backgroundColor));
		NXRectFill(cellFrame);
	}

	PSsetgray(NX_BLACK);

	/* draw open/leaf/closed icon at correct indentation */

	image = [self _openerImage];
	rect = *cellFrame;
	[self getOpenerRect:&rect inView:controlView];
	[image composite:NX_SOVER toPoint:&rect.origin];

	/* write node stringValue as cell title */
	if (stringValue)
	{
		id textCell = [controlView sharedTextCell];

		rect = *cellFrame;
		[self getTitleRect:&rect inView:controlView];
		rect.origin.x += 1.0;	// fudge because we are not drawing a bezel
		[textCell setStringValue:[self stringValue]];
		[textCell setEnabled:[self isEnabled]];
		[textCell drawInside:&rect inView:controlView];
	}

	/* draw draggable image (if any) */

	if ([self isDraggable])
	{
		NXImage *dragImage;

		rect = *cellFrame;
		dragImage = [self getDragImageRect:&rect inView:controlView];
		if (dragImage)
		{
			[dragImage composite:NX_SOVER toPoint:&rect.origin];
		}
	}

	return self;
}

/*(
Draws border then insides of cell.
)*/
- drawSelf:(const NXRect *)cellFrame inView:aView
{
	return [self drawInside:cellFrame inView:aView];
}

/*(
As drawInside:inView: above, but highlighted
)*/
- highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
{
    if (cFlags1.highlighted != flag)
	{
		cFlags1.highlighted = flag;
		[self drawSelf:cellFrame inView:controlView];
    }
    return self;
}

@end
