/*	MiscPSView.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 <AppKit/AppKit.h>
#import <AppKit/NSDPSServerContext.h>
#import "MiscPSView.h"
#import "MiscPSViewWrap.h"

static void switchContextsWithFocus(NSDPSContext *newContext);

@interface MiscPSView (PrivateMethods)

- (NSWindow *)_internalCache;
- (void)_internalFillCache;

@end

@implementation MiscPSView (PrivateMethods)

- (NSWindow *)_internalCache
{
    	if(!cache){
		cache = [[NSWindow allocWithZone:[self zone]] initWithContentRect:[self frame] 
					styleMask:NSBorderlessWindowMask 
					backing:NSBackingStoreRetained defer:NO];
		[cache setMenu:nil];
        	[cache setExcludedFromWindowsMenu:YES];
        	[cache setReleasedWhenClosed:NO];
		[cache setBackgroundColor:[NSColor whiteColor]];
       		[[cache contentView] allocateGState];
		[cache displayIfNeeded];
	}
    	return cache;
}

- (void)_internalFillCache
{
    	static NSDPSServerContext *miscPSContext = nil;	
    	NSDPSContext *curContext;
    	NSSize boundsSize = [self bounds].size;
    	NSString *startString = 
		[NSString stringWithFormat:@"/miscsave save def /miscwidth %f def /mischeight %f def /showpage {} def\n", 
			boundsSize.width, boundsSize.height];
    	NSString *endString = @"\nmiscsave restore\n";

    	if(miscPSContext == nil){
		NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
		NSString *hostName = [userDefaults stringForKey:@"NSHost"];
		NSString *serverName = [userDefaults stringForKey:@"NSPSName"];
        	NSTimeInterval timeout = [userDefaults floatForKey:@"NSNetTimeout"];
		miscPSContext = [[NSDPSServerContext allocWithZone:[self zone]] 
				initWithHostName:hostName ? hostName : @"" 
				serverName:serverName ? serverName : @"" 
				textProc:NULL 
				errorProc:NULL 
				timeout:(timeout == 0.0) ? 60.0 : timeout 
				secure:NO encapsulated:NO];
		if(miscPSContext == nil)
            		return;
    	}


    	[[[self _internalCache] contentView] lockFocus];
        NSEraseRect([self bounds]);
        if(!psData){
            	[[[self _internalCache] contentView] unlockFocus];
            	cacheValid = YES;
                return;
        }    

    	curContext = [NSDPSContext currentContext];

    	NS_DURING {
        	switchContextsWithFocus(miscPSContext);
        	[miscPSContext writePostScriptWithLanguageEncodingConversion:[startString dataUsingEncoding:NSASCIIStringEncoding]];
                [self writePSPreambleInContext:miscPSContext];
        	[miscPSContext writePostScriptWithLanguageEncodingConversion:psData];
                [self writePSPostambleInContext:miscPSContext];
        	[miscPSContext writePostScriptWithLanguageEncodingConversion:[endString dataUsingEncoding:NSASCIIStringEncoding]];
        	[miscPSContext wait];     
        	[NSDPSContext setCurrentContext:curContext];
    	} NS_HANDLER {
        	[NSDPSContext setCurrentContext:curContext]; 
        	[miscPSContext release];
       		miscPSContext = nil;
        	if(![[localException name] isEqualToString:DPSPostscriptErrorException]){
            		[localException raise];
        	} 
    	} NS_ENDHANDLER

    	[[[self _internalCache] contentView] unlockFocus];
    	cacheValid = YES;
}

@end

@implementation MiscPSView
/*"The MiscPSView class defines a view that will draw itself using user specified PostScript commands.

Unlike a regular view, a MiscPSView view maintains a PostScript command buffer whose contents are interpreted when the view draws itself.  This buffer can be modified dynamically.  In this way, MiscPSView provides a class for rendering variable PostScript images.  Commands in the buffer may reference the constants %miscwidth and %mischeight (defined in the PostScript context).  These constants represent the view's dimensions.

To allow for initialization before and clean-up after the drawing process, the MiscPSView class provides hooks for subclasses to overwrite:
#writePSPreambleIn: and #writePSPostambleIn:.

The MiscPSView class implements a cache for faster drawing. The postscript data has to be interpreted only when the cache is invalid."*/

- initWithFrame:(NSRect)frameRect
{
    	[super initWithFrame:frameRect];
    	cache = nil;
        psData = nil;
        cacheValid = NO;
        return self;
}

- (void)dealloc
{
        if(cache)
                [cache release];
        if(psData)
                [psData release];
        [super dealloc];
}

- (void)drawRect:(NSRect)rect 
{
    	if(!cacheValid)
            	[self _internalFillCache];
	PScomposite(rect.origin.x, rect.origin.y, rect.size.width, 
			rect.size.height, [[[self _internalCache] contentView] gState], 
			rect.origin.x, rect.origin.y, NSCompositeCopy);
}

- (void)setFrame:(NSRect)frameRect
{
    	[super setFrame:frameRect];
    	if(cache){
            	[cache setContentSize:frameRect.size];
            	cacheValid = NO;
        }        
}

- (void)setFrameSize:(NSSize)newSize
{
    	[super setFrameSize:newSize];
    	if(cache){
            	[cache setContentSize:newSize];
            	cacheValid = NO;
        }        
}

- (void)setPostScriptData:(NSData *)data
/*"Fills the Postscript command buffer of the receiver with the
contents of data."*/
{
    	if(!data || data == psData)
            	return;
    	[psData release];
        psData = [data copy];
        cacheValid = NO;
        [self setNeedsDisplay:YES];
}

- (NSData *)postscriptData
/*"Returns the contents of the Postscript command buffer of the receiver."*/
{
    	return psData;
}

- (void)writePSPreambleInContext:(NSDPSServerContext *)aContext
/*"Writes Postscript initialization in the context aContext.
The MiscPSView implementation of this method does nothing."*/
{
    	return;
}

- (void)writePSPostambleInContext:(NSDPSServerContext *)aContext
/*"Cleans up Postscript environment in the context aContext.
The MiscPSView implementation of this method does nothing."*/
{
    	return;
}    

@end

static void switchContextsWithFocus(NSDPSContext *newContext)
{
    	float c1x, c1y, c2x, c2y;
    	float winCTM[6];
    	int realWinNum;

    	miscPSWGetFocus(&c1x, &c1y, &c2x, &c2y, winCTM, &realWinNum);
    	[NSDPSContext setCurrentContext:newContext];
    	miscPSWReFocus(realWinNum, winCTM, c1x, c1y, c2x, c2y);
}
