/*	MiscTreeDiagramView.h

	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 "MiscTreeDiagramView.h"
#import "MiscTreeDiagram.h"
#import "MiscUserPath.h"
#import "MiscHitPath.h"
#import "MiscTDVPrivate.h"
#import "MiscTDUtils.h"
#import "MiscDGWriter.h"
 
@implementation MiscTreeDiagramView

- initWithFrame:(NSRect)frameRect treeDiagram:(MiscTreeDiagram *)aDiagram
{
	[super initWithFrame:frameRect];
	treeDiagram = aDiagram;
	isOwnerOfDiagram = NO;
	[self _internalInit];
	return self;
}

- initWithFrame:(NSRect)frameRect
{
	[super initWithFrame:frameRect];
	treeDiagram = [[MiscTreeDiagram allocWithZone:[self zone]] init];
	[treeDiagram setView:self];
	isOwnerOfDiagram = YES;
	[self _internalInit];
	return self;
}

- (void)dealloc
{
	if(isOwnerOfDiagram)
		[treeDiagram release];
	[linePath release];
	[hitPath release];
	[canvasPath release];
	[nodes release];
	if(buffer)
		[buffer release];
	return [super dealloc];
}

- (BOOL)isFlipped
{
    return YES;
}

- (BOOL)isOpaque
{
    return YES;
}

- (float)length
{
    	return 1;
}

- (int)canvasWidthNumberOfPages
{
	return canvasXTimes;
}

- (int)canvasHeightNumberOfPages
{
	return canvasYTimes;
}

- (void)lockFocus
{
   	float rot,gruen,blau;

        [super lockFocus];
	if([[NSDPSContext currentContext] isDrawingToScreen]){
		PScurrentlinewidth(&drawState.linewidth);
		PScurrentrgbcolor(&rot,&gruen,&blau);
                if(drawState.color)
                    	[drawState.color release];
		drawState.color = [[NSColor colorWithCalibratedRed:rot green:gruen blue:blau alpha:1.0] retain];
		drawState.font = nil;
   	}
}

- (void)unlockFocus
{
	[super unlockFocus];
        if(drawState.color)
                [drawState.color release];
        drawState.color = nil;
        if(drawState.font)
                 [drawState.font release];
        drawState.font = nil;
}

- (MiscTreeDiagram *)diagram
{
	return treeDiagram;
}

- (void)addToPageSetup
{
    	[super addToPageSetup];
     	if(zoomFactor != 1.0){
             PSscale(1 / zoomFactor, 1 / zoomFactor);
     	}
}

- (float)zoomFactor
{
    	return zoomFactor;
}

- (void)zoomToFit
{
        NSRect docFrame, contentFrame;
        float scaleX, scaleY;
        NSScrollView *sv = [self enclosingScrollView];

        if(!sv || opMode == MiscPaletteMode)
            	return;
        docFrame = [self frame];
        contentFrame = [[sv contentView] frame];
        scaleX = contentFrame.size.width / (docFrame.size.width + 1);
        scaleY = contentFrame.size.height /(docFrame.size.height + 1);
        if(scaleX > scaleY)
            	scaleX = scaleY;
        else
            	scaleY = scaleX;
        [self _internalZoomToFactor:scaleX];
        return;
}

- (void)zoomToFactor:(float)aZoomFactor
{
        NSScrollView *sv = [self enclosingScrollView];        

        if(!sv || opMode == MiscPaletteMode)
            	return;
        [self _internalZoomToFactor:aZoomFactor];
        return;
}

- (void)zoomToRect:(NSRect)aZoomRect
{
    	return;
}

- (void)setDiagram:(MiscTreeDiagram *)diagram
{
	if(!diagram || treeDiagram == diagram)
		return;
	if(isOwnerOfDiagram){
		[treeDiagram release];
		treeDiagram = [diagram retain];
		[treeDiagram setView:self];
	} else 
		treeDiagram = diagram;
	[self _internalScrollRectAndRepaintAll:NSZeroRect];
}

- (BOOL)writeDiagramToFile:(NSString *)path
{
	BOOL result;
	
	result = [NSArchiver archiveRootObject:treeDiagram toFile:path];
	return result;
}

- (BOOL)readDiagramFromFile:(NSString *)path
{
	BOOL result = NO;
	MiscTreeDiagram *diagram;
	
	diagram = (MiscTreeDiagram *)[NSUnarchiver unarchiveObjectWithFile:path];
	if(diagram){
		[self setDiagram:diagram];
		result = YES;
	}
	return result;
}	

- (BOOL)writeDiagramToDGFile:(NSString *)path
{
	BOOL result;
	MiscDGWriter *writer = [MiscDGWriter dgWriter];
	
	result = [writer writeTreeDiagram:treeDiagram toDirectory:path];
	if(!result)
		NSLog([writer errorMessage]);
	return result;
}

- (void)drawRect:(NSRect)r
{  
	MiscDiagramTree *tree;
	int i,j;
	BOOL resized = NO;
	NSRect theRect;

	tree = [treeDiagram root];
	if(tree && [tree needsUpdateLayout]){		
		[tree layout];
		resized = [self _internalResizeAndPosTree:tree force:NO];
	}
	if(resized)
		theRect = [self bounds];
	else
		theRect = r;
	[bgColor set];
	NSRectFill(theRect);
        if(drawState.color)
            	[drawState.color release];
	drawState.color = [bgColor retain];
	if([[NSDPSContext currentContext] isDrawingToScreen]){
		PSrectviewclip(theRect.origin.x,theRect.origin.y,theRect.size.width,theRect.size.height);       
		PSsetgray(NSLightGray);
		PSsetlinewidth(0.15);
                if(drawState.color)
                                [drawState.color release];
		drawState.color = [[NSColor lightGrayColor] retain];
		drawState.linewidth = 0.15;
		for(i = 0;i <= canvasXTimes;i++)
			for(j = 0;j <= canvasYTimes;j++){
				[self _internalUpdateCanvasPos:i * NSWidth(canvasRect) :j * NSHeight(canvasRect)];
				[canvasPath send:dps_ustroke cached:YES];
			}
	}
	if(tree)
		[self _internalDrawTree:tree inRect:theRect];
	if([[NSDPSContext currentContext] isDrawingToScreen])
          	PSinitviewclip();	         	         	     			
}

- (void)mouseDown:(NSEvent *)event 
{
    	NSPoint p;
	MiscDiagramTree *selectedNode;
	int controlPointNumber;

   	p = [event locationInWindow]; 
    	p = [self convertPoint:p fromView:nil];
	if([treeDiagram root]){
		[hitPath moveTo:p];
		selectedNode = [treeDiagram selectNodeWithHitPath:hitPath controlPoint:&controlPointNumber];
		if(selectedNode && opFlags.editable){
			if([event clickCount] >= 2 && controlPointNumber == -1){
				[self _internalStartLabelEditingFor:selectedNode event:event :YES];
                            	return;
			} else if(controlPointNumber > -1){
				[self _internalResize:selectedNode event:event control:controlPointNumber];
                            	return;
			}
                }   
                if(selectedNode && opFlags.changeable &&
                   ([event modifierFlags] & NSControlKeyMask) == NSControlKeyMask && controlPointNumber == -1){
			[self _internalRearange:selectedNode event:event];
                    	return;
                }
                if(selectedNode && opFlags.dragSource)
			[self _internalDrag:selectedNode event:event];
	}		
}

- (void)keyDown:(NSEvent *)event 
{
	MiscDiagramTree *selectedNode;
	unichar theChar;
	int i;
    	NSString *evChars = [event characters];
    	int length = [evChars length];
	
	if(!(selectedNode = [treeDiagram selectedNode]))
		return;

	for (i = 0; i < length; i++) {
		theChar = [evChars characterAtIndex:i];
		switch (theChar) {
	    		case NSDeleteFunctionKey:
	    		case 127:
				if(opFlags.changeable)
					[self delete:self];
				return; // At this pt, all remaining keys will be useless.
				break;
	    
	   		case NSLeftArrowFunctionKey:
	    		case NSRightArrowFunctionKey:
	    		case NSUpArrowFunctionKey:
	    		case NSDownArrowFunctionKey:
				[self _internalKeyNavigate:selectedNode direction:theChar];
				break;
			case 0xd:
				if(opFlags.editable)
					[self _internalStartLabelEditingFor:selectedNode event:event :NO];
				return;
				break;
		}	
   	 }
}

- (void)setFrame:(NSRect)frameRect
{
    	[super setFrame:frameRect];
    	if(![self enclosingScrollView]){
		canvasRect = [self bounds];
            	[self _internalResizeAndPosTree:[treeDiagram root] force:NO];
        }        
}

- (void)setFrameSize:(NSSize)newSize
{
    	[super setFrameSize:newSize];
    	if(![self enclosingScrollView]){
            	canvasRect = [self bounds];
            	[self _internalResizeAndPosTree:[treeDiagram root] force:NO];
        }        
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}

- (int)canvasX
{
	return canvasXTimes;
}

- (int)canvasY
{
	return canvasYTimes;
} 

- (BOOL)knowsPagesFirst:(int *)firstPageNum last:(int *)lastPageNum
{
	*lastPageNum = canvasXTimes * canvasYTimes;
	return YES;
}

- (NSRect)rectForPage:(int)page
{
    	NSRect theRect;
    	int row, col;
	
	if(page < 1 || page > canvasXTimes * canvasYTimes)
		return NSZeroRect;
	row = 1;
	while(row * canvasXTimes < page)
		row++;	
	col = page - (row - 1) * canvasXTimes;
	theRect.origin.x = (col - 1) * NSWidth(canvasRect);
	theRect.origin.y = (row - 1) * NSHeight(canvasRect);
	theRect.size.width = NSWidth(canvasRect);
	theRect.size.height = NSHeight(canvasRect);
	return theRect;
}

- (void)setEditable:(BOOL)aBool
{
	opFlags.editable = aBool;
}

- (BOOL)isEditable
{
	return opFlags.editable;
}

- (void)setChangeable:(BOOL)aBool
{
	opFlags.changeable = aBool;
}

- (BOOL)isChangeable
{
	return opFlags.changeable;
}

- (void)setSelectable:(BOOL)aBool
{
	opFlags.selectable = aBool;
}

- (BOOL)isSelectable
{
	return opFlags.selectable;
}

- (void)setDragSource:(BOOL)aBool
{
	opFlags.dragSource = aBool;
}

- (BOOL)isDragSource
{
	return opFlags.dragSource;
}

- (void)setDragDestination:(BOOL)aBool
{
	opFlags.dragDest = aBool;
}

- (BOOL)isDragDestination
{
	return opFlags.dragDest;
}

- (MiscTreeViewOperationMode)operationMode
{
	return opMode;
}

- (void)setOperationMode:(MiscTreeViewOperationMode)aMode
{
	opMode = aMode;
	switch(opMode){
		case MiscEditMode:
			opFlags.selectable = YES;
			opFlags.editable = YES;
			opFlags.changeable = YES;
			opFlags.dragSource = NO;
			opFlags.dragDest = YES;
			break;
		case MiscPresentationMode:
			opFlags.selectable = YES;
			opFlags.editable = NO;
			opFlags.changeable = NO;
			opFlags.dragSource = NO;
			opFlags.dragDest = NO;
			break;
		case MiscPaletteMode:
			opFlags.selectable = YES;
			opFlags.editable = YES;
			opFlags.changeable = NO;
			opFlags.dragSource = YES;
			opFlags.dragDest = NO;
			[self _internalSetupPaletteMode];
			break;
	}
}


- (NSColor *)backgroundColor
{
	return bgColor;
}

- (void)setBackgroundColor:(NSColor *)aColor
{
	NSParameterAssert(aColor);
	if([aColor isEqual:bgColor])
		return;
	[bgColor release];
	bgColor = [aColor copy];
	[self setNeedsDisplay:YES];
}


@end

