ftp.nice.ch/pub/next/graphics/viewer/EnhancedYap.NIHS.bs.tar.gz#/EnhancedYap/Source/YapDocument.m

This is YapDocument.m in view mode; [Download] [Up]

/*
 *  YapDocument.m
 *  Author: Ali Ozer
 *  Created: Aug 28, 1988
 *  Modified for 0.8: Sep 1988
 *  Modified for 0.9 and revised: Feb & Mar 1989
 *  Modified for 1.0 and nibified: Jun & Jul 1989
 *  Modified for 2.0 and zonified: Aug 1990
 *  Modified: Jan 92 for 3.0. Localized.
 *
 *  YapDocument class implements the Yap documents --- for every open 
 *  window, we have another instance of the YapDocument class. Each instance
 *  loads itself into a separate zone.
 *
 *  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/error.h>
#import <libc.h>
#import <string.h>
#import <sys/file.h>

#import "YapDocument.h"
#import "PSText.h"
#import "YapApp.h"
#import "YapOutput.h"
#import "FindPanel.h"

#import <objc/NXBundle.h>
#import <objc/List.h>
#import <objc/zone.h>
#import <streams/streams.h>
#import <defaults/defaults.h>
#import <mach/mach.h>
#import <libc.h>
#import <string.h>

#define UNTITLED NXLocalString ("UNTITLED", NULL, "Name of default document")
#define CANTWRITEFILE_STRING NXLocalString ("Can't write file.", NULL, "Document could not be saved")
#define CLOSEWINDOW_STRING NXLocalString("Close", NULL, "Request to close window containing unsaved document from menu or close button.")
#define SAVECHANGES_STRING NXLocalString("%s has changes. Save them?", NULL, "Question asked of user when he/she tries to close a window containing an unsaved document.  The %s is the name of the document.")
#define SAVE_STRING NXLocalString("Save", NULL, "Button choice which allows the user to save the document.")
#define DONTSAVE_STRING NXLocalString("Don't Save", NULL, "Button choice which allows the user to abort the save of a document which is being closed.")
#define OK_STRING NXLocalString ("OK", NULL, "Default response in alert panel")
#define CANCEL_STRING NXLocalString ("Cancel", NULL, "Button choice allowing user to cancel the request to close a window")

#define XOFFSET 5.0 	// Offset of subsequent windows
#define YOFFSET -20.0
#define MAXSIZE 1.0e38	// Maximum size of a text object

@implementation YapDocument

/*
 * The next two methods allow us to cache/reuse zones.
 */
static id zoneList = nil;

+ (NXZone *)newZone
{
    if (!zoneList || ![zoneList count]) {
	return NXCreateZone(vm_page_size, vm_page_size, YES);
    } else {
	return (NXZone *)[zoneList removeLastObject];
    }
}

+ (void)reuseZone:(NXZone *)aZone
{
    if (!zoneList) zoneList = [List new];
    [zoneList addObject:(id)aZone];
}

/*
 * Return the document in the specified window.
 */
+ documentForWindow:window
{
    id del = [window delegate];
    return (del && [del isKindOf:[YapDocument class]]) ? del : nil;
}


/*
 * Create a new instance of YapDocument with the specified stream in the
 * buffer.  If the file cannot be opened, no document is created and nil
 * is returned.
 */
+ newFromStream:(NXStream *)stream name:(const char *)fileName
{
    id docWin;		/* Window belonging to this document. */
    id textObj;		/* The text object we put in the window. */
    NXRect textFrame;	/* The frame of the text object in our window */


    self = [[self allocFromZone:[self newZone]] init];
    
    if (![NXApp loadNibSection:"Document.nib" owner:self withNames:NO fromZone:[self zone]]) {
		NXLogError ("Can't find Document.nib!");	
		NXCloseMemory (stream, NX_FREEBUFFER);
		[self free];
		return nil;
		}

    /*
     * Loading the nib file above sets the document outlet to the
     * scrollview; so we can use this outlet to get at the window & such.
     */
    docWin = [document window];
    [[document docView] getFrame:&textFrame];
 
    /*
     * Put the window offset from the previous document window... If no
     * previous window exists, or the main window is undetermined, then
     */ 
    if ([NXApp mainWindow]) {
		NXRect winFrame, winLoc;
		[[NXApp mainWindow] getFrame:&winFrame];
		[[docWin class] getContentRect:&winLoc forFrameRect:&winFrame style:[docWin style]];
		[docWin moveTo:NX_X(&winLoc) + XOFFSET :NX_Y(&winLoc) + YOFFSET];
		}
	
    [self setName:UNTITLED];
    [docWin setDelegate:self];

	if (stream) {
		char *text;
		int len, maxLen;
		NXGetMemoryBuffer (stream, &text, &len, &maxLen);
		textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame text:text alignment:NX_LEFTALIGNED];
		if (fileName)
			[self setName:fileName];
			else
			{
			[self setName:UNTITLED];
			[[document window] setDocEdited:YES];
			};
		NXCloseMemory (stream, NX_FREEBUFFER);
		} else {
		textObj = [[PSText allocFromZone:[self zone]] initFrame:&textFrame];
		[self setName:UNTITLED];
    	}

    /*
     * Put this new text object in the window and free the IB-created one.
     */
    [[document setDocView:textObj] free];

    /*
     * Set various parameters.
     */
    [document setAutoresizeSubviews:YES];
    [textObj setVertResizable:YES];		// Grow down as you type
    [textObj setHorizResizable:NO];		// But not sideways 
    [textObj setAutosizing:NX_WIDTHSIZABLE];	// Size horizontally when resized
    [textObj setMonoFont:YES];
    [textObj setOpaque:YES];
    [textObj setMinSize:&textFrame.size];
    NX_WIDTH(&textFrame) = NX_HEIGHT(&textFrame) = MAXSIZE;
    [textObj setMaxSize:&textFrame.size];	// Can grow
    [textObj setSel:0:0];			// Set the selection
    [textObj setDelegate:self];
    [textObj sizeToFit];

    [docWin makeKeyAndOrderFront:self];

    [self initializePrintInfo];

    return self;
}



/*
 * Create a new instance of YapDocument with the specified file in the
 * buffer.  If the file cannot be opened, no document is created and nil
 * is returned.
 */
+ newFromFile:(const char *)fileName
{
    NXStream *stream = NULL;
    id docWin;		/* Window belonging to this document. */
    id textObj;		/* The text object we put in the window. */
    NXRect textFrame;	/* The frame of the text object in our window */

    if (fileName && !(stream = NXMapFile(fileName, NX_READONLY))) 
		{
		return nil;
    	}
	return [self newFromStream:stream name:fileName];
}

+ new
{
    return [self newFromFile:NULL];
}

- initializePrintInfo
{
    static BOOL printInfoInitialized = NO;
    if (!printInfoInitialized) {
	[[NXApp printInfo] setVertCentered:NO];
	[[NXApp printInfo] setHorizCentered:NO];
	[[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
	[[NXApp printInfo] setMarginLeft:36.0 right:36.0 top:72.0 bottom:72.0];
	printInfoInitialized = YES;
    }
    return self;
}

/*
 * Checks to see if the window has been edited...
 */
- (BOOL)needsSaving
{
    return [[document window] isDocEdited];
}

/*
 * Delegate method for the document Text object. We use this method
 * to detect when the text in the window is modified.
 */
- text:text isEmpty:(BOOL)empty
{
    if (![[document window] isDocEdited]) {
    	[[document window] setDocEdited:YES];
    }
    return NO;
}

/*
 * Delegate method for the document Text object. We use this method
 * to detect when the font is changed so we an write it out as the default.
 */
- textWillConvert:textObject fromFont:oldFont toFont:newFont
{
    if (newFont) {
        char str[80];
	sprintf (str, "%f", [newFont pointSize]);
	NXWriteDefault ([NXApp appName], "NXFontSize", str);
	NXWriteDefault ([NXApp appName], "NXFont", [newFont name]);
	[Text setDefaultFont:newFont];
    }

    return newFont;
}

/*
 * saveDocument: will write out the contents of the document
 * to the specified file. 
 * 
 * If fileName is NULL, asks user for a file name.
 * Returns NO if the user decides to cancel the operation.
 * Otherwise returns YES (whether or not the document could be saved),
 * modifying the needsSaving state.
 */
- (BOOL)saveDocument:(const char *)fileName
{
    int fd;     // File descriptor
    NXStream *stream = NULL;
    BOOL saveOK;

    if (!fileName || !strcmp (fileName, UNTITLED) || !strcmp (fileName, "")) {
	if (!(fileName = [[SavePanel new] runModalForDirectory:"." file:UNTITLED] ? [[SavePanel new] filename] : NULL)) {
	    return NO;
	}
    }

    if (saveOK = 
	    (((fd = open (fileName, O_WRONLY|O_CREAT|O_TRUNC, 0644)) != -1) &&
	    (stream = NXOpenFile (fd, NX_WRITEONLY))))
	[[document docView] writeText:stream];
    if (stream) NXClose (stream);
    if (fd != -1) close (fd);

    if (saveOK) {
	[self setName:fileName];
	[[document window] setDocEdited:NO];
    } else {
	NXRunAlertPanel (NULL, CANTWRITEFILE_STRING, OK_STRING, NULL, NULL);
    }
    return YES;
}

/*
 * Set/Get the name of the document. This is the same as the title.
 * Free the old name after the new one is set, because sometimes this
 * routine might be called as the old name as the argument...
 */
- setName:(const char *)documentName
{
    documentName = documentName ? documentName : "";
    if (documentName != name) {
	free(name);
	name = NXCopyStringBufferFromZone (documentName, [self zone]); 
	[[document window] setTitleAsFilename:name];
    }
    return self;
}

-(const char *)name
{
    return name;
}

/*
 * windowWillClose: gets called by windows who have this instance of 
 * YapDocument as delegate.  We call closeDocument:andWindow: to see if the
 * document needs saving and take the appropriate action if so. If the user
 * cancels the save, closeDocument:andWindow: returns NO and we return nil.
 * This prevents the window from closing...
 */
- windowWillClose:sender
{
    return [self closeDocument:CLOSEWINDOW_STRING andWindow:NO] ? self : nil;
}

/*
 * Closes the document. If document needs saving, asks user he/she'd like the doc
 * saved. Returns NO if the user cancels out of the save operation, otherwise returns YES.
 * flag determines if the window should also be closed with the document.
 */
- (BOOL)closeDocument:(const char *)message andWindow:(BOOL)flag
{
    if ([self needsSaving]) {
	int save = NXRunAlertPanel(message, SAVECHANGES_STRING, SAVE_STRING, DONTSAVE_STRING, CANCEL_STRING, [self name]);
	if (save == NX_ALERTOTHER) {	// Cancel
	    return NO;
	} else if (save == NX_ALERTDEFAULT) {
	    [self save:nil];
	}
    }
    [[document window] setDelegate:nil];
    if (flag) [[document window] close];
    [self free];
    return YES;
}

- free
{
    NXZone *docZone = [self zone];
    if (name) free(name);
    [super free];
    [YapDocument reuseZone:docZone];
    return nil;
}
    
/*
 * save: saves the current document. If the document is untitled, it 
 * puts up a savePanel to get the user to enter a file name. saveAs:
 * saves the document under a new name by putting up a savePanel.
 */
- save:sender 
{
    (void)[self saveDocument:[self name]];
    return self;
}
  
- saveAs:sender 
{
    if ([[SavePanel new] runModalForDirectory:"." file:[self name]]) {
    	(void)[self saveDocument:[[SavePanel new] filename]];
    }
    return self;
}

- execute:sender
{
    [[NXApp outputView] executeCodeFrom:[document docView]];

    return self;
}   

/*
 * To get around the problem of printPSCode: going up the responder chain and
 * causing print panel to come back after Cancel, we use the following glue.
 */
- print:sender
{
    [[document docView] printPSCode:sender];
    return self;
}





@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.