/* 
 * SideSplitView.m
 *
 * Purpose:
 *		This object implements a split view which divides a view into two
 *		side by side areas. These areas are intended to be filled by subviews.
 *		The user can then change the porportion of the subviews to one
 *		another. The purpose is to provide developers with an alternative to
 *		NXSplitView.
 *
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by: Mary McNabb
 * Created: Apr 91
 *
 */

#import "SideSplitView.h"
#import <dpsclient/psops.h>
#import <dpsclient/wraps.h>
#import <math.h>
#import <libc.h>

#define eventMask NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK

#define DIVIDERWIDTH 8.0		/* the divider is 8 pixels wide. */
#define HALFWIDTH 4.0

static NXRect   dimpleRect = {{3.0, 5.0}, {8.0, 7.0}};

/* Retrace is 1/67 of sec * 10^6 micro secs per second */
#define RETRACE	(1000000.0/67.0)  /* vertical retrace period in micro secs */

@implementation SideSplitView

/*
 * initialize the split view. also initialize the size of the divider
 */
- initFrame:(const NXRect *) theRect
{
	NXSize	dimpleSize;
	
	srandom(time(0));		/* initialize random number generator */
	[super initFrame:theRect];
	[self setAutoresizeSubviews:YES];
	dimple = [NXImage findImageNamed:"Dimple"];
	[dimple getSize:&dimpleSize];
	
	dimpleHalfHeight = floor(dimpleSize.height / 2.0);

	dividerRect.origin.x = 0.0;
	dividerRect.origin.y = bounds.origin.y;
	dividerRect.size.height = bounds.size.height;
	dividerRect.size.width = dimpleSize.width + 3.0;
	
    minX = 0.0;

	return self;
}

/*
 * initialize the subviews of the split view.
 */
- initViews	
{
	[window disableFlushWindow];
	[self adjustSubviews];			/* just in case we were sloppy in IB */
	[window reenableFlushWindow];
	[window flushWindow];
	[self display];
	return self;
}

/*
 * take advantage of IB. Use these set methods to initialize the subviews
 */
- setLeftView:(id)newView
{
	NXRect leftViewFrame;
    
	leftView = newView;
	[leftView setAutosizing:NX_WIDTHSIZABLE|
		NX_HEIGHTSIZABLE|NX_MAXXMARGINSIZABLE];
			
	[leftView getFrame:&leftViewFrame];
	dividerRect.origin.x = leftViewFrame.size.width;
	
	[self addSubview:leftView];
	return self;
}

/*
 * don't bother with the size and origin of the view as it gets completely reset in
 * adjustSubviews
 */
- setRightView:(id)newView
{
	rightView = newView;
	
	[rightView setAutosizing:NX_WIDTHSIZABLE|
		NX_HEIGHTSIZABLE|NX_MINXMARGINSIZABLE];
	[self addSubview:rightView];
	return self;
}

- (BOOL)acceptsFirstMouse
{
	return YES;
}

/*
 * The side split view consists of a light gray background, and a dimple.
 * Relies on the subviews to draw bevels, etc.
 */
- drawSelf:(const NXRect *)r :(int)c
{
	NXPoint dimplePoint;

	PSsetgray(NX_LTGRAY);
	NXRectFill(&bounds);

	dimplePoint.x = dividerRect.origin.x + 1.0;
	dimplePoint.y = floor(dividerRect.origin.y +
    			  dividerRect.size.height / 2.0 - dimpleHalfHeight);

	[dimple composite:NX_SOVER toPoint:&dimplePoint];

    return self;
}

/*
 * Controls the movement of the divider as the user slides it with the mouse
 */
- mouseDown:(NXEvent *) theEvent
{
	NXPoint localPoint = theEvent->location;
	NXEvent *nextEvent;
	NXCoord lastX=0.0;
	int oldMask;

	[self convertPoint:&localPoint fromView:nil];
	if (!NXMouseInRect(&localPoint, &dividerRect, NO))
		return self;
	oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];

		/* We've gotten a mouse down on the divider, so go ahead and draw the slider now */
	[self lockFocus];
	PSsetinstance(YES);
	PSsetgray(NX_LTGRAY);
	PSsetalpha(0.3333);
	PScompositerect(localPoint.x-HALFWIDTH, dividerRect.origin.y,
		DIVIDERWIDTH, dividerRect.size.height, NX_COPY);
	
		/* while the user is dragging composite a new slider in new postion -- watching for
		 * boundary conditions. Exit loop on mouse up.
		 */
 	do {
		nextEvent = [NXApp getNextEvent:eventMask];
		localPoint = nextEvent->location;
		[self convertPoint:&localPoint fromView:nil];
		if (localPoint.x < minX) localPoint.x = minX;
		if (localPoint.x > maxX) localPoint.x = maxX;
		if (localPoint.x != lastX) {
			/* wait some random amount of time within retrace period. */
			usleep((random() % (int)RETRACE));
			PSnewinstance();
			PScompositerect(localPoint.x-HALFWIDTH, dividerRect.origin.y,
				DIVIDERWIDTH, dividerRect.size.height, NX_COPY);
			lastX = localPoint.x;
		}
	} while (nextEvent->type != NX_MOUSEUP);

		/* User is done dragging. Move the divider to new location
		 */	
	dividerRect.origin.x = localPoint.x - HALFWIDTH;
	PSsetinstance(NO);
	[self unlockFocus];
	[self adjustSubviews];
	[self display];
	[window setEventMask:oldMask];
	
	return self;
}

/*
 * blits the dimple to the screen in the right spot
 */
- drawDimple
{
	NXPoint origin;
	origin.x = dividerRect.origin.x;
	origin.y = floor(dividerRect.origin.y + dividerRect.size.height/2.0 - dimpleRect.size.height/2.0);
	[dimple composite:NX_COPY fromRect:&dimpleRect toPoint:&origin];
	return self;
}

/*
 * modifies the size of the subviews after user has positioned the divider, or
 * has resized the window.
 * also used to initialize the sizes of the subviews.
 */
- adjustSubviews
{
	NXRect leftViewFrame, rightViewFrame;

		/* first the left one  */
	[leftView getFrame:&leftViewFrame];
	leftViewFrame.size.width = dividerRect.origin.x - leftViewFrame.origin.x;
	leftViewFrame.size.height = bounds.size.height;
	leftViewFrame.origin.x = bounds.origin.x;
	leftViewFrame.origin.y = bounds.origin.y;
	[leftView setFrame:&leftViewFrame];
	[window invalidateCursorRectsForView:leftView];

		/* then the right one */
	[rightView getFrame:&rightViewFrame];
	rightViewFrame.origin.x = leftViewFrame.size.width + DIVIDERWIDTH;
	rightViewFrame.origin.y = bounds.origin.y;
	rightViewFrame.size.width = bounds.size.width - rightViewFrame.origin.x;
	rightViewFrame.size.height = bounds.size.height;
	[rightView setFrame:&rightViewFrame];
	[window invalidateCursorRectsForView:rightView];
	
		/* update dividerRect in case of resize of window */
	dividerRect.origin.x = leftViewFrame.size.width;
	dividerRect.size.height = leftViewFrame.size.height;
	dividerRect.origin.y = leftViewFrame.origin.y;
	maxX = bounds.size.width - HALFWIDTH;
	return self;
}

/*
 * called when the window gets resized
 */
- resizeSubviews:(const NXSize *)oldSize 
{
    [super resizeSubviews:oldSize];
    [self adjustSubviews];
    return self;
}

@end
