DrawView.m

#import <appkit/appkit.h>

/*
DrawView:
	Subclasses from View:Responder:Object
	August 5, 1993
	Sean Luke
	
DrawView stores, manages, and displays DrawCells, to create a draw-program-like
environment.  It handles events and passes these to the DrawCells, which deal
with them as they like.
*/

@interface DrawView:View	
	{
	id		cell_list;					// list of all cells
	}

- initFrame:(const NXRect *) newFrame;			// overloads View's 
										// standard constructor
- drawSelf:(const NXRect*) rects:(int) rectCount;	// draws the cells
- mouseDown:(NXEvent*) theEvent;				// passes mouseDown events
- mouseDragged:(NXEvent*) theEvent;			// ... etc.
- mouseUp:(NXEvent*) theEvent;				// ... etc.

@end

DrawView.m

#import "DrawView.h"
#import "IconCell.h"
#import "RectangleCell.h"

@implementation DrawView

- initFrame:(const NXRect *) newFrame
	{
	id returnval=[super initFrame:newFrame];
	cell_list=[[List alloc] init];			// initialize the cell list
	
	// Make four members of cell list, two icons, two rectangles
	[cell_list addObject:[[[IconCell alloc] init] setView:self]];
	[cell_list addObject:[[[IconCell alloc] init] setView:self]];
	[cell_list addObject:[[[RectangleCell alloc] init] setView:self]];
	[cell_list addObject:[[[RectangleCell alloc] init] setView:self]];
	return returnval;
	}

- drawSelf:(const NXRect*) rects:(int) rectCount
	{
	int x;
	int max=[cell_list count];
	
	// erase entire view
	PSsetgray(NX_LTGRAY);
	NXRectFill(&bounds);
	
	// tell each cell to draw itself
	for (x=0;x<max;x++)
		{
		[[cell_list objectAt:x] drawSelf:rects:rectCount];
		}
	return self;
	}

- mouseDown:(NXEvent*) theEvent
	{
	int x;
	int max=[cell_list count];
	
	// add mouseDragged as a valid item in the view
	
	[window addToEventMask:NX_MOUSEDRAGGEDMASK];
	
	// deselect old object(s)
	for (x=0;x<max;x++)
		{
		[[cell_list objectAt:x] deselect];		
		}	
	
	// tell each cell to select itself if hit
	for (x=0;x<max;x++)
		{
		if ([[cell_list objectAt:x] hit:theEvent])
			{
			[[cell_list objectAt:x] select];
			[[cell_list objectAt:x] mouseDown:theEvent];
			break;
			}
		}
	// redisplay view
	[self display];
	return self;
	}

- mouseDragged:(NXEvent*) theEvent
	{
	int x;
	int max=[cell_list count];
	
	// tell each cell to move if selected
	for (x=0;x<max;x++)
		{
		if ([[cell_list objectAt:x] isSelected])
			{
			[[cell_list objectAt:x] mouseDragged:(NXEvent*) theEvent];
			break;
			}
		}
	// redisplay view
	[self display];
	return self;
	}

- mouseUp:(NXEvent*) theEvent
	{
	int x;
	int max=[cell_list count];
	
	// tell each cell to stop moving itself if selected
	for (x=0;x<max;x++)
		{
		if ([[cell_list objectAt:x] isSelected])
			{
			[[cell_list objectAt:x] mouseUp:(NXEvent*) theEvent];
			break;
			}
		}
	// redisplay view
	[self display];
	return self;
	}

@end

DrawCell.h

#import <appkit/appkit.h>

/*
DrawCell:
	Subclasses from Object
	August 5, 1993
	Sean Luke
	
Subclasses of DrawCell can draw themselves as individual drawing items in 
a DrawView.
*/

@interface DrawCell:Object	
	{
	NXPoint		coord;					// origin coordinate
	BOOL			selected;
	id			view;
	}

- init;
- setView:the_view;
- setPoint:(NXPoint*) the_point;				// sets NXPoint
- drawSelf:(const NXRect*) rects:(int) rectCount;	// draws the cell
- (BOOL) hit:(NXEvent*) theEvent;				// tests if hit at a point
- mouseDown:(NXEvent*) theEvent;				// handles mouseDown events
- mouseDragged:(NXEvent*) theEvent;			// ... etc.
- mouseUp:(NXEvent*) theEvent;				// ... etc.
- select;									// sets selected to YES
- deselect;								// ...to NO
- (BOOL) isSelected;						// returns selected

@end

DrawCell.m

#import "DrawCell.h"
#import "DrawView.h"

@implementation DrawCell

- init
	{
	id returnval=[super init];
	selected=0;
	// no need to initialize coord
	return returnval;
	}
	
- setPoint:(NXPoint*) the_point
	{
	coord=*the_point;
	return self;
	}

- setView:the_view
	{
	view=the_view;
	return self;
	}	

- drawSelf:(const NXRect*) rects:(int) rectCount
	{
	return self;
	}

- (BOOL) hit:(NXEvent*) theEvent
	{
	return NO;				// just a default
	}
	
- mouseDown:(NXEvent*) theEvent
	{
	return self;
	}
	
- mouseDragged:(NXEvent*) theEvent
	{
	return self;
	}
	
- mouseUp:(NXEvent*) theEvent
	{
	return self;
	}
	
- select
	{
	selected=YES;
	return self;
	}
	
- deselect
	{
	selected=NO;
	return self;
	}
	
- (BOOL) isSelected
	{
	return selected;
	}
	
@end

IconCell.h

#import "DrawCell.h"		// which imports appkit

/*
IconCell:
	Subclasses from DrawCell:Object
	August 5, 1993
	Sean Luke
	
Draws an icon object to be moved around a DrawView.
*/

@interface IconCell:DrawCell	
	{
	id 		icon;
	}

- init;
- drawSelf:(const NXRect*) rects:(int) rectCount;	// draws the cell
- (BOOL) hit:(NXEvent*) theEvent;				// tests if hit at a point
- mouseDown:(NXEvent*) theEvent;				// handles mouseDown events
- mouseDragged:(NXEvent*) theEvent;				// ... etc.
- mouseUp:(NXEvent*) theEvent;					// ... etc.

@end

IconCell.m

#import "IconCell.h"		// which imports appkit and hence NXImage.h

#define ICON_WIDTH 48
#define ICON_HEIGHT 48

@implementation IconCell

- init
	{
	id returnval=[super init];
	icon=[NXImage findImageNamed:"NXdefaulticon"];
	// we assume this is a 48x48 icon--see above
	return returnval;
	}

- drawSelf:(const NXRect*) rects:(int) rectCount
	{
	[icon composite:NX_SOVER toPoint:&coord];	
	return self;
	}

- (BOOL) hit:(NXEvent*) theEvent
	{
	NXPoint start=theEvent->location;
	NXRect temp_rect;
	
	// Load temp_rect with size and coordinates
	temp_rect.size.width=ICON_WIDTH;
	temp_rect.size.height=ICON_HEIGHT;
	temp_rect.origin=coord;

	// convert event's point into view coordinate system
	[view convertPoint:&start fromView:nil];
	return NXPointInRect(&start, &temp_rect);
	}

- mouseDown:(NXEvent*) theEvent
	{
	return [super mouseDown:theEvent];
	}

- mouseDragged:(NXEvent*) theEvent
	{
	NXPoint start=theEvent->location;
	[view convertPoint:&start fromView:nil];
	coord=start;
	return [super mouseDragged:theEvent];
	}

- mouseUp:(NXEvent*) theEvent
	{
	return [super mouseUp:theEvent];
	}
	
@end

RectangleCell.h

#import "DrawCell.h"		// which imports appkit

/*
RectangleCell:
	Subclasses from DrawCell:Object
	August 5, 1993
	Sean Luke
	
Draws a rectangle object to be moved around a DrawView.
*/

@interface RectangleCell:DrawCell	
	{
	NXSize		rectangle_size;
	float		line_width;				// width is >=0
	float		line_gray;					// grays are between 0 and 1
	float		fill_gray;
	}

- init;
- drawSelf:(const NXRect*) rects:(int) rectCount;	// draws the cell
- (BOOL) hit:(NXEvent*) theEvent;				// tests if hit at a point
- mouseDown:(NXEvent*) theEvent;				// handles mouseDown events
- mouseDragged:(NXEvent*) theEvent;				// ... etc.
- mouseUp:(NXEvent*) theEvent;					// ... etc.

@end

RectangleCell.m

#import "RectangleCell.h"			// which imports appkit
#import "RectangleCellWraps.h"

@implementation RectangleCell

- init
	{
	id returnval=[super init];
	line_width=4;
	line_gray=0;
	fill_gray=1;
	rectangle_size.width=30;
	rectangle_size.height=20;
	return returnval;
	}

- drawSelf:(const NXRect*) rects:(int) rectCount
	{
	RectangleCell_draw(coord.x, coord.y, 
		rectangle_size.width, rectangle_size.height,
		line_width, line_gray, fill_gray);
	return self;
	}

- (BOOL) hit:(NXEvent*) theEvent
	{
	NXPoint start=theEvent->location;
	NXRect temp_rect;
	
	// Load temp_rect with size and coordinates
	temp_rect.size=rectangle_size;
	temp_rect.origin=coord;

	// convert event's point into view coordinate system
	[view convertPoint:&start fromView:nil];
	return NXPointInRect(&start, &temp_rect);
	}

- mouseDown:(NXEvent*) theEvent
	{
	return [super mouseDown:theEvent];
	}

- mouseDragged:(NXEvent*) theEvent
	{
	NXPoint start=theEvent->location;
	[view convertPoint:&start fromView:nil];
	coord=start;
	return [super mouseDragged:theEvent];
	}

- mouseUp:(NXEvent*) theEvent
	{
	return [super mouseUp:theEvent];
	}

@end

RectangleCellWraps.psw

defineps RectangleCell_draw 
	(float x; float y; float w; float h; float lw; float lg; float fg)

	newpath
		x y moveto
		w 0 rlineto
		0 h rlineto
		w neg 0 rlineto
		0 h neg rlineto
	closepath
	gsave 
		fg setgray
		fill 
	grestore
		lg setgray
		lw setlinewidth
		stroke
endps