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.