/*	MiscTDVPrivate.m

	Copyright 1996 Uwe Hoffmann.

	This notice may not be removed from this source code.
	The use and distribution of this software is governed by the
	terms of the MiscKit license agreement.  Refer to the license
	document included with the MiscKit distribution for the terms.

	Version 2 (August 1996)
*/

#import <mach/mach_init.h>

#import "MiscTreeDiagramView.h"
#import "MiscDiagramTree.h"
#import "MiscTreeDiagram.h"
#import "MiscUserPath.h"
#import "MiscHitPath.h"
#import "MiscTDVPrivate.h"
#import "MiscTDViewUtils.h"

static NSTextView *textView = nil;

@implementation MiscTreeDiagramView(PrivateMethods)

- (BOOL)_internalResizeAndPosTree:(MiscDiagramTree *)aTree force:(BOOL)aBOOL
{
	NSRect r;
	NSPoint oldPos;
	NSPoint newPos;
	float dx, dy;
	int tx,ty;
	BOOL result = NO;
	
	r = [aTree branchDrawBounds];
	oldPos = [aTree pos];
	dx = oldPos.x - r.origin.x;
	dy = oldPos.y - r.origin.y;
	tx = ceil(NSWidth(r) / NSWidth(canvasRect));
	ty = ceil(NSHeight(r) / NSHeight(canvasRect));
	if((ty != canvasYTimes || tx != canvasXTimes || aBOOL) && [self enclosingScrollView]){
		canvasXTimes = tx;
		canvasYTimes = ty;
		[self setFrameSize:NSMakeSize(NSWidth(canvasRect) * canvasXTimes * zoomFactor,
                                NSHeight(canvasRect) * canvasYTimes * zoomFactor)];
		result = YES;
	}
	if(canvasXTimes == 1)
		newPos.x = ([self bounds].size.width - r.size.width) / 2 + dx;
	else
		newPos.x = 3 + dx;
	if(canvasYTimes == 1)	
		newPos.y = ([self bounds].size.height - r.size.height) / 2 + dy;
	else
		newPos.y = 3 + dy;
	[aTree setPos:newPos];
	return result;
}

- (void)_internalUpdateCanvasPos:(float)xPos :(float)yPos
{
	float *bbox;
    	float *params;
	
	bbox = [canvasPath bbox];
	params = [canvasPath params];
	canvasRect.origin.x = xPos;
	canvasRect.origin.y = yPos;
	bbox[0] = canvasRect.origin.x;
   	bbox[1] = canvasRect.origin.y;
    	bbox[2] = canvasRect.origin.x + canvasRect.size.width;
    	bbox[3] = canvasRect.origin.y + canvasRect.size.height;
	params[0] = canvasRect.origin.x;
	params[1] = canvasRect.origin.y;
	params[2] = canvasRect.size.width;
	params[3] = 0.0;
	params[4] = 0.0;
	params[5] = canvasRect.size.height;
	params[6] = -canvasRect.size.width;
	params[7] = 0.0;
	params[8] = 0.0;
	params[9] = -canvasRect.size.height;
}

- (void)_internalInit
{
	NSArray *types = [NSArray arrayWithObjects:MiscTreeDiagramPboardType,
            MiscTreeDiagramStylePboardType, NSColorPboardType, nil];
	
	buffer = nil;
        zoomFactor = 1;
	[self registerForDraggedTypes:types];
	linePath = [[MiscUserPath allocWithZone:[self zone]] init];
	canvasPath = [[MiscUserPath allocWithZone:[self zone]] init];
	hitPath = [[MiscHitPath allocWithZone:[self zone]] initWithWidth:4.0];
	nodes = [[NSMutableArray allocWithZone:[self zone]] init];
	canvasXTimes = 1;
	canvasYTimes = 1;
	if([self enclosingScrollView]){
		canvasRect = miscTD_calcCanvas([treeDiagram printInfo]);
		[self setFrameSize:NSMakeSize(canvasRect.size.width, canvasRect.size.height)];
	} else
		canvasRect = [self bounds];
	[canvasPath moveto:canvasRect.origin.x :canvasRect.origin.y];
	[canvasPath rlineto:canvasRect.size.width :0.0];
	[canvasPath rlineto:0.0 :canvasRect.size.height];
	[canvasPath rlineto:-canvasRect.size.width :0.0];
	[canvasPath rlineto:0.0 :-canvasRect.size.height];
	showAttachments = YES;
	repaintMode = MISCTDV_REPAINT_NORMAL;
        drawState.color = nil;
        drawState.font = nil;
        [self setPostsBoundsChangedNotifications:YES];
	[self setOperationMode:MiscEditMode];
	bgColor = [[NSColor whiteColor] copy];
}

- (void)_internalDrawTree:(MiscDiagramTree *)tree inRect:(NSRect)r
{
	int i;
	BOOL knobs, attachments;
	MiscTreeStyle *treeStyle = [treeDiagram treeStyle];
        
	[linePath resetFill]; 
	if((![[NSDPSContext currentContext] isDrawingToScreen] && ![[NSPrintOperation currentOperation] isEPSOperation]))
		[tree fillLines:linePath andAddTo:nodes];
	else
		[tree fillLines:linePath andAddTo:nodes ifInvolvedInRect:r];
        if([treeStyle shadow]){
                 if(![[treeStyle shadowColor] isEqual:drawState.color]){
                         [drawState.color release];
                         drawState.color = [[treeStyle shadowColor] retain];
                         [drawState.color set];
                 }
                 for(i = 0; i < [nodes count];i++)
                         [[nodes objectAtIndex:i] drawShadow:&drawState];
        }
	if([tree child] && ![tree isCollapsed]){
		PSsetgray(NSBlack);
                if(drawState.color)
                    	[drawState.color release];
		drawState.color = [[NSColor blackColor] retain];
		PSsetlinewidth(0.15);
		drawState.linewidth = 0.15;
		[linePath send:dps_ustroke cached:NO];
	}
	for(i = 0; i < [nodes count];i++)
		[[nodes objectAtIndex:i] drawEnding:&drawState];
	for(i = 0; i < [nodes count];i++)
		[[nodes objectAtIndex:i] drawFill:&drawState];
	if([[NSDPSContext currentContext] isDrawingToScreen]){
		knobs = YES;
		attachments = showAttachments;
	} else
		knobs = attachments = NO;
	for(i = 0; i < [nodes count];i++)
		[[nodes objectAtIndex:i] drawOutline:&drawState knobs:knobs viewScale:zoomFactor];
	for(i = 0; i < [nodes count];i++)
		[[nodes objectAtIndex:i] drawCellsInView:self attachments:attachments];
	[nodes removeAllObjects];	
}	

- (BOOL)_internalCopyToPasteboard:(NSPasteboard *)pb
{
	NSData *data;
	NSArray *types = [NSArray arrayWithObjects:MiscTreeDiagramPboardType, NSPostScriptPboardType, NSStringPboardType,nil];
	BOOL success = NO;
	MiscDiagramTree *theCopy;

	if(![treeDiagram selectedNode])
		return success;
	theCopy = [[treeDiagram selectedNode] copy];
	data = [NSArchiver archivedDataWithRootObject:theCopy];
	if(data){
		[pb declareTypes:types owner:[self class]];
		success = [pb setData:data forType:MiscTreeDiagramPboardType];
	}
	[theCopy release];
	return success;
}

- (BOOL)_internalPasteFromPasteboard:(NSPasteboard *)pb
{
	BOOL success = NO;
	MiscDiagramTree *pasteTree, *copyTree;
	NSData *data;
	id pasteObject;
	int i, nrCopies;

   	if(miscTD_includesType([pb types], MiscTreeDiagramPboardType)){
		data = [pb dataForType:MiscTreeDiagramPboardType];
		if(data){
			pasteObject = [NSUnarchiver unarchiveObjectWithData:data];
			if([pasteObject isKindOfClass:[NSDictionary class]]){
				pasteTree = (MiscDiagramTree *)[pasteObject objectForKey:@"Template"];
				if([treeDiagram selectedNode]){
					nrCopies = [[pasteObject objectForKey:@"NumberOfCopies"] intValue];
					[treeDiagram beginEditing];
					for(i = 0;i < nrCopies;i++){
						copyTree = (MiscDiagramTree *)[pasteTree copyWithZone:[self zone]];
						[treeDiagram addChildToSelected:copyTree];
						[copyTree release];
					}
					[treeDiagram endEditing];
					success = YES;
				} else if(![treeDiagram root]){
					[treeDiagram setRoot:pasteTree];
					success = YES;
				}
			} else if([pasteObject isKindOfClass:[MiscDiagramTree class]]){ 
				pasteTree = (MiscDiagramTree *)pasteObject;
				if([treeDiagram selectedNode]){
					[treeDiagram addChildToSelected:pasteTree];
					success = YES;
				} else if(![treeDiagram root]){
					[treeDiagram setRoot:pasteTree];
					success = YES;
				}
			}
		}
	}
	return success;
}

+ (void)_internalConvert:(NSData *)data toPostscriptPasteboard:(NSPasteboard *)pb
{
	NSWindow *w;
	MiscDiagramTree *pbtree;
    	NSZone *zone;
    	NSData *psdata;
    	MiscTreeDiagramView *scrapper;
    	NSRect scrapperFrame = {{0.0, 0.0}, {11.0*72.0, 14.0*72.0}};
	NSRect pbtreeBounds;

    	if(!data) 
		return;
    	zone = NSCreateZone(vm_page_size * 2, vm_page_size, NO);
    	NSSetZoneName(zone, @"Scrapper");
    	scrapper = [[self allocWithZone:zone] initWithFrame:scrapperFrame];
    	pbtree = (MiscDiagramTree *)[NSUnarchiver unarchiveObjectWithData:data];
	scrapperFrame = [scrapper frame];
    	w = [[NSWindow allocWithZone:zone] 
		initWithContentRect:scrapperFrame 
		styleMask:NSBorderlessWindowMask 
		backing:NSBackingStoreNonretained defer:NO];
    	[w setContentView:scrapper];
	[[scrapper diagram] setRoot:pbtree];
	pbtreeBounds = [pbtree branchDrawBounds];
	pbtreeBounds = NSInsetRect(pbtreeBounds , -5 , -5);
	psdata = [scrapper dataWithEPSInsideRect:pbtreeBounds];
    	[pb setData:psdata forType:NSPostScriptPboardType];
    	[w release];
    	NSRecycleZone(zone);
}

+ (void)_internalConvert:(NSData *)data toAsciiPasteboard:(NSPasteboard *)pb;
{
	return;
}

- (void)_internalStartLabelEditingFor:(MiscDiagramTree *)node event:(NSEvent *)event :(BOOL)mouseDown
{
	NSRect textFrame, r;
	NSWindow *window = [self window];
        NSTextContainer *textContainer;
        MiscNodeStyle *nodeStyle;

	[window disableFlushWindow];
	repaintMode = MISCTDV_REPAINT_EVENT;
	[self  lockFocus];
	[window endEditingFor:self];
        textFrame = [node textBounds];

	if(!textView){
		textView = [[NSTextView alloc] initWithFrame:textFrame];
		[[textView textContainer] setWidthTracksTextView:NO];
        	[[textView textContainer] setHeightTracksTextView:NO];
        	[textView setHorizontallyResizable:NO];
        	[textView setVerticallyResizable:YES];
		[textView setFieldEditor:NO];
        	[textView setUsesFontPanel:YES];
        	[textView setDrawsBackground:YES];
	}

        textContainer = [textView textContainer];
        nodeStyle = [node nodeStyle];

        [textContainer setContainerSize:NSMakeSize(NSWidth(textFrame),miscTD_LargeNumberForText)];
        
        [textView setMinSize:NSMakeSize(NSWidth(textFrame),(2.0 * [textContainer lineFragmentPadding]))];
        [textView setMaxSize:textFrame.size];
        if([nodeStyle textPlacement] == MiscMiddleTextPlacement)
                [textView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)];
        else if([nodeStyle textPlacement] == MiscTopTextPlacement)
                [textView setAutoresizingMask:NSViewMinYMargin];
        else
                [textView setAutoresizingMask:NSViewMaxYMargin];
	
        [textView setFrame:textFrame];
        if([node label])
            	[[textView textStorage] setAttributedString:[node label]];
        [textView sizeToFit];

	r = [textView frame];

        if([nodeStyle textPlacement] == MiscMiddleTextPlacement){	       
		r.origin.y += (textFrame.size.height - r.size.height) / 2;
        } else if([nodeStyle textPlacement] == MiscBottomTextPlacement){	
		r.origin.y += textFrame.size.height - r.size.height;
    	}

	[textView setFrameOrigin:r.origin];

	labelEditingTempData.node = node;
	labelEditingTempData.textFrame = r;

        [textView setDelegate:self];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_internalLabelEditingFrameChanged:) 
		name:NSViewFrameDidChangeNotification object:textView];

        [self addSubview:textView];
	if(mouseDown)
            	[textView mouseDown:event];
	else
            	[textView selectAll:self];
	[self unlockFocus];
	repaintMode = MISCTDV_REPAINT_NORMAL;
	[window enableFlushWindow];
	[window flushWindow];
	PSWait();
        [window makeFirstResponder:textView];
}

- (void)_internalLabelEditingFrameChanged:(NSNotification *)notification 
{
	MiscNodeStyle *nodeStyle;
    	NSRect textFrame, r;
	
	textFrame = [labelEditingTempData.node textBounds];
	nodeStyle = [labelEditingTempData.node nodeStyle];
	r = [textView frame];

        if([nodeStyle textPlacement] == MiscMiddleTextPlacement){	
        	r.origin.y = textFrame.origin.y + (textFrame.size.height - r.size.height) / 2;
		[textView setFrameOrigin:r.origin];
        } else if([nodeStyle textPlacement] == MiscBottomTextPlacement){	
       	 	r.origin.y = textFrame.origin.y + textFrame.size.height - r.size.height;
		[textView setFrameOrigin:r.origin];
    	}
        [self displayRect:textFrame];
        [textView display];
	labelEditingTempData.textFrame = r;
}

- (void)textDidEndEditing:(NSNotification *)notification
{
    	NSTextView *textObject = [notification object];
	
	[textObject removeFromSuperview];
	[textObject setDelegate:nil];
	[[NSNotificationCenter defaultCenter] removeObserver:self];
        [treeDiagram setLabel:[textObject textStorage] of:labelEditingTempData.node];
}

- (void)_internalKeyNavigate:(MiscDiagramTree *)node direction:(unichar)aChar
{
	MiscLayoutTreeType layoutType;
	NSWindow *window;

	window = [self window];
	layoutType = [node layoutType];
	[window disableFlushWindow];
	repaintMode = MISCTDV_REPAINT_EVENT;
    	[self  lockFocus];
	switch(aChar){
		case NSLeftArrowFunctionKey:
				if(layoutType == MiscHorizontalTreeType)
					[treeDiagram selectParent];
				else
					[treeDiagram selectPreviousSibling];
		    break;
		case NSRightArrowFunctionKey:
				if(layoutType == MiscHorizontalTreeType)
					[treeDiagram selectChild];
				else
					[treeDiagram selectNextSibling];
		    break;
		case NSUpArrowFunctionKey:
		    	if(layoutType == MiscHorizontalTreeType)
					[treeDiagram selectPreviousSibling];
				else
					[treeDiagram selectParent];
		    break;
		case NSDownArrowFunctionKey:
		    	if(layoutType == MiscHorizontalTreeType)
					[treeDiagram selectNextSibling];
				else
					[treeDiagram selectChild];
		    break;
		default:
		    break;
	}
	[self unlockFocus];
	repaintMode = MISCTDV_REPAINT_NORMAL;
	[window enableFlushWindow];
	[window flushWindow];
	PSWait();
	[window makeFirstResponder:self];
}

- (void)_internalResize:(MiscDiagramTree *)node event:(NSEvent *)event control:(int)aInt
{
	BOOL oldWindowAcceptsMouseMoved;
	NSEvent *ev;
	BOOL finished;
	NSPoint pt,ptLast,delta;
	NSRect rectBounds,rectLast,rectVis;
	MiscDiagramTree *copyOfNode;
	int controlPointNumber;
	NSWindow *window = [self window];
        NSSize newSize, oldSize = [node size];

        rectBounds = [self bounds];
	controlPointNumber = aInt;	
	repaintMode = MISCTDV_REPAINT_EVENT;
	ptLast = [node controlPoint:aInt];
    	[self  lockFocus];
	oldWindowAcceptsMouseMoved = [window acceptsMouseMovedEvents];
	[window setAcceptsMouseMovedEvents:YES];
	ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
	finished = ([ev type] == NSLeftMouseUp);
	if([node parent])
		rectLast = [(MiscDiagramTree *)[node parent] branchDrawBounds];
	else
		rectLast = [node nodeDrawBounds];	
	rectVis = [self visibleRect];
	[node setResizingFlag];
	[self _internalScrollRect:NSZeroRect andRepaintRect:rectLast]; 
	[self _internalFillBuffer];
	copyOfNode = [node copyNodeWithZone:[self zone]];
	while(!finished){
		if([self autoscroll:ev]){
                    	rectVis = [self visibleRect];
             		rectLast = NSIntersectionRect(rectVis , rectLast);
                        [self _internalScrollRect:NSZeroRect andRepaintRect:rectLast];
                        [self _internalFillBuffer];
                }
                pt = [self convertPoint:[ev locationInWindow] fromView:nil];
                if(!NSPointInRect(pt, rectBounds)){
                    pt.x = MAX(pt.x,rectBounds.origin.x);
                    pt.y = MAX(pt.y,rectBounds.origin.y);
                    pt.x = MIN(pt.x,rectBounds.origin.x + rectBounds.size.width);
                    pt.y = MIN(pt.y,rectBounds.origin.y + rectBounds.size.height);
                }
                delta.x = pt.x - ptLast.x;
                delta.y = pt.y - ptLast.y;
                if(delta.x || delta.y){
                    rectVis = [self visibleRect];
                    rectLast = NSIntersectionRect(rectVis , rectLast);
                    [self _internalComposite:rectLast fromBufferBy:NSCompositeCopy];	
                    [copyOfNode resizeToControlPoint:pt :&controlPointNumber];
                    rectLast = [copyOfNode nodeDrawBounds];
                    ptLast = pt;
                }
                [copyOfNode drawResizingInView:self viewScale:zoomFactor];
                [window flushWindow];
                PSWait();
		ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
		finished = ([ev type] == NSLeftMouseUp);
	}
	[node resetResizingFlag];
        [window disableFlushWindow];
        newSize = [copyOfNode size];
        if(oldSize.width != newSize.width || oldSize.height != newSize.height){
        	[[node shape] setReflectHorizontal:[[copyOfNode shape] reflectedHorizontal]];
        	[[node shape] setReflectVertical:[[copyOfNode shape] reflectedVertical]];
                [treeDiagram setSize:newSize of:node];
        } else
		[self _internalScrollRectAndRepaintVisible:NSZeroRect];
	[window setAcceptsMouseMovedEvents:oldWindowAcceptsMouseMoved];
	[self unlockFocus];
	repaintMode = MISCTDV_REPAINT_NORMAL;
	[window enableFlushWindow];
	[window flushWindow];
	PSWait();
	[window makeFirstResponder:self];
	[copyOfNode release]; 
}

- (void)_internalRearange:(MiscDiagramTree *)node event:(NSEvent *)event
{
	BOOL oldWindowAcceptsMouseMoved;
	NSEvent *ev;
	BOOL finished;
	NSPoint pt,ptLast,delta,newPos,offset,minPoint,maxPoint;
	NSRect rectLast,rectVis;
	MiscDiagramTree *copyOfNode;
	int controlPointNumber;
        unsigned oldIndex, newIndex;
	MiscDiagramTree *firstChild, *lastChild, *c;
	NSWindow *window = [self window];

	if(![node parent] || ![[[node parent] child] sibling])
		return;
	firstChild = (MiscDiagramTree *)[[node parent] child];
	lastChild = (MiscDiagramTree *)[firstChild sibling];
	while([lastChild sibling])
		lastChild = (MiscDiagramTree *)[lastChild sibling];		
	if([node layoutType] == MiscHorizontalTreeType){
		minPoint.x = maxPoint.x = [node pos].x;
		minPoint.y = [firstChild pos].y - 2 * [node border] - [node size].height;
		maxPoint.y = [lastChild pos].y + [lastChild size].height + 2 * [node border]; 
	} else {
		minPoint.y = maxPoint.y = [node pos].y;
		minPoint.x = [firstChild pos].x - 2 * [node border] - [node size].width;
		maxPoint.x = [lastChild pos].x + [lastChild size].width + 2 * [node border];
	}	
	controlPointNumber = 0;	
	repaintMode = MISCTDV_REPAINT_EVENT;
    	[self  lockFocus];
	oldWindowAcceptsMouseMoved = [window acceptsMouseMovedEvents];
	[window setAcceptsMouseMovedEvents:YES];
	ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
	finished = ([ev type] == NSLeftMouseUp);
	rectLast = [(MiscDiagramTree *)[node parent] branchDrawBounds];	
	rectVis = [self visibleRect];
	pt = [self convertPoint:[ev locationInWindow] fromView:nil];
	ptLast = pt;
	offset.x = pt.x - [node pos].x;
	offset.y = pt.y - [node pos].y;
	[node setResizingFlag];
	[self _internalScrollRect:NSZeroRect andRepaintRect:rectLast]; 
	[self _internalFillBuffer];
	copyOfNode = [node copyNodeWithZone:[self zone]];
	while(!finished){
                if([self autoscroll:ev]){
                    rectVis = [self visibleRect];
                    rectLast = NSIntersectionRect(rectVis , rectLast);
                    [self _internalScrollRect:NSZeroRect andRepaintRect:rectLast];
                    [self _internalFillBuffer];
                }
                pt = [self convertPoint:[ev locationInWindow] fromView:nil];
		delta.x = pt.x - ptLast.x;
	   	delta.y = pt.y - ptLast.y;
		if(delta.x || delta.y){
			rectVis = [self visibleRect];
			rectLast = NSIntersectionRect(rectVis , rectLast);
			[self _internalComposite:rectLast fromBufferBy:NSCompositeCopy];	
			newPos = [copyOfNode pos];
			if([node layoutType] == MiscHorizontalTreeType){
				newPos.y = pt.y - offset.y;
				newPos.y = MAX(minPoint.y, newPos.y);
				newPos.y = MIN(maxPoint.y, newPos.y);
			} else {
				newPos.x = pt.x - offset.x;
				newPos.x = MAX(minPoint.x, newPos.x);
				newPos.x = MIN(maxPoint.x, newPos.x);
			}
			[copyOfNode setPos:newPos];
			[copyOfNode drawResizingInView:self viewScale:zoomFactor];
			[window flushWindow];
			PSWait();
			rectLast = [copyOfNode nodeDrawBounds];
			ptLast = pt;
		} 
		ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
		finished = ([ev type] == NSLeftMouseUp);
	}
	[node resetResizingFlag];
	c = firstChild;
	oldIndex = 0;
	while(node != c){
		oldIndex++;
		c = (MiscDiagramTree *)[c sibling];
	}
	c = firstChild;
	newIndex = 0;
	if([node layoutType] == MiscHorizontalTreeType){	
		while(c && [copyOfNode pos].y > [c pos].y){
			if(c != node)
				newIndex++;
			c = (MiscDiagramTree *)[c sibling];
		}
	} else {
		while(c && [copyOfNode pos].x > [c pos].x){
			if(c != node)
				newIndex++;
			c = (MiscDiagramTree *)[c sibling];
		}
	}
	[window disableFlushWindow];
        if(newIndex != oldIndex)
            	[treeDiagram rearrangeChild:node toIndex:newIndex];
        else
		[self _internalScrollRectAndRepaintVisible:NSZeroRect];
	[window setAcceptsMouseMovedEvents:oldWindowAcceptsMouseMoved];
	[self unlockFocus];
	repaintMode = MISCTDV_REPAINT_NORMAL;
	[window enableFlushWindow];
	[window flushWindow];
	PSWait();
	[window makeFirstResponder:self];
	[copyOfNode release]; 
}

- (void)_internalFillBuffer
{
	NSRect rect, contentRect;
	id contentView;
	NSSize oldSize;
   
	rect = [self visibleRect];
	contentView = [[self window] contentView];
	contentRect = [contentView bounds];
	if(![contentView gState]){
		[contentView allocateGState];
		[contentView lockFocus];
		[contentView unlockFocus];
	}
	rect = [self convertRect:rect toView:contentView];
	if(buffer){
		oldSize = [buffer size];
		if(contentRect.size.width > oldSize.width || contentRect.size.height > oldSize.height)
			[buffer setSize:contentRect.size];
	} else 
		buffer = [[NSImage allocWithZone:[self zone]] initWithSize:contentRect.size];
	[buffer lockFocus];
		PSsetgray(NSWhite);
		NSRectFill(rect);	  
		PScomposite(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height,
			[contentView gState],rect.origin.x,rect.origin.y,NSCompositeCopy);
	[buffer unlockFocus];
}

	
- (void)_internalComposite:(NSRect)r fromBufferBy:(int)op
{
	NSRect rect;
	id contentView;
	NSPoint pt = {0,0};
   
   	if(!buffer)
		return;  
	contentView = [[self window] contentView];
	rect = [self convertRect:r toView:contentView];
	[contentView lockFocus];
		PSgsave();
		NSRectClip(rect);
		[buffer compositeToPoint:pt operation:NSCompositeCopy];
		PSgrestore();
	[contentView unlockFocus];   
}

- (void)_internalScrollRectAndRepaintVisible:(NSRect)scrollRect
{
	NSRect rx;
	NSWindow *window = [self window];
	
	rx = [self visibleRect];
	if(repaintMode == MISCTDV_REPAINT_NORMAL){
		[window disableFlushWindow];
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self displayRect:rx];
		[window enableFlushWindow];
		[window flushWindow];
	} else {
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self drawRect:rx];
        }    
}

- (void)_internalScrollRectAndRepaintAll:(NSRect)scrollRect
{
	NSWindow *window = [self window];

	if(repaintMode == MISCTDV_REPAINT_NORMAL){
		[window disableFlushWindow];
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self displayRect:[self bounds]];
		[window enableFlushWindow];
		[window flushWindow];
	} else {
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self drawRect:[self bounds]];
        }        
}

- (void)_internalScrollRect:(NSRect)scrollRect andRepaintRect:(NSRect)aRect
{
	NSWindow *window = [self window];

	if(repaintMode == MISCTDV_REPAINT_NORMAL){
		[window disableFlushWindow];
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self displayRect:aRect];
		[window enableFlushWindow];
		[window flushWindow];
	} else {
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self drawRect:aRect];
        }        
}

- (void)_internalScrollRect:(NSRect)scrollRect andRepaintRects:(NSRect)rect1 :(NSRect)rect2
{
	NSWindow *window = [self window];

	if(repaintMode == MISCTDV_REPAINT_NORMAL){
		[window disableFlushWindow];
            	[self lockFocus];
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self drawRect:rect1];
		[self drawRect:rect2];
		[self unlockFocus];
		[window enableFlushWindow];
		[window flushWindow];
	} else {
            	if(scrollRect.size.width > 0 || scrollRect.size.height > 0)
                    	[self scrollRectToVisible:scrollRect];
		[self drawRect:rect1];
		[self drawRect:rect2];
	}
}

- (void)_internalZoomToFactor:(float)aZoomFactor
{
    	MiscDiagramTree *selectedNode = [treeDiagram selectedNode];
        NSScrollView *sv = [self enclosingScrollView];
        NSRect newFrameRect;
        
        if(!sv || aZoomFactor == zoomFactor)
            return;        
        [self scaleUnitSquareToSize:NSMakeSize(aZoomFactor / zoomFactor,aZoomFactor / zoomFactor)];
        newFrameRect = [self frame];
        newFrameRect.size.width *= aZoomFactor / zoomFactor;
        newFrameRect.size.height *= aZoomFactor / zoomFactor;
        [self setFrame:newFrameRect];
        zoomFactor = aZoomFactor;
        if(selectedNode)
        	[self scrollRectToVisible:[selectedNode nodeDrawBounds]];
	[[self window] display];
}

- (void)_internalDrag:(MiscDiagramTree *)node event:(NSEvent *)event
{
	NSImage *dragImage;
	NSPasteboard *dragPboard;
	NSData *data = nil;
	NSPoint imageLocation,pt,ptLast,delta,textOffset;
	NSRect dragRect,textRect;
        BOOL finished = NO, moved = NO, oldWindowAcceptsMouseMoved;
        NSWindow *window = [self window];
        NSEvent *ev;
        id contentView;
	int nrNodes;
	NSDictionary *dragNodes;
	NSString *keys[2];
	NSObject *values[2];
	
        oldWindowAcceptsMouseMoved = [window acceptsMouseMovedEvents];
	[window setAcceptsMouseMovedEvents:YES];
	ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
	finished = ([ev type] == NSLeftMouseUp);
	pt = [ev locationInWindow];
	ptLast = pt;
	while(!finished){
                pt = [ev locationInWindow];
		delta.x = ABS(pt.x - ptLast.x);
	   	delta.y = ABS(pt.y - ptLast.y);
		if(delta.x > 5 || delta.y > 5){
                    	finished = YES;
                    	moved = YES;
                }    
		ev = [window nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask];
		finished = finished || ([ev type] == NSLeftMouseUp);
	}
        if(!moved || [ev type] == NSLeftMouseUp)
            	return;
	[window setAcceptsMouseMovedEvents:oldWindowAcceptsMouseMoved];
	[NSApp preventWindowOrdering];

    	dragPboard = [NSPasteboard pasteboardWithName:NSDragPboard];
	if(!paletteControl)
		nrNodes = 1;
	else
		nrNodes = [paletteControl intValue];
	if(nrNodes <= 1) 
		data = [NSArchiver archivedDataWithRootObject:node];
	else if(nrNodes > 1){
		keys[0] = @"Template";
		keys[1] = @"NumberOfCopies";
		values[0] = [[node copy] autorelease];
		values[1] = [NSNumber numberWithInt:nrNodes];
		dragNodes = [NSDictionary dictionaryWithObjects:(id *)values 
					forKeys:keys count:2]; 
		data = [NSArchiver archivedDataWithRootObject:dragNodes];
	}
	[dragPboard declareTypes:[NSArray arrayWithObject:MiscTreeDiagramPboardType] owner:[self class]];
	[dragPboard setData:data forType:MiscTreeDiagramPboardType];

	dragRect = [node nodeDrawBounds];
	dragImage = [[[NSImage alloc] initWithSize:dragRect.size] autorelease];
	
        textRect = [node textBounds];

        textOffset.x = textRect.origin.x - dragRect.origin.x;
        textOffset.y = textRect.origin.y - dragRect.origin.y;

        contentView = [[self window] contentView];
        
        textRect = [self convertRect:textRect toView:contentView];
        
	if(![contentView gState]){
		[contentView allocateGState];
		[contentView lockFocus];
		[contentView unlockFocus];
	}

        [window disableFlushWindow];
        repaintMode = MISCTDV_REPAINT_EVENT;
        [self  lockFocus];
        [node setSelected:NO];
        [self _internalScrollRect:NSZeroRect andRepaintRect:dragRect];
	[dragImage lockFocus];
		PStranslate(-dragRect.origin.x,-dragRect.origin.y);
		[[NSColor colorWithCalibratedRed:1 green:1 blue:1 alpha:0] set];
		NSRectFill(dragRect);
		[[NSColor colorWithCalibratedRed:0 green:0 blue:0 alpha:1] set];	  
		[node drawResizing];
                PStranslate(dragRect.origin.x,dragRect.origin.y);
                PScomposite(textRect.origin.x,textRect.origin.y,textRect.size.width,textRect.size.height,
			[contentView gState],textOffset.x,textOffset.y,NSCompositeCopy);
	[dragImage unlockFocus];
        [node setSelected:YES];
	[self _internalScrollRect:NSZeroRect andRepaintRect:dragRect];
        [self unlockFocus];
        repaintMode = MISCTDV_REPAINT_NORMAL;
        [window enableFlushWindow];
	[window flushWindow];
	PSWait();
        
	imageLocation.x = dragRect.origin.x;
	imageLocation.y = dragRect.origin.y + dragRect.size.height;
    	[self dragImage:dragImage 
		at:imageLocation 
		offset:NSZeroSize event:event pasteboard:dragPboard source:self slideBack:YES];
}

- (void)_internalSetupPaletteMode
{
	if(isOwnerOfDiagram)
		[treeDiagram release];
	treeDiagram = [[MiscTreeDiagram allocWithZone:[self zone]] init];
	[treeDiagram setView:self];
	isOwnerOfDiagram = YES;
	[treeDiagram setRoot:[[MiscDiagramTree alloc] initWithSize:NSMakeSize(50,30) shapeType:MiscRectangleShapeType]];
	[self setBackgroundColor:[NSColor colorWithCalibratedRed:0.8509 green:0.7372 blue:0.5764 alpha:1.0]];
}	

@end
