This is DocControl.m in view mode; [Download] [Up]
// DocControl.m // // Free software created 1 Feb 1992 // by Paul Burchard <burchard@math.utah.edu>. #import "DocControl.h" #import "Doc.h" #import <appkit/appkit.h> #import <sys/file.h> #import <sys/param.h> /* for MAXPATHLEN */ #import <string.h> @implementation DocControl - (int)computeTypeIndexOf:(const char *)fileName inList:(const char **)theTypes { int hNum; const char **aType, *thisType; if(!fileName || !theTypes) return (-1); if(!(thisType = strrchr(fileName, '/'))) thisType = fileName; if(thisType = strrchr(thisType, '.')) thisType++; else thisType = ""; for(hNum=0, aType=theTypes; *aType; hNum++, aType++) if(0 == strcmp(*aType, thisType)) break; if(!*aType) return (-1); return hNum; } - (int)getTypeAndOpenPath:(char *)fileBuf defaultFolder:(const char *)theFolder types:(const char **)theTypes { int hNum; const char *const *files; static id openPanel = nil; if(!fileBuf || !theTypes) return (-1); if(!openPanel) openPanel = [OpenPanel new]; [NXApp setAutoupdate:NO]; if(theFolder && *theFolder) { if(![openPanel runModalForDirectory:theFolder file:NULL types:theTypes]) { [NXApp setAutoupdate:YES]; return (-1); } } else { if(![openPanel runModalForTypes:theTypes]) { [NXApp setAutoupdate:YES]; return (-1); } } files = [openPanel filenames]; if(!files || !files[0]) { [NXApp setAutoupdate:YES]; return (-1); } strcpy(fileBuf, files[0]); [NXApp setAutoupdate:YES]; if((hNum=[self computeTypeIndexOf:fileBuf inList:theTypes]) < 0) return (-1); return hNum; } // defaultPath: overrides defaultFolder: arg - (BOOL)getSavePath:(char *)fileBuf defaultFolder:(const char *)theFolder defaultPath:(const char *)origName type:(const char *)theType { static id savePanel = nil; BOOL ok, as; char dirName[MAXPATHLEN+1], fileName[MAXPATHLEN+1]; if(!savePanel) { savePanel = [SavePanel new]; [savePanel setDirectory:[[self class] defaultFolder]]; } if(theType && *theType) [savePanel setRequiredFileType:theType]; as = NO; if(origName && *origName) { char *p; as = YES; strcpy(dirName, origName); if(!(p=strrchr(dirName, '/'))) as = NO; else if(p!=dirName) *p = 0; if(p = strrchr(origName, '/')) strcpy(fileName, p+1); else strcpy(fileName, origName); if(!fileName[0]) as = NO; } if(!as && theFolder && *theFolder) [savePanel setDirectory:theFolder]; [NXApp setAutoupdate:NO]; if(as) ok = [savePanel runModalForDirectory:dirName file:fileName]; else ok = [savePanel runModal]; if(!ok) { [NXApp setAutoupdate:YES]; return NO; } strcpy(fileBuf,[savePanel filename]); [NXApp setAutoupdate:YES]; return YES; } - init { [super init]; convertWindowToDoc = [[HashTable alloc] initKeyDesc:"@" valueDesc:"@"]; return self; } - setDocHandlers:HandlerList { int i, n; if((n=[HandlerList count]) <= 0) return nil; if(fileTypes) NX_FREE(fileTypes); NX_MALLOC(fileTypes, const char *, n+1); if(!fileTypes) return nil; for(i=0; i<n; i++) fileTypes[i] = [[HandlerList objectAt:i] fileType]; fileTypes[i] = 0; [DocHandlers free]; DocHandlers = HandlerList; return self; } + (const char *)defaultFolder { return NXHomeDirectory(); } - appDidInit:sender { if(launchWithCreateDoc) [self createDoc:self]; return self; } - free { [convertWindowToDoc free]; if(fileTypes) NX_FREE(fileTypes); [DocHandlers free]; return [super free]; } - (BOOL)appAcceptsAnotherFile:sender { return YES; } - (int)app:sender openFile:(const char *)fileName type:(const char *)aType { int hNum, nHandlers = [DocHandlers count]; if(!fileName || !aType || [DocHandlers count]<=0) return NO; launchWithCreateDoc = NO; for(hNum=0; hNum<nHandlers; hNum++) if(0 == strcmp(aType, [[DocHandlers objectAt:hNum] fileType])) break; if(hNum >= nHandlers) return NO; if([self openForHandlerAt:hNum name:fileName]) return YES; else return NO; } - setMainDoc:aDoc { [[aDoc window] makeKeyAndOrderFront:nil]; return self; } - mainDoc { if(![NXApp mainWindow]) return nil; return (id)[convertWindowToDoc valueForKey:(void *)[NXApp mainWindow]]; } - stringTable { return stringTable; } - createDocForHandlerAt:(int)hNum { int useNum; id theDoc; if([DocHandlers count] <= 0) return nil; if(hNum<0 || hNum>=[DocHandlers count]) useNum = 0; else useNum = hNum; theDoc = [[[DocHandlers objectAt:useNum] alloc] init]; if(!theDoc || ![theDoc window]) { [theDoc free]; return nil; } [self setMainDoc:theDoc]; [convertWindowToDoc insertKey:(void *)[theDoc window] value:(void *)theDoc]; [[theDoc window] setDocEdited:NO]; return theDoc; } - createDoc:sender { return [self createDocForHandlerAt:(-1)]; } - createDoc1:sender { return [self createDocForHandlerAt:0]; } - createDoc2:sender { return [self createDocForHandlerAt:1]; } - createDoc3:sender { return [self createDocForHandlerAt:2]; } - handlerForFile:(const char *)fileName { int hNum; if(!fileName || [DocHandlers count]<=0 || !fileTypes) return nil; hNum = [self computeTypeIndexOf:fileName inList:fileTypes]; if(hNum<0 || hNum>=[DocHandlers count]) return nil; return [DocHandlers objectAt:hNum]; } - openForHandlerAt:(int)hNum name:(const char *)fileName { int gotNum = (-1); id theDoc, TheHandler; char fileBuf[MAXPATHLEN+1]; const char *oneType[2] = {0, 0}; if([DocHandlers count]<=0 || !fileTypes) return nil; // Get Doc handler and file name (putting latter into fileBuf). if(fileName) { strcpy(fileBuf, fileName); if(hNum>=0 && hNum<[DocHandlers count]) gotNum = hNum; else gotNum = [self computeTypeIndexOf:fileName inList:fileTypes]; } else if(hNum>=0 && hNum<[DocHandlers count]) { oneType[0] = fileTypes[hNum]; gotNum = [self getTypeAndOpenPath:fileBuf defaultFolder:[[DocHandlers objectAt:hNum] defaultFolder] types:oneType]; } else gotNum = [self getTypeAndOpenPath:fileBuf defaultFolder:[[self class] defaultFolder] types:fileTypes]; if(gotNum<0 || gotNum>=[DocHandlers count]) return nil; TheHandler = [DocHandlers objectAt:gotNum]; // Check if this file is already open in some Doc; if so just pop up. if(theDoc = [TheHandler docForFileName:fileBuf]) { [self setMainDoc:theDoc]; return theDoc; } // Create new Doc for this file, load it in, and pop it up. theDoc = [[TheHandler alloc] init]; [theDoc setFileName:fileBuf]; if(!theDoc || ![theDoc window] || ![theDoc load:self]) { NXRunAlertPanel([stringTable valueForStringKey:"Open"], [stringTable valueForStringKey:"Cannot read %s!"], [stringTable valueForStringKey:"OK"], NULL, NULL, fileBuf); [theDoc free]; return nil; } [self setMainDoc:theDoc]; [convertWindowToDoc insertKey:(void *)[theDoc window] value:(void *)theDoc]; [[theDoc window] setDocEdited:NO]; return theDoc; } - open:sender { return [self openForHandlerAt:(-1) name:NULL]; } - open1:sender { return [self openForHandlerAt:0 name:NULL]; } - open2:sender { return [self openForHandlerAt:1 name:NULL]; } - open3:sender { return [self openForHandlerAt:2 name:NULL]; } - saveDoc:theDoc as:(BOOL)yn { id otherDoc; const char *fileName; char fileBuf[MAXPATHLEN+1]; // Get new file name if still untitled or "save as" is true. if(yn || ![theDoc fileName]) { if(![self getSavePath:fileBuf defaultFolder:[[theDoc class] defaultFolder] defaultPath:[theDoc fileName] type:[[theDoc class] fileType]]) return nil; // Make sure new file name is not already in use in this app. otherDoc = [[theDoc class] docForFileName:fileBuf]; if(otherDoc && otherDoc!=theDoc) { /*!!! int choice; choice = NXRunAlertPanel([stringTable valueForStringKey:"Save"], [stringTable valueForStringKey:"You already have %s open in another window.\nDestroy other window?"], [stringTable valueForStringKey:"Yes"], [stringTable valueForStringKey:"Cancel"], NULL, fileBuf); if(choice != NX_ALERTDEFAULT) return nil; !!!*/ [[otherDoc window] setDocEdited:NO]; [self closeDoc:otherDoc andFree:YES]; } [theDoc setFileName:fileBuf]; } if(!(fileName = [theDoc fileName])) return nil; // If backup option on, append "~" to the existing file. if([[theDoc class] backupOnSave] && access(fileName, F_OK)==0) { strcpy(fileBuf, fileName); strcat(fileBuf, "~"); rename(fileName, fileBuf); } // Write to file and alert user on error. if(![theDoc dump:self]) { NXRunAlertPanel([stringTable valueForStringKey:"Save"], [stringTable valueForStringKey:"Cannot write %s!"], [stringTable valueForStringKey:"OK"], NULL, NULL, fileName); return nil; } [[theDoc window] setDocEdited:NO]; return theDoc; } - save:sender { return [self saveDoc:[self mainDoc] as:NO]; } - saveAs:sender { return [self saveDoc:[self mainDoc] as:YES]; } - revertDocToSaved:theDoc { const char *fileName; // If still untitled or unedited no reversion is possible. if([theDoc window] && ![[theDoc window] isDocEdited]) return nil; if(!(fileName = [theDoc fileName])) return nil; // Ask user if reversion is really what was meant. if(NXRunAlertPanel([stringTable valueForStringKey:"Revert"], [stringTable valueForStringKey:"Revert to saved version of %s?"], [stringTable valueForStringKey:"Revert"], [stringTable valueForStringKey:"Cancel"], NULL, fileName) != NX_ALERTDEFAULT) return nil; // Re-read file from disk. if(![theDoc load:self]) { NXRunAlertPanel([stringTable valueForStringKey:"Revert"], [stringTable valueForStringKey:"Cannot read %s!"], [stringTable valueForStringKey:"OK"], NULL, NULL, fileName); return nil; } [[theDoc window] setDocEdited:NO]; return theDoc; } - revertToSaved:sender { return [self revertDocToSaved:[self mainDoc]]; } - closeDoc:theDoc andFree:(BOOL)yn { int choice; const char *fileName; // Untitled files OK---user may still want to save them. fileName = [theDoc fileName]; // Give user a chance to save if edited. if([[theDoc window] isDocEdited]) { choice = NXRunAlertPanel([stringTable valueForStringKey:"Close"], [stringTable valueForStringKey:"%s has been modified.\nSave it?"], [stringTable valueForStringKey:"Yes"], [stringTable valueForStringKey:"No"], [stringTable valueForStringKey:"Cancel"], fileName ? fileName : [stringTable valueForStringKey:"This file"]); switch (choice) { case NX_ALERTALTERNATE: break; case NX_ALERTOTHER: return nil; case NX_ALERTDEFAULT: default: [self saveDoc:theDoc as:(fileName ? NO : YES)]; break; } } [[theDoc window] setDocEdited:NO]; // Remove Doc from index. [convertWindowToDoc removeKey:(void *)[theDoc window]]; // Note that doc closes its window upon freeing. if(yn) [[theDoc window] close];//!!! [theDoc setFileName:NULL];//!!! //!!!if(yn) [theDoc free]; return self; } - close:sender { return [self closeDoc:[self mainDoc] andFree:YES]; } - appWillTerminate:sender { const void *windowKey; void *docValue; NXHashState state = [convertWindowToDoc initState]; // Give user a chance to review unsaved documents and cancel the Quit. while([convertWindowToDoc nextState:&state key:&windowKey value:&docValue]) if([(id)windowKey isDocEdited]) { [self setMainDoc:(id)docValue]; if(![self closeDoc:(id)docValue andFree:YES]) return nil; } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.