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.