home *** CD-ROM | disk | FTP | other *** search
- /* 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/appkit.h>
- #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);
- if (!ts) return self;
- [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);
- if (!ts)
- return self;
- NXReadRect(ts, &theFrame);
- [docWindow placeWindowAndDisplay:&theFrame];
- NXReadType(ts, "*", &contents);
- [docContents setStringValue:contents];
- NXCloseTypedStream(ts);
-
- return self;
- }
-
- @end
-