This is Document.m in view mode; [Download] [Up]
/* Document.m * Purpose: Initializes, loads, archives and frees a single * document. The template for the document is located in * Document.nib, which is loaded by this class each time * a new document is created. The windows are tiled using * the newLocation() function. * * 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. * * Written by: R. Dunbar Poor * Created: 28/April/1991 * */ #import "Document.h" #import "DocController.h" #import <appkit/Application.h> #import <appkit/Cell.h> #import <appkit/Panel.h> /* for NX_ALERTxxx */ #import <appkit/SavePanel.h> #import <appkit/Window.h> /* for setTitleAsFilename */ #import <objc/hashtable.h> /* for NXCopyStringBufferFromZone */ #import <sys/param.h> #import <strings.h> /* for strncpy() */ #import <stdlib.h> @interface Document(DocumentPrivate) - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain; - _write:(const char *)filename; - _read:(const char *)filename; @end static void newLocation(NXPoint *p) /* * This method computes a new location for each new window created. */ { static count = 0; p->x += (20.0 * count); p->y -= (25.0 * count); count = (count > 10)? 0 : count+1; } @implementation Document - init /* * The default initialization is simply to open a new (empty) document. */ { return [self initFromFile:NULL]; } - initFromFile:(const char *)filename /* * Read a document and initialize it from a file. */ { [super init]; [NXApp loadNibSection:"Document.nib" owner:self withNames:NO]; [self loadFromFile:filename]; if (!filename) { NXRect theFrame; /* If its a new document, generate a new position for it */ [docWindow getFrame:&theFrame]; newLocation(&theFrame.origin); [docWindow moveTo:theFrame.origin.x :theFrame.origin.y]; } [docWindow makeKeyAndOrderFront:self]; /* * I'm not sure why windowDidBecomeMain isn't called as a result of * makeKeyAndOrderFront. At any rate, we want new windows to become * the "active" document. */ [self windowDidBecomeMain:self]; return self; } - loadFromFile:(const char *)filename /* * Come here with all the nib objects instantiated. We initialize any * application-specific state from the contents from the given filename, * and finish up any initialization. */ { if (filename) { if (![self _read:filename]) { [self free]; /* couldn't load document file */ return nil; } } /* * do some common setup. */ [self setDocumentName:filename]; [self setDocEdited:NO]; return self; } - free { /* tell the controller that we can no longer be the active doucment */ [[NXApp delegate] unsetActiveDocument:self]; if (name) free(name); [docWindow free]; return [super free]; } - (const char *)message { return [docContents stringValue]; } - activateDocument:sender { [statusField setStringValue:"Active..."]; return self; } - deactivateDocument:sender { [statusField setStringValue:"Inactive..."]; return self; } - hideDocument:sender { [docWindow orderOut:sender]; return self; } - setDocEdited:(BOOL)edited { [docWindow setDocEdited:edited]; /* * The following is a hack to tell the controller to update the * Revert menu cell. Unfortunately, it does lots of work besides * that... */ if ([[NXApp delegate] activeDocument] == self) { [[NXApp delegate] setActiveDocument:self]; } return self; } - (BOOL)isDocEdited { return [docWindow isDocEdited]; } - setDocumentName:(const char *)newName { /* * If we are passing 'name' itself as the newName argument, as will happen * in the revert: method, we don't want to modify it. In particular, we * better not call free() on it and then try to copy it back to itself (which * was a bug I had for a while.) */ if (newName != name) { /* name isn't the same as newName, so it is safe to free name now */ if (name) free(name); if (newName) { name = NXCopyStringBuffer(newName); } else { name = NULL; } } if (name) { [docWindow setTitleAsFilename:name]; } else { [docWindow setTitleAsFilename:"Untitled Document"]; } return self; } - save:sender { if ([self isDocEdited] || !name) { [self _saveWithNewName:NO retainNewName:YES]; } return self; } - saveAs:sender { return [self _saveWithNewName:YES retainNewName:YES]; } - saveTo:sender { return [self _saveWithNewName:YES retainNewName:NO]; } - revert:sender { int choice; if ([self isDocEdited]) { choice = NXRunAlertPanel( "Revert", "Discard changes to the document?", "Revert", "Cancel", NULL); switch (choice) { case NX_ALERTDEFAULT: [self loadFromFile:name]; break; case NX_ALERTOTHER: return nil; } } return self; } - close:sender { [docWindow performClose:self]; return sender; } - dirty:sender { [self setDocEdited:YES]; return [self windowDidBecomeMain:self]; } - checkForEdited:sender /* * If the document is edited, give the user a chance to save the * document. Returns nil if they want to cancel. */ { int choice; if ([self isDocEdited]) { [docWindow makeKeyAndOrderFront:self]; choice = NXRunAlertPanel( "Close", "Save changes to %s?", "Save", /* NX_ALERTDEFAULT */ "Don't Save", /* NX_ALERTALTERNATE */ "Cancel", /* NX_ALERTOTHER */ (name)?name:"Untitled Document"); switch (choice) { case NX_ALERTALTERNATE: break; case NX_ALERTDEFAULT: [self save:nil]; break; case NX_ALERTOTHER: return nil; } } return self; } - windowDidBecomeMain:sender { [[NXApp delegate] setActiveDocument:self]; return [self activateDocument:sender]; } - windowDidResignMain:sender { return [self deactivateDocument:sender]; } - windowWillClose:sender { if (![self checkForEdited:sender]) return nil; /* make the document disavow any knowledge of the window */ [docWindow setDelegate:nil]; docWindow = nil; /* The document can't live now that its window is gone... */ [self free]; return sender; } /* * All varieties of save go through this routine. It covers all the cases * of running the Save Panel and retaining the name chosen. */ - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain { id savePanel; const char *saveName; /* filename to save into */ if (!name || doNewName) { /* saveAs or saveTo */ savePanel = [SavePanel new]; [savePanel setRequiredFileType:DOCUMENT_TYPE]; if ([savePanel runModalForDirectory:NULL file:name]) { saveName = [savePanel filename]; } else { /* aborted out? */ return self; } } else { /* ordinary Save */ saveName = name; } [self _write:saveName]; /* update the document name if requested */ if (doRetain) { [self setDocumentName:saveName]; [docWindow setDocEdited:NO]; } return self; } - _write:(const char *)filename { NXTypedStream *ts; NXRect theFrame; const char *contents; ts = NXOpenTypedStreamForFile(filename, NX_WRITEONLY); [docWindow getFrame:&theFrame]; NXWriteRect(ts, &theFrame); contents = [docContents stringValue]; NXWriteType(ts, "*", &contents); NXCloseTypedStream(ts); return self; } - _read:(const char *)filename { NXTypedStream *ts; NXRect theFrame; char *contents; ts = NXOpenTypedStreamForFile(filename, NX_READONLY); NXReadRect(ts, &theFrame); [docWindow placeWindowAndDisplay:&theFrame]; NXReadType(ts, "*", &contents); [docContents setStringValue:contents]; NXCloseTypedStream(ts); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.