// HotShape.m
// By Charles G. Fleming, Educational Computing Services, Allegheny College.
// Copyright 1992 Allegheny College
// You may freely copy, distribute and reuse this code. 
// Allegheny College and the author disclaim any warranty of any kind, 
// expressed or implied, as to its fitness for any particular use.
// This work was partially supported by a grant from the Vira Heinz Endowment.

#import "HotShape.h"
#import "HotPath.h"
#import "SmallCell.h"

@implementation HotShape

- (const char *)getInspectorClassName
{
	return "HotShapeInspector";
}	

// Scale the HotShape object so that it has unit length and height.
// Give it a custom cell so that it can be resized to an arbitrarily small
// size in InterfaceBuilder.  The default shape will be a diamond shape.
- initFrame:(const NXRect *)frameRect
{
	SmallCell *smallCell;
	
	self = [super initFrame:frameRect];
	
	hotPathName = malloc(1);
	visible = YES;
	[self scale:bounds.size.width :bounds.size.height];

	smallCell = [[SmallCell allocFromZone:[self zone]] init];
	[self setCell:smallCell];

	// Define the hot shape paths that will be used.
	setHotPaths();
	
	tag = 0;
	[self setHotPathName:"diamond"];

	[self setOpaque:NO];	
	return self;
}

- (BOOL)acceptsFirstMouse
{
	return YES;
}
	
- drawSelf:(const NXRect *)rects :(int)rectCount
{	
	drawHotPath(hotPathName, visible);
	return self;
}	

- awake
{
	setHotPaths();
	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];
	[self scale:bounds.size.width :bounds.size.height];
	return self;
}

- setHotPathName:(const char *)name
{
	free(hotPathName);
	hotPathName = malloc(strlen(name) + 1);
	strcpy(hotPathName, name);	
	return self;
}	

- (char *)hotPathName
{
	return hotPathName;
}
		
- (BOOL)visible
{
	return visible;
}

- setVisible:(BOOL)seeIt
{
	visible = seeIt;
	return self;
}	
	
- read:(NXTypedStream *)typedStream
{
	[super read:typedStream];
	NXReadTypes(typedStream, "*s", &hotPathName, &visible);
	return self;
}		

- write:(NXTypedStream *)typedStream
{
	[super write:typedStream];
	
	NXWriteTypes(typedStream, "*s", &hotPathName, &visible);
	return self;
}

- mouseDown:(NXEvent *)theEvent
{
	SEL action;
	NXEvent *nextEvent;
	BOOL finished = NO, done = NO;	
	int at;
	NXPoint windowCoords = {theEvent->location.x, theEvent->location.y};
	View *currentview, *currentSuperview;
	List *subviewList;
	
	if([self inHotPath:windowCoords])
	{
		if([cell isEnabled])
			while(!finished)
			{
				nextEvent = [NXApp getNextEvent:NX_MOUSEUPMASK];
				if(nextEvent->type == NX_MOUSEUP)
				{
					windowCoords.x = nextEvent->location.x;
					windowCoords.y = nextEvent->location.y;				
					if([self inHotPath:windowCoords])
					{
						action = [cell action];
						if(action)
							[self sendAction:action to:[cell target]];
					}
					finished = YES;	
				}
			}
	}		
	else
	{
		// Pass on to the location to lower hotshape;
		currentview = self;
		currentSuperview = superview;
		while(currentSuperview && !done)
		{
			subviewList = [currentSuperview subviews];
			at = [subviewList indexOf:currentview] - 1;
			done = [self traverseTreeWithRoot:currentSuperview startAt:at
					usingLocation:windowCoords];
			currentview = currentSuperview;
			currentSuperview = [currentSuperview superview];
		}		
	}
	return self;	
}

// Traverse a view's subview hierarchy, starting at the specified node,
// until we get to the front-most hotshape.  If specified location is in
// the hot path of the front-most hotshape we return YES.  In all other
// cases we return NO.
- (BOOL)traverseTreeWithRoot:root startAt:(int)at 		
		usingLocation:(NXPoint)windowCoords
{
	SEL action;
	BOOL flag = NO;
	List *subviewList;
	int index;
	id node;
	SmallCell *smallCell;

	subviewList = [root subviews];
	for(index = at; index > -1 && !flag; index--)
	{
		node = [subviewList objectAt:index];
		if([node subviews])
			flag = [self traverseTreeWithRoot:node 
					startAt:[[node subviews] count]-1
					usingLocation:windowCoords];
		else
			if([node isKindOf:[HotShape class]])
			{
				flag = [node inHotPath:windowCoords];
				if(flag)
				{
					smallCell = [node cell];
					action = [smallCell action];
					if(action)
						[node sendAction:action to:[smallCell target]];
				}
			}				
	}

	return flag;
}

// Determines if a point (given in window coords) is in the hot path.
- (BOOL)inHotPath:(NXPoint)location
{
	NXPoint viewCoords = {location.x, location.y};
	int inRegion = NO;

	[self convertPoint:&viewCoords fromView:nil];
	[self lockFocus];	
	inHotRegion(hotPathName, viewCoords.x, viewCoords.y, &inRegion);
	[self unlockFocus];	
	return inRegion;
}							
@end
