
/* Generated by Interface Builder */
/* 
	Augmented by Slugg Jello, 
	Mouthing Flowers
	152 20th Ave. #1
	Seattle, WA. 98112
	slugg@mouthers.nwnexus.wa.com
*/

#import "Rubberband.h"
#import <appkit/Window.h>
#import <appkit/View.h>
#import <appkit/Application.h>
#import <appkit/appkit.h>
#include "Rubber.h" // psw stuff
#include <math.h>

#define DRAG_MASK (NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)
//#define DEBUG_OUTPUT
#define RUBBERBAND_WINDOW_WIDTH	1

@implementation Rubberband

/***** PRIVATE METHODS *****/

- (void)correctRect :(NXRect *)rect
{
	/* make sure it makes sense to NX routines which don't like negative 
		size values */
	if (rect->size.width < 0.0)
	{
		rect->origin.x += rect->size.width;
		rect->size.width = -rect->size.width;
	}
	if (rect->size.height < 0.0)
	{
		rect->origin.y += rect->size.height;
		rect->size.height = -rect->size.height;
	}
}

- setPosition
/* Position and draw rubberband.  Assumes currentRect is set and rubberband
	windows are initialized.  Also assumes currentRect is 'corrected'. */
{
	struct
	{
		NXRect top,left,bottom,right;
	} points;
	
	NXRect tRect,screenRect=currentRect;
	
	[[myView window] convertBaseToScreen :&screenRect.origin];
		
	/**** set size of each rubberband side ****/
	
	/* left */
	tRect = screenRect;
	tRect.size.width = RUBBERBAND_WINDOW_WIDTH;
	tRect.size.height = MAX(RUBBERBAND_WINDOW_WIDTH,screenRect.size.height);
	points.left = tRect;
	
	/* right */
	tRect.origin.x += screenRect.size.width - 1.0;
	points.right = tRect;
	
	/* bottom */
	tRect = screenRect;
	tRect.origin.x += 1.0;
	tRect.size.width = MAX(RUBBERBAND_WINDOW_WIDTH,screenRect.size.width-1.0);
	tRect.size.height = RUBBERBAND_WINDOW_WIDTH;
	points.bottom = tRect;
	
	/* top */
	tRect.origin.y += screenRect.size.height - 1.0;
	points.top = tRect;
	
	/* this'll erase old one and draw new one in new position */
	PSWPositionRect((float *)&points);

   return self;
}


/***** PUBLIC METHODS *****/

- initWithView :aView
{	
	[self init];
	[self setView :aView];
	return self;
}

- init
{
	[super init];
	myView = nil;
	
	[self setPivot :RB_CORNER];
	[self setInclusive :TRUE];
	
	return self;
}

- setView :aView
/* returns previous myView, sets bounds to those of aView */
{
	id oldView = myView;
	NXRect rect;
	
	if (aView == nil)
	{
		[self setBounds :NULL];
		return oldView;
	}
		
	myView = aView;

#if defined(DEBUG_OUTPUT)
	fprintf(stderr,"view == %p\n",myView);
#endif
	
	[myView getBounds :&rect];
	[self setBounds:&rect];
		
	return oldView;
}


- doStretch :(NXEvent *)event
/* stretch the rubber band until user lets go of mouse */
{
	int oldMask;
	NXEvent *nextEvent;
	NXPoint mouseLocation;
	NXPoint pivotPt;
	NXRect tRect;

	if (myView == nil)
		return self;
		
#if defined(DEBUG_OUTPUT)
	fprintf(stderr,"we's a trackin\n");
#endif
	
	currentRect.origin = event->location;
	currentRect.size.width = currentRect.size.height = 0.0;
	
	if (flags.useBounds)
	/* do nothing if first click isn't in bounds */
		if (![myView mouse :&currentRect.origin inRect:&boundsRect])
			return self;
			
		
	mouseLocation = event->location;
	
  /* we want to grab mouse dragged events */
   oldMask = [[myView window] addToEventMask:NX_MOUSEDRAGGEDMASK];
    
	if (flags.pivot == RB_CENTER)
		pivotPt = currentRect.origin;
		
	PSWInitRubberband();
	
	[self setPosition];
	
	PSWMakeVisible(TRUE);
		
  /* start our event loop, looking for mouse-dragged or mouse-up events */
	nextEvent = [NXApp getNextEvent:DRAG_MASK];
	
	tRect = currentRect;
	
	while (nextEvent->type != NX_MOUSEUP) 
	{
		if (flags.pivot == RB_CORNER)
		{
			tRect.size.width += nextEvent->location.x - mouseLocation.x;
			tRect.size.height += nextEvent->location.y - mouseLocation.y;
		}
		else
		{
			tRect.size.width += 
					(nextEvent->location.x - mouseLocation.x) * 2;
			tRect.size.height += 
					(nextEvent->location.y - mouseLocation.y) * 2;
			tRect.origin.x = pivotPt.x - tRect.size.width / 2;
			tRect.origin.y = pivotPt.y - tRect.size.height / 2;
		}
		
		currentRect = tRect;
		[self correctRect :&currentRect];
		
		if (flags.useBounds)
			NXIntersectionRect(&boundsRect,&currentRect);
		NXIntegralRect(&currentRect);

		mouseLocation = nextEvent->location;
		
		[self setPosition];

#if defined(DEBUG_OUTPUT)
		fprintf(stderr,
			"current rect: origin (%3.2lf,%3.2lf), size (%3.2lf,%3.2lf)\n",
			currentRect.origin.x,currentRect.origin.y,
			currentRect.size.width,currentRect.size.height);
#endif
		
		nextEvent = [NXApp getNextEvent:DRAG_MASK];
	}
 
  /* reset the event mask */
    [[myView window] setEventMask:oldMask];
	 
#if defined(DEBUG_OUTPUT)
	fprintf(stderr,"we's a done trackin\n");
#endif
    
	PSWMakeVisible(FALSE);
	
    return self;
}

- currentRect :(NXRect *)rect
/* return currentRect in rect in View coords */
{
	if (!rect)
		return self;
		
	*rect = currentRect;
		
	if (!flags.inclusive)
	{
		++rect->origin.x;
		++rect->origin.y;
		rect->size.width -= 2.0;
		rect->size.height -= 2.0;
	}
	
	/* convert from Window to View coords */
	[myView convertRect:rect fromView:nil];
	
   return self;
}

- bounds :(NXRect *)rect
/* return boundsRect in rect in View coords */
{
	if (!rect)
		return self;
	
	*rect = boundsRect;
		
	/* convert from Window to View coords */
	[myView convertRect:rect fromView:nil];
	
   return self;
}

- setBounds :(const NXRect *)rect 
/* set boundaries within which the rubberband may be dragged. 
	rect is in myView coords.  if rect is NULL, then no bounds. */
{
	if (rect == NULL)
	{
		flags.useBounds = FALSE;
		return self;
	}
		
	flags.useBounds = TRUE;
	boundsRect = *rect;
	
	/* convert from View to Window coords */
	[myView convertRect:&boundsRect toView:nil];
	
#if defined(DEBUG_OUTPUT)
	fprintf(stderr,"bounds: origin (%f %f) size (%f %f)\n",boundsRect.origin.x,
		boundsRect.origin.y,boundsRect.size.width,boundsRect.size.height);
#endif
	
	return self;
}

 
- setCurrentRectToBounds;
{
	flags.useBounds = TRUE;
	boundsRect = currentRect;
	return self;
}
 
- (int)setPivot :(int)pivot
{
	int oldPivot = flags.pivot;
	flags.pivot = pivot;
#if defined(DEBUG_OUTPUT)
	fprintf(stderr,"pivot == %s\n",
			flags.pivot == RB_CORNER ? "CORNER":"CENTER");
#endif
	
	return oldPivot;
}

- (BOOL)setInclusive:(BOOL)onoff
{
	BOOL bPrev = flags.inclusive;
	flags.inclusive = onoff;
   return bPrev;
}


@end
