/*
Pencil V1.0, Copyright 1994, 95 by Florian Marquardt.
This program may be distributed under the terms of the GNU general
public license (Start Pencil and select "Info>COPYING..." for a copy of the
license).
*/
#import "PencilView.h"
#import "PencilGraphic.h"
#import "MultipleSelection.h"
#import "Group.h"
#import "PencilImage.h"
#import "PencilText.h"

#define EX te->location.x
#define EY te->location.y

#define EDITED if(!edited) { edited=YES; [window setDocEdited:YES]; }

BOOL movementStatus=0;	// currently moving a graphic? (EPS images only redrawn when move finished)
extern BOOL globalTermination;
id globalText=nil; // used for drawing RichText
NXStream *result=NULL; // used for passing results to the TCL interpreter (PencilTCLClient)

float *fpts=NULL;	// array for points for freehand drawing... global since there is only one mouse
int maxfpts=200;	// arraysize=2*maxfpts*sizeof(float)
float athresh, sumthresh;

@implementation PencilView
- initFrame:(const NXRect *)re
{
	[super initFrame:re];
	glist=[[List alloc] init];
	scale=100;
	if(fpts==NULL)
	{	fpts=(float *)malloc(sizeof(float)*2*maxfpts);  }
	if(!globalText)
	{
	globalText=[[Text alloc] init];
	[globalText setMonoFont:NO];
	[globalText setEditable:NO];
	[globalText setSelectable:NO];
	[globalText setFlipped:YES];
	[globalText setClipping:NO];
	}
	if(flippedView=[[View alloc] initFrame:&bounds]) // for RichText editing
	{
		[flippedView setFlipped:YES];
	}
	return self;
}

- (BOOL)acceptsFirstResponder { return YES; }

- awakeFromNib
{
	[window makeKeyAndOrderFront:self];
	[inspector inspectForView:self];
	return self;
}

- free
{
	int i;

	if(glist) {
	i=[glist count];
	while(i--) [[glist objectAt:i] free];	
	[glist free];
	}
	if(customDef)	free(customDef);
	if(docname) free(docname);
	return [super free];
}

- (BOOL)acceptsFirstMouse { return YES; }

- drawSelf:(const NXRect *)re:(int)co
{
	int i,c; id g;

	if(!initialized || NXDrawingStatus!=NX_DRAWING)
	{
	char filename[300];
	NXStream *stream;
	char *buffer;
	long l;

	strcpy(filename, [[NXBundle mainBundle] directory]);
	strcat(filename, "/../pencilDefinitions.ps");
	stream=NXMapFile(filename, NX_READONLY);
	if(!stream)
	{
	strcpy(filename, [[NXBundle mainBundle] directory]);
	strcat(filename, "/pencilDefinitions.ps");
	stream=NXMapFile(filename, NX_READONLY);
	}
		if(stream)
		{
		NXSeek(stream,0,NX_FROMEND);
		buffer=(char *)malloc(sizeof(char)*(l=NXTell(stream)+1));
		NXSeek(stream,0,NX_FROMSTART);
		NXRead(stream,buffer,l-1);
		NXClose(stream);
		buffer[l-1]=0;
		DPSPrintf(DPSGetCurrentContext()," %s\n",buffer);
		free(buffer);
		}
		else
		{
			NXRunAlertPanel("Alert", "Couldn't find pencilDefinitions.ps", "OK?!",NULL,NULL);
			[NXApp terminate:self];
		}
	if(NXDrawingStatus==NX_DRAWING)
	{
	strcpy(filename, [[NXBundle mainBundle] directory]);
	strcat(filename, "/../pencilControlProcDefinitions.ps");
	stream=NXMapFile(filename, NX_READONLY);
	if(!stream)
	{
	strcpy(filename, [[NXBundle mainBundle] directory]);
	strcat(filename, "/pencilControlProcDefinitions.ps");
	stream=NXMapFile(filename, NX_READONLY);
	}
		if(stream)
		{
		NXSeek(stream,0,NX_FROMEND);
		buffer=(char *)malloc(sizeof(char)*(l=NXTell(stream)+1));
		NXSeek(stream,0,NX_FROMSTART);
		NXRead(stream,buffer,l-1);
		NXClose(stream);
		buffer[l-1]=0;
		DPSPrintf(DPSGetCurrentContext()," %s\n",buffer);
		free(buffer);
		}
		else
		{
			NXRunAlertPanel("Alert", "Couldn't find pencilControlProcDefinitions.ps", "OK?!",NULL,NULL);
			[NXApp terminate:self];
		}
	}
	else
		if(customDef) DPSPrintf(DPSGetCurrentContext(), " %s\n ", customDef);
		if(NXDrawingStatus==NX_DRAWING) initialized=YES;
	}
	[window disableFlushWindow];
	PSsetgray(1);
	if(re)	{ NXRectClip(re); NXRectFill(re); } else NXRectFill(&bounds);
	c=[glist count];
	for(i=0;i<c;i++) if(![(g=[glist objectAt:i]) selected]) [g draw:re];
	[window reenableFlushWindow];
	return self;
}

- displayCurrentGraphic
{
	if(currentGraphic)
	{
	[self lockFocus];
		PSsetinstance(YES);
		PSnewinstance();
		[currentGraphic drawControl:NULL:curPt:500/scale];
		PSsetinstance(NO);
	[self unlockFocus];
	}
	return self;
}

- displayWithCurrentGraphic
{
	[self display];
	[self displayCurrentGraphic];
	return self;
}

- setCurrentGraphicAndDisplay:(id)gr
{
	currentGraphic=gr;
	[self displayCurrentGraphic];
	return self;
}

#define IFCUR if(currentGraphic)
#define CUR currentGraphic

const char *txtinit="/text (Text) def /fontsize 32 def /font (Helvetica) def";

#define ADJUST [self convertPoint:&te->location fromView:nil]
- mouseDown:(NXEvent *)te
{
	// command-click: create new graphic
	// click: if currentGraphic selected: select/move pts or move graphic
	// 		if not: try to select/move graphic
	// control-click: rotate

	if(!editingRichText)
	{
	if(te->flags & NX_COMMANDMASK) // create
	{
		char *mname; NXColor c1; NXColor c2; float lw; char *dm; char *fm; char *sm; char *ud; NXRect rec; 

		EDITED;
		IFCUR { [CUR calculateBoundingBox:self]; [CUR giveBounds:&rec]; [CUR select:NO];   }
			else	rec=bounds;
		[inspector giveSettings:&mname:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
		if(!currentGraphic && !strncmp(mname,"charP", 5))
		{
					[inspector setUserDef:txtinit];
					ud=txtinit;
		}
		if(!strcmp(mname,"RT"))
			[glist addObject:CUR=[[[PencilText alloc] init] initWithSettings:mname:c1:c2:lw:dm:fm:sm:ud]];
		else
			[glist addObject:CUR=[[[PencilGraphic alloc] init] initWithSettings:mname:c1:c2:lw:dm:fm:sm:ud]];
		curGrPos=[glist count]-1;
		[CUR select:YES];
		[self display:&rec:1];
		// if RichText desired
		if(!strcmp(mname,"RT"))
		{
			editingRichText=YES;
			[self addSubview:flippedView];
			[flippedView setFrame:&bounds];
			[CUR editText:te:self:flippedView];
		}
		else
		{
		if([[[[[inspector creationType] target] itemList] selectedCell] tag]==1)
		{
			athresh=[[ inspector athreshField] floatValue];
			sumthresh=[[inspector sthreshField] floatValue];
			if(athresh<.1) athresh=1.5;
			if(sumthresh<.1)	sumthresh=.9;
			[CUR createPolyFreehand:te:&curPt:self:500/scale];
		}
		else
		{
			[CUR create:te:&curPt:self:500/scale];
		}
		}
	}
	else	// select graphic/point, move point
	{
		[self convertPoint:&te->location fromView:nil];

		IFCUR
		{
			if([CUR isMemberOf:[PencilText class]])
			{
				if(te->data.mouse.click==2)				{
					editingRichText=YES;
					EDITED;
					[self addSubview:flippedView];
					[flippedView setFrame:&bounds];
					[CUR editText:te:self:flippedView];
					return self;
				}
			}

			if(!(te->flags & NX_CONTROLMASK)) { // not rotating/scaling
			// select pt, move it
			if([CUR move:te:&curPt:self:500/scale])	{ EDITED; return self; }
			}
		}

		{	// select graphic
			int i, selCount=0;
			id curcur=currentGraphic;

			if(!CUR && !(te->flags & NX_CONTROLMASK)) te->flags&=(~NX_SHIFTMASK);
			if(CUR && ![CUR isMemberOf:[MultipleSelection class]] && (!(te->flags&NX_SHIFTMASK) || (te->flags & NX_CONTROLMASK)))
			{
			if((te->flags & NX_ALTERNATEMASK) && !(te->flags & NX_CONTROLMASK)) i=curGrPos; else i=curGrPos+1;
			while(i--) if([[glist objectAt:i] selected:te:&curPt:self]) break;
			if(i<0)
			{
				i=[glist count];
				while(--i>curGrPos) if([[glist objectAt:i] selected:te:&curPt:self]) break;
				if(i<=curGrPos)	i=-1;
			}
			}
			else
			{
				id g;

				i=[glist count]; while(i--) { if([g=[glist objectAt:i] selected]) ++selCount; if([g selected:te:&curPt:self]) break; }
			}
			if(i>=0)
			{
				char *name; NXColor c1; NXColor c2; float lw; char *dm, *fm, *sm, *ud;
				NXRect rec; BOOL multipleSelChanged=NO;

				if((te->flags & NX_SHIFTMASK) && !(te->flags & NX_CONTROLMASK)) // multiple selection
				{
				if(![[glist objectAt:i] selected])
				{
					if(!CUR || ![CUR isMemberOf:[MultipleSelection class]])
					{
						IFCUR {
						CUR=[[MultipleSelection alloc] initWithGraphic:CUR];
						[CUR addToSelection:[[glist objectAt:i] select:YES] posFromEnd:selCount];
						}
						else
						{
							CUR=[[MultipleSelection alloc] initWithGraphic:[[glist objectAt:i] select:YES]];
						}
					}
					else
						[CUR addToSelection:[[glist objectAt:i] select:YES] posFromEnd:selCount];
					multipleSelChanged=YES;
				}
				}
				else
				{
					if(!CUR || ![CUR isMemberOf:[MultipleSelection class]] || ![[glist objectAt:i] selected])
					{
					CUR=[glist objectAt:i];
					curGrPos=i;
					[CUR select:YES];
					}
				}
				if(curcur!=CUR && curcur!=nil && (!multipleSelChanged || ![CUR partOfSelection:curcur]))
				{
					[curcur calculateBoundingBox:self];
					[curcur giveBounds:&rec];
					[curcur select:NO];
					[self display:&rec:1];
				}
				if(curcur!=CUR   || multipleSelChanged)
				{
					[CUR calculateBoundingBox:self];
					[CUR giveBounds:&rec];
					[self display:&rec:1];
				}
				[self lockFocus];
				PSsetinstance(YES);
				if(curcur!=CUR   || multipleSelChanged)
				{
				PSnewinstance();
				[currentGraphic drawControl:NULL:curPt:500/scale];
				}
				if(curcur!=CUR)
				{
				[CUR giveSettings:&name:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
				[inspector takeSettings:name:c1:c2:lw:dm:fm:sm:ud];
				}
				{	// moving/rotating/scaling the selection
	
			if(te->flags & NX_CONTROLMASK) // rotate or scale
				{
			if(te->flags & NX_SHIFTMASK) // scale
			{
 	int	oldMask;
	BOOL shouldLoop=YES;
	float dx,dy, oldscalex=1, oldscaley=1, newscalex, newscaley;
	NXEvent thisEvent;
	
	EDITED;
	
    	oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];

	dx=te->location.x; dy=te->location.y;

    while (shouldLoop) {
        do {
	te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK)];
    	} while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));

   	ADJUST;
	newscalex=(EX-dx)/20+1;
	if(!(te->flags & NX_ALTERNATEMASK)) newscaley=newscalex; else newscaley=(EY-dy)/20+1;
	if(fabs(newscalex)<1e-3) newscalex=1e-3;
	if(fabs(newscaley)<1e-3) newscaley=1e-3;
	[CUR scaleCenter:dx:dy by:newscalex/oldscalex:newscaley/oldscaley];
	oldscalex=newscalex;
	oldscaley=newscaley;
	PSnewinstance();
	[CUR drawControl:NULL:curPt:500/scale];
	PSflushgraphics();
	NXPing();
	if(te->type==NX_LMOUSEUP) shouldLoop=NO;
    }
	PSnewinstance();
	[CUR drawControl:NULL:curPt:500/scale];
	PSflushgraphics();
	NXPing();
	PSsetinstance(NO);
	[self unlockFocus];
    [window setEventMask:oldMask];
	return self;
			}
			else
			{
  	int	oldMask;
	BOOL shouldLoop=YES;
	float dx,dy;
	BOOL placingHorizontal=YES;
	NXPoint pt0, pt1;
	NXEvent thisEvent;
	
    	oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];

	dx=te->location.x; dy=te->location.y;


    while (shouldLoop) {
            do {
	te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK | NX_LMOUSEDOWNMASK)];
    	} while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));

   	ADJUST;
		if(placingHorizontal)
		{
			PSnewinstance();
			[CUR drawControl:NULL:curPt:500/scale];
			PSsetgray(0);
			PSmoveto(dx-5,dy);
			PSrlineto(10,0); PSstroke();
			PSmoveto(dx, dy-5);
			PSrlineto(0,10); PSstroke();
			PSmoveto(dx,dy);
			PSlineto(te->location.x, te->location.y);
			PSstroke();
			PSflushgraphics();
			NXPing();
		}
		else
		{
			[CUR rotateAroundCenter:dx:dy fromPoint:&pt1 toPoint:&te->location];
			pt1=te->location;
			PSnewinstance();
			[CUR drawControl:NULL:curPt:500/scale];
			PSsetgray(.5);
			PSmoveto(dx-5,dy);
			PSrlineto(10,0); PSstroke();
			PSmoveto(dx, dy-5);
			PSrlineto(0,10); PSstroke();
			PSmoveto(dx,dy);
			PSlineto(pt0.x, pt0.y);
			PSstroke();
			PSflushgraphics();
			NXPing();
		}
		if(te->type==NX_LMOUSEUP)
		{
			if(placingHorizontal)
			{
				placingHorizontal=NO;
				pt0=pt1=te->location;
				if(pt0.x==dx && pt0.y==dy) { pt0.x+=30; pt1=pt0; }
			}
			else
				shouldLoop=NO;
		}
    }
	PSnewinstance();
	[CUR drawControl:NULL:curPt:500/scale];
	PSflushgraphics();
	NXPing();
	PSsetinstance(NO);
	[self unlockFocus];
    [window setEventMask:oldMask];
	return self;
}
}
else
{
  	int	oldMask;
	BOOL shouldLoop=YES;
	float dx,dy;
	NXEvent thisEvent;
	
    	oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];

	dx=te->location.x; dy=te->location.y;

    while (shouldLoop) {
        do {
	te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK)];
    	} while(te->type==NX_LMOUSEDRAGGED && ([NXApp peekNextEvent:NX_LMOUSEDRAGGEDMASK into:&thisEvent]));

   	ADJUST;
	if(EX!=dx || EY!=dy)
	{
	movementStatus=1;
	EDITED;
	[CUR addTranslation:EX-dx:EY-dy];
	dx=EX; dy=EY;
	PSnewinstance();
	[CUR drawControl:NULL:curPt:500/scale];
	PSflushgraphics();
	NXPing();
	}
	if(te->type==NX_LMOUSEUP)
		shouldLoop=NO; 

    }
    	if(movementStatus) {
		movementStatus=0;
		[CUR drawIfNeeded:NULL:curPt:500/scale];
	}
	PSsetinstance(NO);
	[self unlockFocus];
    [window setEventMask:oldMask];
    }
}
			}
			else		// no graphic selected
			{
				IFCUR
				{
					NXRect rec;

					[CUR calculateBoundingBox:self];
					[CUR giveBounds:&rec];
					[CUR select:NO];
					CUR=nil; curGrPos=-1;
					[self display:&rec:1];
					[self lockFocus]; PSnewinstance(); [self unlockFocus];
					[inspector clearUserDef:nil];
				}
				// drag selection
{	  int	oldMask;
	BOOL shouldLoop=YES;
	float dx,dy;
	int c=[glist count];
	NXRect theSel, thebounds;

    	oldMask = [window addToEventMask:NX_LMOUSEDRAGGEDMASK];

	dx=te->location.x; dy=te->location.y;

	[self lockFocus];
	PSsetinstance(YES);
	
    while (shouldLoop) {
        te = [NXApp getNextEvent:(NX_LMOUSEUPMASK |
                                         NX_LMOUSEDRAGGEDMASK)];

   	ADJUST;
	if(EX!=dx || EY!=dy)
	{
	PSnewinstance();
	PSsetgray(.5);
	PSrectstroke(dx,dy,EX-dx,EY-dy);
	PSflushgraphics();
	}
	if(te->type==NX_LMOUSEUP)
		shouldLoop=NO; 

    }
	PSsetinstance(NO);
	PSnewinstance();
	[self unlockFocus];
    [window setEventMask:oldMask];
	
	if(dx<EX) { theSel.origin.x=dx;  theSel.size.width=EX-dx; } else { theSel.origin.x=EX; theSel.size.width=dx-EX; }
	if(dy<EY) { theSel.origin.y=dy; theSel.size.height=EY-dy; } else { theSel.origin.y=EY; theSel.size.height=dy-EY; }
	if(theSel.size.width!=0 && theSel.size.height!=0)
	{
	for(i=0;i<c;i++)	{
	[[glist objectAt:i] giveBounds:&thebounds];
	if(NXIntersectsRect(&theSel, &thebounds))
	{
		IFCUR { [CUR addToSelection:[[glist objectAt:i] select:YES] posFromEnd:0]; }
		else
		{	[CUR=[MultipleSelection alloc] initWithGraphic:[[glist objectAt:i] select:YES]];
		 }
	}
	}
	IFCUR {
	[CUR calculateBoundingBox:self];
	[CUR giveBounds:&thebounds];
	[self display:&thebounds:1];
	[self displayCurrentGraphic];
	}
	}
}
			}
		}
	}
	}
	else
		[window endEditingFor:self];
	return self;
}

- (BOOL)makeGroup
{
	id oldcur;

	if(CUR && [CUR isMemberOf:[MultipleSelection class]])
	{
	EDITED;
	oldcur=CUR;
	[glist addObject:CUR=[[[Group alloc] initEmpty] select:YES]];
	[oldcur moveElementsFrom:glist to:[CUR slist]];
	[oldcur select:NO];
	return YES;
	}
	else
		return NO;
}

- ungroup
{
	if(CUR && [CUR isKindOf:[Group class]])
	{
		id sl, old;
		int i,c,pos;
	
		EDITED;
		sl=[old=CUR slist];
		c=[sl count];
		pos=[glist indexOf:CUR];
		[glist removeObject:CUR];
		CUR=[[MultipleSelection alloc] initEmpty];
		[old addElementsTo:[CUR slist]];
		for(i=0;i<c;i++)	[glist insertObject:[sl objectAt:i] at:pos+i];
		[old freeLeaveObjects];
	}
	return self;
}

- ungroup:sender
{
	[self ungroup];
	[self displayWithCurrentGraphic];
	return self;
}

- makeStandardGroup:sender
{
	if([self makeGroup])
	{
		[CUR setGroupType:PENCIL_STANDARD_GROUP];
		[self displayCurrentGraphic];
	}
	return self;
}

- makeClipGroup:sender
{
	if([self makeGroup])
	{
		[CUR setGroupType:PENCIL_CLIP_GROUP];
		[self displayCurrentGraphic];
	}
	return self;
}

- deselectAll:sender
{
				IFCUR
				{
					NXRect rec;

					[CUR calculateBoundingBox:self];
					[CUR giveBounds:&rec];
					[CUR select:NO];
					CUR=nil; curGrPos=-1; 
					[self display:&rec:1];
					[self lockFocus]; PSnewinstance(); [self unlockFocus];
					[inspector clearUserDef:nil];
				}
	return self;
}
- selectAll:sender
{
	int c=[glist count], i;
	NXRect thebounds;

	IFCUR { [CUR select:NO]; CUR=nil; curGrPos=-1; }
	for(i=0;i<c;i++)	{
		IFCUR { [CUR addToSelection:[[glist objectAt:i] select:YES] posFromEnd:0]; }
		else
		{	[CUR=[MultipleSelection alloc] initWithGraphic:[[glist objectAt:i] select:YES]];  }
	}
	[CUR calculateBoundingBox:self];
	[CUR giveBounds:&thebounds];
	NXIntersectionRect(&bounds,&thebounds);
	[self display:&thebounds:1];
	[self displayCurrentGraphic];
	return self;
}
- setMethodname:(char*)name { IFCUR { [CUR setMethodname:name]; EDITED; } return [self displayCurrentGraphic]; }
- setDrawingMethod:(char *)name { IFCUR  { [CUR setDrawingMethod:name]; EDITED; } return [self displayCurrentGraphic]; }
- setStrokeMethod:(char *)name { IFCUR { [CUR setStrokeMethod:name]; EDITED; } return [self displayCurrentGraphic]; }
- setFillMethod:(char *)name { IFCUR { [CUR setFillMethod:name]; EDITED; } return [self displayCurrentGraphic]; }
- setSpecialAttributes:(char *)name { IFCUR { [CUR setSpecialAttributes:name]; EDITED; } return [self displayCurrentGraphic]; }
- setColor1:(NXColor)col { IFCUR { [CUR setColor1:col]; EDITED; } return [self displayCurrentGraphic];}
- setColor2:(NXColor)col { IFCUR { [CUR setColor2:col]; EDITED; } return [self displayCurrentGraphic]; }
- setLineWidth:(float)lw { IFCUR { [CUR setLineWidth:lw]; EDITED; } return [self displayCurrentGraphic]; }
- changeScale:sender
{
	float sc;

	sc=atoi([[sender selectedCell] title]);
	if(sc>0)
	{
	float x,y,w,h;
	x=bounds.origin.x;
	y=bounds.origin.y;
	w=bounds.size.width;
	h=bounds.size.height;
	[self scale:sc/scale:sc/scale];
	[self translate: bounds.origin.x-(x+w/2-bounds.size.width/2):bounds.origin.y-(y+h/2-bounds.size.height/2)];
	scale=sc;
	[self display];
	[self displayCurrentGraphic];
	}
	return self;
}
- scroll:sender
{
	switch([sender tag])
	{
	case 0: [self translate:0:bounds.size.height*[scrollFactor floatValue]]; break;
	case 1: [self translate:0:bounds.size.height*(-[scrollFactor floatValue])]; break;
	case 2: [self translate:bounds.size.width*(-[scrollFactor floatValue]):0]; break;
	case 3: [self translate:bounds.size.width*[scrollFactor floatValue]:0]; break;
	}
	[self display];
	[self displayCurrentGraphic];
	return self;
}
- toFront:sender
{
	IFCUR
	{
		EDITED;
		if([CUR isMemberOf:[MultipleSelection class]])
			[CUR toFrontIn:glist];
		else
		{
		if(curGrPos!=[glist count]-1)
		{
			[glist removeObjectAt:curGrPos];
			[glist addObject:currentGraphic];
		}
		}
	}
	return self;
}
- toBack:sender
{
	IFCUR
	{
		EDITED;
		if([CUR isMemberOf:[MultipleSelection class]])
			[CUR toBackIn:glist];
		else
		{
		if(curGrPos!=0)
		{
			[glist removeObjectAt:curGrPos];
			[glist insertObject:currentGraphic at:0];
		}
		}
	}
	return self;
}
- insertPoint:sender
{
	IFCUR	{	EDITED; [(PencilGraphic *)currentGraphic insertPoint:&curPt]; [self displayCurrentGraphic]; }
	return self;
}
- insertNextPoint:sender
{
	IFCUR	{	EDITED; [(PencilGraphic *)currentGraphic insertNextPoint:&curPt]; [self displayCurrentGraphic]; }
	return self;
}
- deletePoint:sender
{
	IFCUR	{	EDITED; [(PencilGraphic *)currentGraphic deletePoint:&curPt]; [self displayCurrentGraphic]; }
	return self;
}

- insertThreePoints:sender
{
	IFCUR	{	EDITED; [(PencilGraphic *)currentGraphic insertThreePoints:&curPt]; [self displayCurrentGraphic]; }
	return self;
}
- alignThreePoints:sender
{
	IFCUR	{	EDITED; [(PencilGraphic *)currentGraphic alignThreePoints:&curPt]; [self displayCurrentGraphic]; }
	return self;
}

- keyDown:(NXEvent *)te
{
	if(te->data.key.charCode==127)
		[self deleteGraphic:self];
	else
	{
	switch((char)(te->data.key.charCode))
	{
	case '8': [self translate:0:bounds.size.height*[scrollFactor floatValue]]; break;
	case '2': [self translate:0:bounds.size.height*(-[scrollFactor floatValue])]; break;
	case '4': [self translate:bounds.size.width*(-[scrollFactor floatValue]):0]; break;
	case '6': [self translate:bounds.size.width*[scrollFactor floatValue]:0]; break;
	case ' ' :
	case 13: return [self paste:self:YES]; break;
	}
	[self display];
	[self displayCurrentGraphic];
	}
	return self;
}

- deleteGraphic:sender
{
	IFCUR {			EDITED; if([CUR isMemberOf:[MultipleSelection class]])
 [CUR deleteThemIn:glist]; else [glist removeObjectAt:curGrPos]; curGrPos=-1; currentGraphic=nil; [self lockFocus]; PSnewinstance(); [self unlockFocus];  }
	return self;
}

- writeDoc:(NXTypedStream *)stream
{
	int n, i;
	NXRect wframe;

	[window getFrame:&wframe];
	NXWriteRect(stream, &wframe);
	NXWriteRect(stream, &bounds);
	NXWriteRect(stream, &frame);
	n=[glist count];
	NXWriteTypes(stream,"f*i",&scale, &customDef, &n);
	for(i=0;i<n;i++)
		NXWriteRootObject(stream, [glist objectAt:i]);
	return self;
}

- readDoc:(NXTypedStream *)stream
{
	int n;
	NXRect nbounds, nframe, wframe;

	NXReadRect(stream, &wframe);
	NXReadRect(stream, &nbounds);
	NXReadRect(stream, &nframe);
	NXReadTypes(stream,"f*i",&scale, &customDef,&n);
	[glist=[List alloc] setAvailableCapacity:n];
	while(n--) [glist addObject:NXReadObject(stream)];
	[window placeWindow:&wframe];
	// [self setFrame:&nframe];
	[self setDrawSize:nbounds.size.width:nbounds.size.height];
	[self setDrawOrigin:nbounds.origin.x: nbounds.origin.y];
	return self;
}

- doSave:(char *)filename
{
	NXTypedStream *st;
	
	if(st=NXOpenTypedStreamForFile(filename, NX_WRITEONLY))
	{
	edited=NO;
	[window setDocEdited:NO];
	[self writeDoc:st];
	NXCloseTypedStream(st);
	}
	return self;
}

- saveAs:sender
{
	[[SavePanel new] setRequiredFileType:"pencil"];
	if([[SavePanel new] runModal]==NX_OKTAG)
	{
	if([[SavePanel new] filename])
	{
	if(docname) free(docname);
	docname=(char *)malloc(sizeof(char)*(strlen([[SavePanel new] filename])+1));
	strcpy(docname, [[SavePanel new] filename]);
	[window setTitleAsFilename:docname];
	[self doSave:docname];
	}
	}
	return self;
}

- save:sender
{
	if(docname) [self doSave:docname]; else [self saveAs:sender];
	return self;
}

- readFromFilename:(char *)name
{
	int i;
	NXStream *st;
	
	if(st=NXOpenTypedStreamForFile(name, NX_READONLY))
	{
	if(glist) {
	i=[glist count];
	while(i--) [[glist objectAt:i] free];	
	[glist free];
	}
	if(customDef)	free(customDef);
	if(docname) free(docname);
	docname=(char *)malloc(sizeof(char)*(strlen(name)+1));
	strcpy(docname, name);
	[window setTitleAsFilename:docname];
	[self readDoc:st];
	NXCloseTypedStream(st);
	[self sendCustom];
	// [self display];
	[window display];
	}
	return self;
}

- windowDidBecomeMain:sender
{
	char *name; NXColor c1; NXColor c2; float lw; char *dm, *fm, *sm, *ud;

	[inspector inspectForView:self];
	IFCUR { 				[CUR giveSettings:&name:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
				[inspector takeSettings:name:c1:c2:lw:dm:fm:sm:ud];
}
	return self;
}

- windowWillClose:sender
{
	int ch;

	if(!globalTermination)
	{
	if(edited)
	{
	if(docname)
		ch=NXRunAlertPanel("Close Document", "Do you want to save \"%s\" before closing it?", "Save","Don't save","Cancel", docname);
	else
		ch=NXRunAlertPanel("Close Document", "Do you want to save the yet unnamed document before closing it?", "Save","Don't save","Cancel");
	switch(ch)
	{
	case 0: [inspector viewVanished:self]; return self;
	case 1: [self save:self]; [inspector viewVanished:self]; return self;
	case -1: return nil;
	}
	}
	}
	else
	{
	if(edited)
	{
	if(docname)
		ch=NXRunAlertPanel("Close Document", "Do you want to save \"%s\" before quitting?", "Save","Don't save","Cancel", docname);
	else
		ch=NXRunAlertPanel("Close Document", "Do you want to save the yet unnamed document before quitting?", "Save","Don't save","Cancel");
	switch(ch)
	{
	case 0: [inspector viewVanished:self]; return self;
	case 1: [self save:self]; [inspector viewVanished:self]; return self;
	case -1: globalTermination=NO; return nil;
	}
	}
	}
	[inspector viewVanished:self]; return self;
}

-copy:sender
{
    const char  *const types[1] = {"PencilPBoardType"};
    id           pb = [Pasteboard new];
    char        *data;
    int          length;

	if(currentGraphic)
	{
	[CUR calculateBoundingBox:self];
    [pb declareTypes:types num:1 owner:nil];
    data = NXWriteRootObjectToBuffer(currentGraphic, &length);
    [pb writeType:types[0] data:data length:length];
    NXFreeObjectBuffer(data, length);
	}  
  return self;
}

- copyAsPostScript:sender
{
    const char  *const types[1] = {NXPostScriptPboardType};

	[self deselectAll:self];
	[[Pasteboard new] declareTypes:types num:1 owner:nil];
	[self writePSCodeInside:&bounds to:[Pasteboard new]];
  return self;
}

- cut:sender
{
	[self copy:sender];
	[self deleteGraphic:sender];
	return self;
}

- paste:sender
{
	return [self paste:sender:NO]; // no centering
}

-paste:sender:(BOOL)flag
{
    char  **type;
    id      pb = [Pasteboard new];
    char   *data;
    int     length;

    for(type = [pb types];*type;type++) {
        if(!strcmp(*type,"PencilPBoardType"))
            break;
    }
    if(*type) {
				char *name; NXColor c1; NXColor c2; float lw; char *dm, *fm, *sm, *ud;

				IFCUR
				{
					NXRect rec;

					[CUR calculateBoundingBox:self];
					[CUR giveBounds:&rec];
					[CUR select:NO];
					CUR=nil;
					curGrPos=-1;
					[self display:&rec:1];
					[self lockFocus]; PSnewinstance(); [self unlockFocus];
				}

        [pb readType:*type data:&data length:&length];
         currentGraphic =NXReadObjectFromBuffer(data, length);
	if([CUR isMemberOf:[MultipleSelection class]])
		[CUR addElementsTo:glist];
	else
		[glist addObject:CUR];
    NXFreeObjectBuffer(data, length);
	if(flag)
	{
		NXPoint c;
		
		[window getMouseLocation:&c];
		[self convertPoint:&c fromView:nil];
		[CUR centerAt:&c];
	}
	else
		[currentGraphic addTranslation:-10:-10];
	curGrPos=[glist count]-1;
		[CUR select:YES];
				[self lockFocus];
				PSsetinstance(YES);
				PSnewinstance();
				[currentGraphic drawControl:NULL:curPt:500/scale];
				[CUR giveSettings:&name:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
				[inspector takeSettings:name:c1:c2:lw:dm:fm:sm:ud];
				PSsetinstance(NO);
				[self unlockFocus];
		EDITED;
	}
    return self;
}

- sendCustom
{
	[self lockFocus];
	DPSPrintf(DPSGetCurrentContext(), " %s\n ", customDef);
	[self unlockFocus];
	return self;
}

- takeCustomFrom:(id)text
{
	if(customDef) free(customDef);
	customDef=(char *)malloc(sizeof(char)*([text textLength]+1));
	[text getSubstring:customDef start:0 length:[text textLength]+1];
	[self sendCustom];
	[self display];
	EDITED;
	return self;
}

- giveCustomTo:(id)text
{
	[text setText:(const char *)customDef];
	return self;
}

- importImage:sender
{
	const char *types[]={ "tiff", "eps", "rib", NULL };

	if([[OpenPanel new] runModalForTypes:types]==NX_OKTAG)
	{
		[self importImageFromFile:[[OpenPanel new] filename]:bounds.origin.x+5:bounds.origin.y+5];
		[self displayCurrentGraphic];
	}
	return self;
}

- importImageFromFile:(char *)name:(float)posx:(float)posy
{
		char *mname; NXColor c1; NXColor c2; float lw; char *dm; char *fm; char *sm; char *ud;
		[self deselectAll:self];
		[inspector giveSettings:&mname:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
		if(CUR=[[PencilImage alloc] initFromFile:name])
		{
			curGrPos=[glist count];
			[glist addObject:CUR];
			[CUR select:YES];
			[CUR initWithSettings:mname:c1:c2:lw:dm:fm:sm:ud];
			[CUR addTranslation:posx:posy];
			[CUR calculateBoundingBox:self];
			EDITED;
		}
		return self;
}

- richTextEnded:sender
{
	editingRichText=NO;
	[flippedView removeFromSuperview];
	return self;
}

- convertToCharPath:sender
{
	IFCUR {
		// is it a RichText?
		if([currentGraphic isKindOf:[PencilText class]])
		{
		id newtxt;
		char *mname; NXColor c1; NXColor c2; float lw; char *dm, *fm, *sm, *ud;
		
		EDITED;
		newtxt=[[PencilGraphic alloc] init];
		[CUR makeCharacterPath: self: newtxt]; // RichTxt will initialize CharP
		[glist addObject:newtxt];
		[self deleteGraphic:self];
		CUR=newtxt;
		curGrPos=[glist count]-2;
		[CUR select:YES];
		[self displayWithCurrentGraphic];
		{
				[CUR giveSettings:&mname:&c1:&c2:&lw:&dm:&fm:&sm:&ud];
				[inspector takeSettings:mname:c1:c2:lw:dm:fm:sm:ud];
		}
		}
	}
	return self;
}

- (char *)performCommand:(const char *)cmd
{
	static char resStr[20];
	int which=0;
	
	EDITED;
	if(!strcmp(cmd,"objectcount")) { sprintf(resStr, "%d", [glist count]); return resStr; }
	if(!strncmp(cmd,"getobject",9)) { 
		int num;
		sscanf(cmd,"%*s %d", &num);
		if(num>=0 && num<[glist count])
		{
			return [[glist objectAt:num] giveDescription];		}
	}
	if(!strncmp(cmd,"changeobject",12)) which=1; else if(!strncmp(cmd,"addobject",9)) which=2;
	
	if(which)
	{
		int type, num;
		if(result) NXCloseMemory(result, NX_TRUNCATEBUFFER);
		result=NXOpenMemory(cmd, strlen(cmd), NX_READONLY);
		if(which==2) NXScanf(result, "addobject %d",&type);
		else
			NXScanf(result, "changeobject %d %d",&num, &type);
		[self deselectAll:self];
		if(which==2)
		{
		switch(type)
		{
			case 0: CUR=[PencilGraphic alloc]; break;
			case 1: CUR=[PencilText alloc]; break;
		}
		}
		else
			if(num>=0 && num<[glist count])
				CUR=[glist objectAt:num];
		IFCUR
		{
			[CUR initFromDescription:result];
			curGrPos=[glist count];
			if(which==2) [glist addObject:CUR];
			[CUR calculateBoundingBox:self];
			CUR=nil;
			sprintf(resStr, "%d", curGrPos);
			curGrPos=-1;
			return resStr;
		}
	}
	if(!strcmp(cmd, "display")) {
		[self displayWithCurrentGraphic];
		return "1";
	}
	if(!strncmp(cmd, "resize", 6)) {
		NXRect f;
		sscanf(cmd, "resize %f %f %f %f", &f.origin.x, &f.origin.y, &f.size.width, &f.size.height);
		[window placeWindowAndDisplay:&f];
		return "1";
	}
	if(!strncmp(cmd, "save", 4)) {
		char name[400];
		sscanf(cmd, "save %s", name);
		[self doSave:name];
		return "1";
	}
	if(!strcmp(cmd, "selection")) {
	int len;
	int maxlen;
	char *buf;
	 
	if(result) NXCloseMemory(result, NX_TRUNCATEBUFFER);
	result=NXOpenMemory(NULL,0,NX_WRITEONLY);
	IFCUR {
		if([CUR isMemberOf:[MultipleSelection class]]) {
			id slist;
			int i;
			
			slist=[CUR slist];
			for(i=0;i<[slist count];i++) {
				NXPrintf(result, "%d ", [glist indexOf:[slist objectAt:i]]);
			}
		} else {
		NXPrintf(result, "%d", curGrPos);
		}
	}
	else
		NXPrintf(result, "");
	NXGetMemoryBuffer(result, &buf, &len, &maxlen);
	return buf;
	}
	if(!strncmp(cmd, "delete", 6)) {
		int num;
		
		sscanf(cmd, "delete %d", &num);
		[self deselectAll:self];
		[glist removeObjectAt:num];
		return "1";
	}
	if(!strcmp(cmd, "print")) {
		[self printPSCode:self];
		return "1";
	}
	if(!strcmp(cmd, "name")) {
		if(docname)
			return docname;
		else
			return "";
	}
	if(!strncmp(cmd,"move",4)) {
		float dx, dy;
		
		sscanf(cmd, "move %f %f", &dx, &dy);
		[self translate:dx:dy];
		return "1";
	}
	if(!strncmp(cmd,"scale",5)) {
		float dx;
		
		sscanf(cmd, "scale %f", &dx);
		[self scale:dx/scale:dx/scale];
		scale=dx;
		return "1";
	}
	if(!strcmp(cmd,"paste")) {
		[self paste:self];
		return "1";
	}
	if(!strncmp(cmd, "select1",7)) {
		int i;
	
		sscanf(cmd, "select1 %d", &i);
		IFCUR { [CUR select:NO]; CUR=nil; curGrPos=-1; }
		if(i>=0 && i<[glist count])	{ [CUR=[glist objectAt:i] select:YES]; curGrPos=i; }
		return "1";
	}
	if(!strncmp(cmd, "select",6)) {
	int  i, c=[glist count];

	if(result) NXCloseMemory(result, NX_TRUNCATEBUFFER);
	result=NXOpenMemory(cmd, strlen(cmd), NX_READONLY);

	NXScanf(result, "select");
	IFCUR { [CUR select:NO]; CUR=nil; curGrPos=-1; }
	while(NXScanf(result, " %d", &i)==1)	{
		if(i>=0 && i<c)
		{
		IFCUR { [CUR addToSelection:[[glist objectAt:i] select:YES] posFromEnd:0]; }
		else
		{	[CUR=[MultipleSelection alloc] initWithGraphic:[[glist objectAt:i] select:YES]];  }
		}
	}
	[CUR calculateBoundingBox:self];
	return "1";
	}
	if(!strcmp(cmd, "group")) {
		[self makeGroup];
		[CUR setGroupType:PENCIL_STANDARD_GROUP];
		return "1";
	}
	if(!strcmp(cmd, "ungroup")) {
		[self ungroup];
		return "1";
	}
	if(!strcmp(cmd,"clipgroup")) {
		[self makeClipGroup:self];
		return "1";
	}
	if(!strcmp(cmd, "front")) {
		[self toFront:self];
		return "1";
	}
	if(!strcmp(cmd, "back")) {
		[self toBack:self];
		return "1";
	}
	if(!strcmp(cmd, "copyPS")) {
		[self copyAsPostScript:self];
		return "1";
	}
	if(!strncmp(cmd, "import",6)) {
		char name[400];
		float x,y;
		
		sscanf(cmd, "import %s %f %f", name, &x, &y);
		[self importImageFromFile:name:x:y];
		return "1";
	}
	return "failed";
}
@end
