/*
 *  YapApp.m
 *  Author: Ali Ozer
 *  Created: Mar 89 for 0.9
 *  Modified: Jul & Aug 89 for 1.0
 *  Modified: Aug 90 for 2.0
 *  Modified: Apr & Jun 91 and Jun 92 for 3.0. Localized Jan 92.
 *
 *  YapApp is the application class used in Yap. It implements the
 *  central functionality of coordinating the output and document
 *  windows, opening documents, managing shared panels, etc...
 *
 *  You may freely copy, distribute and reuse the code in this example.
 *  NeXT disclaims any warranty of any kind, expressed or implied,
 *  as to its fitness for any particular use.
 */

#import <appkit/appkit.h>
#import <objc/NXBundle.h>
#import <objc/zone.h>
#import <sys/param.h>
#import "YapApp.h"
#import "YapDocument.h"
#import "YapOutput.h"

#define CANTOPENFILE_STRING NXLocalString("Could not open file.", NULL, "The user-specified file could not be opened")
#define OK_STRING NXLocalString("OK", NULL, "Default response in alert panel")
#define UNSAVEDDOCS_STRING NXLocalString("You have unsaved documents.", NULL, "Message given to user when he tries to quit the application without saving all of his documents.")
#define REVIEW_STRING NXLocalString("Review Unsaved", NULL, "Choice (on a button) given to user which allows him/her to review all unsaved documents if he/she quits the application without saving them all first.")
#define QUITANYWAY_STRING NXLocalString("Quit Anyway", NULL, "Choice (on a button) given to user which allows him/her to quit the application even though there are unsaved documents.")
#define QUIT_STRING NXLocalString("Quit", NULL, "The operation of exiting the application.")
#define CANCEL_STRING NXLocalString ("Cancel", NULL, "Button choice allowing user to cancel quit")


@implementation YapApp

- outputView   
{
    return outputView;
}

- outputWindow
{
    return [outputView window];
}

#define DEFAULTWIDTH 612
#define DEFAULTHEIGHT 792
#define MINSIZE 72
#define MAXSIZE 3600

/*
 * Here we have a handle to the output window, created in IB. We create
 * the scroll and the yap output views and add them to this window.
 */
- setOutputWindow:anObject 
{
    NXRect initFrame = {{0.0, 0.0}, {DEFAULTWIDTH, DEFAULTHEIGHT}};

    id scrollView = [ScrollView new];

    outputView = [[YapOutput allocFromZone:[self zone]] initFrame:&initFrame];

    [anObject setBackgroundGray:NX_WHITE];
    [anObject removeFromEventMask:NX_KEYDOWNMASK|NX_KEYUPMASK];
    
    [scrollView setBorderType:NX_NOBORDER];
    [scrollView setHorizScrollerRequired:YES];
    [scrollView setVertScrollerRequired:YES];
 
    [anObject setContentView:scrollView];
    [scrollView setDocView:outputView];

    [anObject setDelegate:self]; // So we can get windowWillResize:toSize:
    [anObject setFrameAutosaveName:"Output Window"];
    [anObject makeKeyAndOrderFront:nil];

    [NXApp updateOutputWindowSize];

    return self;
}

/*
 * updateOutputWindowSize should be called after the size of the output view is
 * changed. It simply makes sure the window isn't too big for the view. If the
 * window is indeed to big, it is resized smaller.
 */
- updateOutputWindowSize
{
    NXRect frame, content;
    NXSize maxWindowSize;

    // The next few lines allow us to get the window size for the window
    // containing a ScrollView and the yap output view.

    [[self outputView] getFrame:&frame];
    [ScrollView getFrameSize:&maxWindowSize forContentSize:&(frame.size)
	        horizScroller:YES vertScroller:YES borderType:NX_NOBORDER];

    // sizeWindow:: wants window size in content area; so we can use the
    // maxWindowSize from above. But to compare it to the window frame,
    // we first need to get the content area for the current frame.

    [[self outputWindow] getFrame:&frame];      
    [Window getContentRect:&content forFrameRect:&frame 
	    style:[[self outputWindow] style]];

    if (content.size.width > maxWindowSize.width ||
	content.size.height > maxWindowSize.height) { 
	[[self outputWindow] 
		sizeWindow:MIN(maxWindowSize.width, content.size.width)
			  :MIN(maxWindowSize.height, content.size.height)];
    }

    // Now we go from the content size to the window frame size, which is 
    // what we will use in windowWillResize:toSize:

    content.size = maxWindowSize;
    [Window getFrameRect:&frame forContentRect:&content
	    style:[[self outputWindow] style]];

    maxWindowSize = frame.size;
    [[self outputWindow] setMaxSize:&maxWindowSize];

    return self;
}

/*
 * newDocument simply creates a new Yap document and displays it.
 */
- newDocument:sender
{
    [YapDocument new];

    return self;
}

/*
 * openDocument gets a file name from the user, creates a new document window,
 * and loads the specified file into it.
 */
- openDocument:sender
{
    // Allow ps, eps, and any other extension not handled by other apps.
    // Note that "" should come first in the list.
    static const char *const yapTypes[] = {"", "ps", "eps", NULL};  

    if ([[OpenPanel new] runModalForTypes:yapTypes]) {
	if ([YapDocument newFromFile:[[OpenPanel new] filename]] == nil) {
	    NXRunAlertPanel (NULL, CANTOPENFILE_STRING, OK_STRING, NULL, NULL);
	}
    }

    return self;
}

/*
 * app:openFile:type: is invoked by Workspace when the user double-clicks
 * on a file Yap is prepared to accept. By default, Yap is not prepared to open
 * any files, however, it can easily be made to open files of certain type
 * through Project Builder.
 */
- (int)app:app openFile:(const char *)path type:(const char *)type
{
    if ([YapDocument newFromFile:path] == nil) return NO;
    else return YES;
}

/*
 * The following method indicates that Yap is ready to open multiple
 * files at one time.
 */
- (BOOL)appAcceptsAnotherFile:sender
{
    return YES;
}

/*
 * Methods to load .nib files for the various panels.
 */
- showInfo:sender
{
    if (!infoPanel) {
        if (![NXApp loadNibSection:"Info.nib" owner:self withNames:NO fromZone:[self zone]]) {
	    NXLogError ("Can't find Info.nib!");	
	}
    }
    [infoPanel makeKeyAndOrderFront:sender];
    return self;
}

- showHelp:sender
{
    if (!helpPanel) {
        if (![NXApp loadNibSection:"Help.nib" owner:self withNames:NO fromZone:[self zone]]) {
	    NXLogError ("Can't find Help.nib!");	
	}
    }
    [helpPanel makeKeyAndOrderFront:sender];
    return self;
}

- showPrefs:sender
{
    if (!prefsPanel) {
        if (![NXApp loadNibSection:"Prefs.nib" owner:self withNames:NO fromZone:[self zone]]) {
	    NXLogError ("Can't find Prefs.nib!");	
	}
	[self updatePreferencesPanel:sender];
    }
    [prefsPanel makeKeyAndOrderFront:sender];
    return self;
}

/*
 * updatePreferencesPanel: is used to copy the existing situation into
 * the Prefences panel.
 */
- updatePreferencesPanel:sender
{
    NXRect outputFrame;

    [[self outputView] getFrame:&outputFrame];
    [outputWidthField setFloatValue:NX_WIDTH(&outputFrame)];
    [outputHeightField setFloatValue:NX_HEIGHT(&outputFrame)];
    [showCacheButton setState:[[self outputView] isCacheShown]];
    [clearCacheButton setState:[[self outputView] isCacheCleared]];

    [outputWidthField selectText:sender];

    return self;
}

/*
 * okPreferencesPanel: causes the values in Preferences to be read into the
 * application and applied to the various objects.
 */
- okPreferencesPanel:sender
{
    NXCoord desiredWidth, desiredHeight;

    desiredWidth = [outputWidthField floatValue];
    if (desiredWidth < MINSIZE || desiredWidth > MAXSIZE) {
	desiredWidth = MIN(MAX(desiredWidth, MINSIZE), MAXSIZE);
	[outputWidthField setFloatValue:desiredWidth];
    }
    desiredHeight = [outputHeightField floatValue];
    if (desiredHeight < MINSIZE || desiredHeight > MAXSIZE) {
	desiredHeight = MIN(MAX(desiredHeight, MINSIZE), MAXSIZE);
	[outputHeightField setFloatValue:desiredHeight];
    }

    [[self outputView] sizeTo:desiredWidth :desiredHeight];
    [self updateOutputWindowSize];

    [[self outputView] setCacheShown:[showCacheButton state]];
    [[self outputView] setCacheCleared:[clearCacheButton state]];

    [[sender window] orderOut:sender];
    [outputWidthField selectText:sender];

    return self;
}

/*
 * Make sure all documents are saved before actually terminating the app...
 */
- terminate:sender
{
    int count = [[self windowList] count];
    BOOL needsSaving = NO;

    // Determine if there are any unsaved documents...

    while (!needsSaving && count--) {
	id document = [YapDocument documentForWindow:[windowList objectAt:count]];
	if (document && [document needsSaving]) {
	    needsSaving = YES;
	}
    }

    if (needsSaving) {
	int choice = NXRunAlertPanel(QUIT_STRING, UNSAVEDDOCS_STRING, REVIEW_STRING, QUITANYWAY_STRING, CANCEL_STRING);
	if (choice == NX_ALERTOTHER)  {
	    return self;				// Cancel
	} else if (choice == NX_ALERTDEFAULT) {
	    count = [[self windowList] count];
	    while (count--) {
		id document = [YapDocument documentForWindow:[windowList objectAt:count]];
		if (document) {
		    if (![document closeDocument:QUIT_STRING andWindow:YES]) {
			return self;			// Cancel
		    }			
		}
	    }
	}
    }

    return [super terminate:sender];
}

- yapSelection:pasteboard userData:(const char *)userData error:(char **)msg
{
	const NXAtom *types;
	char 		*posting;
	int			postingLength;
	char buf[MAXPATHLEN+1];
	NXStream	*ts;
	id	filterboard;
	
	/* get the message */
		filterboard = [Pasteboard newByFilteringTypesInPasteboard:pasteboard];
		[filterboard types];
		
		if ([filterboard readType:NXAsciiPboardType data:&posting length:&postingLength]) 
			{
			ts = NXOpenMemory(NULL, 0, NX_READWRITE);
			NXWrite(ts, posting, postingLength);
			NXSeek(ts,0,NX_FROMSTART);
			[YapDocument newFromStream:ts name:NULL];
			//NXCloseMemory(ts, NX_FREEBUFFER);
			[filterboard deallocatePasteboardData:posting length:postingLength];
			
			}
		else		
		{*msg = "Could not read selection"; return nil;}
		[filterboard free];
		
		return self;
		

};

- appDidInit:sender
{
	[[NXApp appListener] setServicesDelegate:self];
	return self;
}

@end

