ftp.nice.ch/pub/next/graphics/vector/Wood.0.72.s.tar.gz#/Wood/Sources/WoodApp.m

This is WoodApp.m in view mode; [Download] [Up]

#import <sys/dir.h>
#import <misckit/MiscString.h>
#import <misckit/MiscInfoController.h>

#import "wooddoc.h"
#import "PageMargin.h"
#import "WoodBundle.h"
#import "ooe_server.h"
#import "IPPanel.h"
#import "IPFlyPanel.h"

#define	PAGE_LAYOUTSTR		NXLocalizedString("Page_Layout",NULL,Page_Layout)
#define HELP_PANELSTR		NXLocalizedString("Help_Panel",NULL,Help_Panel)
#define NOSTR				NXLocalizedString("NO",NULL,NO)
#define YESSTR				NXLocalizedString("YES",NULL,YES)
#define LIBRARYSUBPATH      "Wood"
#define MODULEEXTENSION     "woodfilter"

static id docClass;	            		// Class of Document to use
static id zoneStorage;					// zone storage for zone reuse
static BOOL docClassConforms;			// YES if docClass conforms to protocol PDocument
static BOOL inMsgPrint = NO;			// YES if message print (Listener)
static const char *versionFormat;		// Version Format String
static const int classVersion  = 110;	// Version multiplied by 100

static void initMenu(id menu);
static id documentInWindow(id window);
static id findDocument(const char *name);
static id openFile(const char *directory, const char *name, BOOL display);
static BOOL fileNameHasExtension(const char *f, const char *e);

@implementation WoodApp:Application

+ initialize
{
    if (self == [WoodApp class]) {
		docClass = [WoodDoc class];
        docClassConforms = YES;  
		versionFormat = NXLocalizedString("Version %.2f",NULL,Version Format);
     	zoneStorage = [[Storage allocFromZone:[self zone]] initCount:0 elementSize:sizeof(NXZone *)
                 description:@encode(NXZone *)];
	}
	return self;
}

- setDocClass:aDocClass
{
    docClass = aDocClass;
    docClassConforms = YES;
    return self;
}

- docClass
{
    return docClass;
}

- inspectorManager
{
	return inspectorManager;
}

- (BOOL)docClassConforms
{
   	return docClassConforms;
}

- loadLocalNib:(const char *)nibName owner:aOwner
{
    id bundle;
	char buf[MAXPATHLEN + 1],*temp;
    NXZone *zone;

	bundle = [NXBundle bundleForClass:[aOwner class]];
    if([bundle getPath:buf 
        forResource:nibName 
        ofType:"nib"]){
				zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
				NXNameZone(zone, nibName);
       			if([self loadNibFile:buf 
            		owner:aOwner 
            		withNames:NO 
            		fromZone:zone]){
            				NXMergeZone(zone);
							return self;
        		} else {
                            NX_MALLOC(temp,char,strlen(nibName) + 51);
            				sprintf(temp,"%s %s\n",localString("Could not load requested resource"),nibName); 
		    				Notify(localString("Resource Error"),temp);
                            NX_FREE(temp);
            				return nil;
        		}
    } else {
        NX_MALLOC(temp,char,strlen(nibName) + 51);
		sprintf(temp,"%s %s\n",localString("Could not find requested resource"),nibName); 
		Notify(localString("Resource Error"),temp);
        NX_FREE(temp);
        return nil;
    }
}

- openFile:(const char *)path file:(const char *)type flag:(BOOL)flag
{
	return openFile(path,type,flag);
}

- currentDocument
{
    return documentInWindow([self mainWindow]);
}

- (const char *)currentDirectory
{
    const char *retval;
    WoodDoc *currentDocument;
   
    currentDocument = [self currentDocument];
    if([currentDocument directory])
    	retval = [currentDocument directory];
    else 
		retval = defaultDir;
    return retval;
}

- (const char *)docExtension
{
   	if(docClassConforms)
		return [docClass docExtension];
	else
		return NULL;
}
   	
- setDefaultDir:(const char *)dir
{
	sprintf(defaultDir,"%s",dir);
    return self;
}

- (BOOL)saveAll
{
    return saveAll;
}

- setSaveAll:(BOOL)value
{
    saveAll = value;
	return self;
}

- (BOOL)dumpAll
{
    return dumpAll;
}

- setDumpAll:(BOOL)value
{
    dumpAll = value;
	return self;
}

- saveAsPanel:sender
{
    id savepanel = [SavePanel new];

	[savepanel setAccessoryView:nil];
    if(docClassConforms)
	       [savepanel setRequiredFileType:[docClass docExtension]];
    return savepanel;
}

- pageLayout:sender
{
    return pageMargin;
}

- (NXZone *)newDocZone
{
    NXZone *dummy;

    if (![zoneStorage count]) {
		return NXCreateZone(vm_page_size, vm_page_size, YES);
    } else {
        dummy = *(NXZone **)[zoneStorage elementAt:[zoneStorage count] - 1];
        [zoneStorage removeLastElement];
		return dummy;
    }
}

- reuseDocZone:(NXZone *)aZone
{
	NXZone *dummy;

	dummy = aZone;
    [zoneStorage addElement:(void *)&dummy];
    NXNameZone(aZone, "Unused");
	return self;
}

- new:sender
{
    [[docClass allocFromZone:[self newDocZone]] init];
    return self;
}

- open:sender
{
    const char *directory;
    const char *const *files;
  	const char *const allowedType[2] = {[self docExtension], NULL};
    OpenPanel *openpanel = [[OpenPanel new] allowMultipleFiles:YES];

	directory = [self currentDirectory];
    if (directory && (*directory == '/')) 
		[openpanel setDirectory:directory];
    if ([openpanel runModalForTypes:allowedType]) {
		files = [openpanel filenames];
		directory = [openpanel directory];
		while (files && *files) {
			haveOpenedDocument = openFile(directory, *files, YES) || haveOpenedDocument;
			files++;
		}
    }
    return self;
}

- saveAll:sender
{
    int count;

	count = [docList count];
    if(docClassConforms)
    	while (count--) {
			[[docList objectAt:count] save:self];
    	}
    return self;
}

- print:sender
{
    if(docClassConforms)
    	return [[self currentDocument] print:sender];
    else
		return nil;
}

- samples:sender
{
	NXBundle *bundle;
	char buf[2048];
	
	bundle = [NXBundle mainBundle];
	strcpy(buf,[bundle directory]);
	strcat(buf,"/Samples");
	if(![[Application workspace] openFile:buf])
		NXBeep();
	return self;
}

- (BOOL)menuItemUpdate:menuCell
{
    SEL action;
    id responder, target;
    BOOL retval = NO;

    target = [menuCell target];
    if(!target){
		action = [menuCell action];
		responder = [NXApp calcTargetForAction:action];
		if([responder respondsTo:@selector(validateCommand:)]){
			retval = [responder validateCommand:menuCell];
		} else { 
			if(![menuCell isEnabled] && responder){
				[menuCell setEnabled:YES];
				retval = YES;
			} else if([menuCell isEnabled] && !responder){
				[menuCell setEnabled:NO];
				retval = YES;
			}
		}
    } else if(target == self) 
			retval = [self validateCommand:menuCell];
	return retval;
}

- (BOOL)validateCommand:menuCell
{
    SEL action = [menuCell action];
	BOOL redraw = NO, enabled = [menuCell isEnabled];
	
    if ((action == @selector(saveAll:)) || (action == @selector(print:))){
	 	if(findDocument(NULL) && !enabled){
			redraw = YES;
			[menuCell setEnabled:YES];
		} else if(!findDocument(NULL) && enabled){
			redraw = YES;
			[menuCell setEnabled:NO];
		}
	}
    return redraw;
}

- setupPageLayout:(float)lm :(float)rm :(float)tm :(float)bm 
{
	if (!pageMargin) {
		pageMargin = [PageMargin new];
		[pageMargin setPlpAccessory:plpAccessory];
		[pageMargin setFrameUsingName:PAGE_LAYOUTSTR];
	}
	[pageMargin setValues:lm right:rm top:tm bottom:bm];
	[pageMargin writePrintInfo];
	return self;
}

- appWillInit:sender
{
	char temp[MAXPATHLEN+1];
	float lm,rm,tm,bm;
	
	if (!NXGetDefaultValue(appName,localString("SaveAll"))) 
		sprintf(temp,NOSTR);
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("SaveAll")));
	if (!strcmp(temp,YESSTR)) 
		saveAll = YES;
	if (!NXGetDefaultValue(appName,localString("DumpAll"))) 
		sprintf(temp,NOSTR);
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("DumpAll")));
	if (!strcmp(temp,YESSTR)) 
		dumpAll = YES;
	if (!NXGetDefaultValue(appName,localString("LeftMargin"))) 
		sprintf(temp,"36.0");
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("LeftMargin")));
	sscanf(temp,"%f",&lm);
	if (!NXGetDefaultValue(appName,localString("RightMargin"))) 
		sprintf(temp,"36.0");
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("RightMargin")));
	sscanf(temp,"%f",&rm);
	if (!NXGetDefaultValue(appName,localString("TopMargin"))) 
		sprintf(temp,"36.0");
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("TopMargin")));
	sscanf(temp,"%f",&tm);
	if (!NXGetDefaultValue(appName,localString("BottomMargin"))) 
		sprintf(temp,"36.0");
	else 
		sprintf(temp,NXGetDefaultValue(appName,localString("BottomMargin")));
	sscanf(temp,"%f",&bm);
	[self setupPageLayout:lm :rm :tm :bm];
	[self setAutoupdate:YES];
	docList = [[List alloc] init];
	[self registerAsOOEServer];
    return self;
}

- appDidInit:sender
{
    int 		i;
    char 		buffer[MAXPATHLEN+1], temp[MAXPATHLEN+1];
    char 		*directory, *name, *ext;
	const char *def;
    int ooeLaunched = NO;
	id inspectorPanel;
	
	[self setDefaultDir:NXHomeDirectory()];
	bundleList = [[List allocFromZone:[self zone]] init];
  	[self createBundlesAndLoadModules:NO];	
    if (NXArgc > 1) {
		for (i = 1; i < NXArgc; i++) {
			strcpy(buffer, NXArgv[i]);
			ext = rindex(buffer, '.');
			if (!ext) {
				strcat(buffer,".");
				strcat(buffer, [self docExtension]);
			}
			if (*buffer == '/') {
				directory = "/";
				name = buffer;
				name++;
			} else {
				name = rindex(buffer, '/');
				if (name) {
					*name++ = '\0';
					sprintf(temp,"%s/%s",[(NXBundle *)[NXBundle mainBundle] directory],buffer);
					directory = temp;
				} else {
					name = buffer;
					directory = NULL;
				}
			}
			haveOpenedDocument = openFile(directory, name, YES) || haveOpenedDocument;
		}
    }
	def =  NXReadDefault([self appName], OOE_LAUNCH_DEFAULT);
    ooeLaunched = (def != NULL && atoi(def) == 1);
    NXWriteDefault([self appName], OOE_LAUNCH_DEFAULT, "0");
    if (!haveOpenedDocument && !ooeLaunched && !NXGetDefaultValue([self appName], "NXServiceLaunch")) 
		[self new:self];	
    if (NXGetDefaultValue(appName,localString("Quit"))) {
		[self activateSelf:YES];
		NXPing();
		[self terminate:self];
    }
    initMenu([self mainMenu]);
	inspectorPanel = [inspectorManager window];
	[inspectorPanel setFrameAutosaveName:"Wood Inspector Frame"];
	[inspectorPanel setVisibleAutosaveName:"Wood Inspector Visibility"];
	[inspectorPanel setFloatingAutosaveName:"Wood Inspector Floating"];
	[inspectorPanel setFrameUsingName:[inspectorPanel frameAutosaveName]];
	[inspectorPanel setFloatingUsingName:[inspectorPanel floatingAutosaveName]];
	[inspectorPanel setVisibleUsingName:[inspectorPanel visibleAutosaveName]];
	if([IPPanel isPanelSavedAsVisible:[inspectorPanel visibleAutosaveName]])
		[inspectorManager inspect:nil];
	[infoController setLicenseFile:[MiscString newWithString:"/Intro/License.rtfd"]];
	[infoController setReleaseNotesFile:[MiscString newWithString:"/Intro/ReleaseNotes.rtfd"]];
    return self;
}

- app:sender willShowHelpPanel:panel
{
    char path[MAXPATHLEN + 1];

	[panel setFrameUsingName:HELP_PANELSTR];
    sprintf(path, "%s/%s", [panel helpDirectory],"Intro/Intro.rtfd");
    [panel showFile:path atMarker:NULL];
	return self;
}

- appWillTerminate:sender
{
	float lm,rm,tm,bm;
	char temp[100];
    id window, document;
    int choice = 0, count = [docList count];
	id inspectorPanel;

	inspectorPanel = [inspectorManager window];
	[inspectorPanel saveFrameUsingName:[inspectorPanel frameAutosaveName]];
	[inspectorPanel saveFloatingUsingName:[inspectorPanel floatingAutosaveName]];
	[inspectorPanel saveVisibleUsingName:[inspectorPanel visibleAutosaveName]]; 
	[pageMargin saveFrameUsingName:PAGE_LAYOUTSTR];
	[pageMargin getValues:&lm right:&rm top:&tm bottom:&bm];
	sprintf(temp,"%f",lm);
	NXWriteDefault([self appName],localString("LeftMargin"),temp);
	sprintf(temp,"%f",rm);
	NXWriteDefault([self appName],localString("RightMargin"),temp);
	sprintf(temp,"%f",tm);
	NXWriteDefault([self appName],localString("TopMargin"),temp);
	sprintf(temp,"%f",bm);
	NXWriteDefault([self appName],localString("BottomMargin"),temp);
	if (saveAll) {
		while (count--) {
			document = [docList objectAt:count];
			if ([document respondsTo:@selector(needsSaving)] && [document needsSaving]) {
				[document save:self];
			}
		}
		NXWriteDefault(appName,localString("SaveAll"),YESSTR);
		NXWriteDefault(appName,localString("DumpAll"),NOSTR);
	} else if (dumpAll) {
		NXWriteDefault(appName,localString("DumpAll"),YESSTR);
		NXWriteDefault(appName,localString("SaveAll"),NOSTR);
	} else {
		NXWriteDefault(appName,localString("DumpAll"),NOSTR);
		NXWriteDefault(appName,localString("SaveAll"),NOSTR);
		while (count--) {
			document = [docList objectAt:count];
			if ([document respondsTo:@selector(needsSaving)] && [document needsSaving]) {
				choice = NXRunAlertPanel(localString("Quit"),
										 localString("You have unsaved documents."),
										 localString("Review Unsaved"),
										 localString("Quit Anyway"),
										 localString("Cancel"));
				if (choice == NX_ALERTOTHER) 
					return nil;
				else if (choice == NX_ALERTALTERNATE) 
					return self;
				else if (choice == NX_ALERTDEFAULT) {
					count = 0;
					choice = [docList count];
					while (choice--) {
						document = [docList objectAt:choice];
						window = [document window];
						if ([document respondsTo:@selector(windowWillClose:)]) {
							if ([document windowWillClose:self]) 
								[window close];
							else 
								return nil;
						}
					}
				}
	    	}
		}
    }
	[[bundleList freeObjects] free];
    return self;
}

- (int)appOpenFile:(const char *)path type:(const char *)type
{
    char *name;
    char directory[MAXPATHLEN+1];

    if (type && !strcmp(type, [self docExtension])) {
		strcpy(directory, path);
		name = rindex(directory, '/');
		if (name) {
			if (name != index(directory, '/')) {
				*name++ = '\0';
				if (openFile(directory, name, YES)) {
					haveOpenedDocument = YES;
					return YES;
				}
			} else {
				name++;
				if (openFile("/", name, YES)) {
					haveOpenedDocument = YES;
					return YES;
				}
			}
		}
    } else {
		sprintf(directory,localString("%s is not a file %s can open.  An attempt to filter appropriate data will be made."),path,appName);
		Notify(localString("File Open Warning"),directory);
		if ([[docClass allocFromZone:[self newDocZone]] initFromPasteboard:[Pasteboard newByFilteringFile:path]]) 
			return YES;
	}
    return NO;
}

- (BOOL)appAcceptsAnotherFile:sender
{
    return YES;
}

- (int)app:sender unmounting:(const char *)fullPath
{
	int	count		= [docList count];
    WoodDoc	*document	= nil;
	
    if(!docClassConforms)
		return 0;
    while (count--) {
		document = [docList objectAt:count];
		if (!strncmp([document directory],fullPath,strlen(fullPath))) {
			Notify(fullPath,localString("is being unmounted; documents here will close."));
			[[document window] performClose:self];
		}
		
    }
    return 1;
}		

- (int)msgDirectory:(const char **)fullPath ok:(int *)flag
{
    *fullPath = [self currentDirectory];
    if (*fullPath) *flag = YES;
    else {
		*fullPath = "";
		*flag = NO;
    }
    return 0;
}

- (int)msgFile:(const char **)fullPath ok:(int *)flag
{
    const char *file;
    
    if(!docClassConforms)
		return 0;
    file = [(WoodDoc *)[self currentDocument] fileName];
    if (file) {
		*fullPath = file;
		*flag = YES;
    } else {
		*fullPath = "";
		*flag = NO;
    }

    return 0;
}

- (int)msgPrint:(const char *)fullPath ok:(int *)flag
{
    BOOL close;
    id document = nil;
    char *directory, *name;
    char temp[MAXPATHLEN+1];
    char path[MAXPATHLEN+1];
    char buffer[MAXPATHLEN+1];

    inMsgPrint = YES;
    strcpy(buffer, fullPath);
    name = rindex(buffer, '/');
    if (name) {
		*name++ = '\0';
		directory = buffer;
    } else {
		name = buffer;
		getwd(temp);
		directory = temp;
    }
	strcpy(path,directory);
	strcat(path, "/");
	strcat(path, name);
	document = findDocument(path);
    if (document) 
		close = NO;
    else {
		document = openFile(directory, name, NO);
		if (document) haveOpenedDocument = YES;
		close = YES;
    }
    if (document && ![document isEmpty]) {
		[self setPrintInfo:[document printInfo]];
        if(docClassConforms)
			[document print:self];
		if (close) 
			[[document window] performClose:self];
		*flag = YES;
    } else 
		*flag = NO;
    inMsgPrint = NO;
    return 0;
}

- (int)msgVersion:(const char **)aString ok:(int *)flag
{
    char buf[20];

    sprintf(buf, versionFormat, classVersion);
    *aString = NXCopyStringBuffer(buf);
    *flag = YES;
    return 0;
}

- (int)msgQuit:(int *)flag
{
    *flag = ([self terminate:self] ? NO : YES);
    return 0;
}

- registerDoc:aDoc
{
	[docList addObject:aDoc];
	return self;
}

- freeDoc:aDoc
{
	if([docList count] == 1)
		[inspectorManager inspect:nil];
	[docList removeObject:aDoc];
	[aDoc free];
	return self;
}		

- docList
{
	return docList;
}

- createBundlesAndLoadModules:(BOOL)doLoad
{
  	char libraryDir[MAXPATHLEN + 1];
	int i;
	id cell, filter;
  
  	[bundleList freeObjects];
  	strcpy(libraryDir, NXHomeDirectory());
  	strcat(libraryDir, "/Library/");
  	strcat(libraryDir, LIBRARYSUBPATH);
  	[self createBundlesForDirectory:libraryDir loadModules:doLoad];
	strcpy(libraryDir, "/LocalLibrary/");
  	strcat(libraryDir, LIBRARYSUBPATH);
  	[self createBundlesForDirectory:libraryDir loadModules:doLoad];
  	strcpy(libraryDir, "/NextLibrary/");
  	strcat(libraryDir, LIBRARYSUBPATH);
  	[self createBundlesForDirectory:libraryDir loadModules:doLoad];
  	strcpy(libraryDir, [(NXBundle *)[NXBundle mainBundle] directory]);
  	[self createBundlesForDirectory:libraryDir loadModules:doLoad];
	[addMenu disableFlushWindow];
	for(i = 0; i < [bundleList count]; i++){
		filter = [bundleList objectAt:i];
		cell = [addMenu addItem:[filter filterName] action:@selector(addFromFilter:) keyEquivalent:0];
		[cell setTag:i];
		[cell setTarget:[self calcTargetForAction:@selector(addFromFilter:)]];
	}
	[addMenu reenableFlushWindow];
	[addMenu flushWindow];   
  	return self;
}

- loadFilter:sender
{
	const char *fileName;
	id openpanel = [OpenPanel new], newWoodBundle, cell;
	const char *const fileTypes[] = {MODULEEXTENSION, NULL};
	BOOL shouldAdd;
	int i, howMany;
	char buf[MAXPATHLEN + 1], *tailOne, *tailTwo;

	[openpanel allowMultipleFiles:NO];
	if([openpanel runModalForTypes:fileTypes]){
		fileName = [openpanel filename];
		shouldAdd = YES;
		i = 0;
		howMany = [bundleList count];
		while(i < howMany && shouldAdd){
			tailOne = rindex(fileName,'/') + 1;
			tailTwo = rindex([(WoodBundle *)[bundleList objectAt:i] directory],'/') + 1;
			shouldAdd = strcmp(tailOne, tailTwo) ? YES : NO;
			i++;
		}
		if(shouldAdd){
			newWoodBundle = [[WoodBundle allocFromZone:[self zone]]
                          initForDirectory:fileName];
			[bundleList addObject:newWoodBundle];
			if([newWoodBundle getPath:buf forResource:"English" ofType:"lproj"])
				[[NXHelpPanel new] addSupplement:"Help" inPath:buf];
			[addMenu disableFlushWindow];
			cell = [addMenu addItem:[newWoodBundle filterName] 
				action:@selector(addFromFilter:) keyEquivalent:0];
			[cell setTag:[bundleList count] - 1];
			[cell setTarget:[self calcTargetForAction:@selector(addFromFilter:)]];
			[addMenu reenableFlushWindow];
			[addMenu flushWindow];
		}			
	}
	return self;
}

- createBundlesForDirectory:(const char *)dirPath loadModules:(BOOL)doLoad
{
  	char modulePath[MAXPATHLEN + 1], buf[MAXPATHLEN + 1];
  	char *modulePathInsert, *tailOne, *tailTwo;
  	DIR *dir;
  	struct direct *dirEntry;
  	WoodBundle *newWoodBundle;
	BOOL shouldAdd;
	int i, howMany;

  	strcpy(modulePath, dirPath);
  	modulePathInsert = modulePath + strlen(modulePath);
  	*modulePathInsert = '/';
  	modulePathInsert++;
  	dir = opendir(dirPath);
  	if(!dir)
    	return self;
  	while(dirEntry = readdir(dir)){
    	if((dirEntry->d_name[0] == '.')
        	|| !fileNameHasExtension(dirEntry->d_name, MODULEEXTENSION))
      		continue;
    	strcpy(modulePathInsert, dirEntry->d_name);
		shouldAdd = YES;
		i = 0;
		howMany = [bundleList count];
		while(i < howMany && shouldAdd){
			tailOne = modulePathInsert;
			tailTwo = rindex([(WoodBundle *)[bundleList objectAt:i] directory],'/') + 1;
			shouldAdd = strcmp(tailOne, tailTwo) ? YES : NO;
			i++;
		}
		if(shouldAdd){
    		newWoodBundle = [[WoodBundle allocFromZone:[self zone]]
                          initForDirectory:modulePath];
			if([newWoodBundle getPath:buf forResource:"English" ofType:"lproj"])
				[[NXHelpPanel new] addSupplement:"Help" inPath:buf];
    		[bundleList addObject:newWoodBundle];
    		if(doLoad)
      			[newWoodBundle filter];
		}
  	}
  	closedir(dir);
  	return self;
}

- filter:(int)nr
{
	return [[bundleList objectAt:nr] filter];
}

@end

static void initMenu(id menu)
{
    int count;
    id matrix, cell;
    id matrixTarget, cellTarget;

	matrix = [menu itemList];
    matrixTarget = [matrix target];
    count = [matrix cellCount];
    while (count--) {
		cell = [matrix cellAt:count :0];
		cellTarget = [cell target];
		if (!matrixTarget && !cellTarget) {
			[cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
		} else if ([cell hasSubmenu]) {
			initMenu(cellTarget);
		} else if (cellTarget == [NXApp delegate])
			[cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
    }
}

static id documentInWindow(id window)
{
    id document = [window delegate];

	if (document) 
		return [document isKindOf:docClass] ? document : nil;
	else 
		return nil;
}

static id findDocument(const char *name)
{
    int count;
    WoodDoc *document; 
	id docList;

    docList = [NXApp docList];
    count = [docList count];
    while (count--) {
		document = [docList objectAt:count];
		if (!name || !strcmp([document fileName], name)) 
			return document;
    }
    return nil;
}

static id openFile(const char *directory, const char *name, BOOL display)
{
    id window;
    char buffer[MAXPATHLEN+1], path[MAXPATHLEN+1], temp[MAXPATHLEN+1];

    if (name && *name) {
		if (!directory) 
			directory = ".";
		else if (*directory != '/') {
			strcpy(buffer, "./");
			strcat(buffer, directory);
			directory = buffer;
			getwd(temp);
			if (!chdir(directory) && getwd(path)) 
				chdir(temp);
			else {
				Notify(localString("Open:  path invalid"), directory);
				return nil;
			}
		} else 
			strcpy(path,directory);
		strcat(path, "/");
		strcat(path, name);
		window = [findDocument(path) window];
		if (window) {
			if (display) 
				[window makeKeyAndOrderFront:window];
			return [window delegate];
	    } else 
			return [[docClass allocFromZone:[NXApp newDocZone]] initFromFile:path];
    }
    return nil;
}

static BOOL fileNameHasExtension(const char *f, const char *e)
{
  const char         *dot;

  if (f && e && (dot = strrchr(f, '.')) && !strcmp(dot + 1, e))
    return YES;
  else
    return NO;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.