This is AppWithDoc.m in view mode; [Download] [Up]
#import "AppWithDoc.h" #import "Document.h" #import <objc/List.h> #import <appkit/Matrix.h> #import <appkit/Menu.h> #import <appkit/MenuCell.h> #import <appkit/OpenPanel.h> #import <appkit/defaults.h> #import <appkit/nextstd.h> #import <objc/hashtable.h> /* for NXCopyStringBuffer() */ #import <text/text.h> #define VERSION_STRING "%.2f" @implementation AppWithDoc:Application /* * This class is used primarily to handle the opening of new documents and * other application-wide activity. It listens for requests from the * Workspace Manager to open a Document file as well as target/action * messages from the New and Open... menu items. */ /* Private C functions used to implement methods in this class. */ static id initMenu(id menu) /* * Returns the activate menu if is found in this menu. */ { const char *s; static id activateMenu; id cell, cellTarget; id matrix = [menu itemList]; int count = [matrix cellCount]; while (count--) { cell = [matrix cellAt:count :0]; cellTarget = [cell target]; if ([cell hasSubmenu]) initMenu(cellTarget); s = [cell title]; if (s && !strcmp(s, "Activate")) activateMenu = cellTarget; } return activateMenu; } static id documentInWindow(id window) /* * Checks to see if the passed window's delegate is a Document. If it is, it * returns that document, otherwise it returns nil. */ { id document = [window delegate]; return [document isKindOf:[Document class]] ? document : nil; } static id findDocument(const char *name) /* * Searches the window list looking for a Document with the specified name. * Returns the window containing the document if found. If name == NULL then * the first document found is returned. */ { id document, window; id windowList = [NXApp windowList]; int count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = documentInWindow(window); if (document && !(name && strcmp([document filename], name))) return window; } return nil; } static BOOL loadActivateMenu() /* * Adds all open documents to the activate menu. Returns whether any were * actually loaded. */ { BOOL foundOne = NO; id document, window; id windowList = [NXApp windowList]; int count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = documentInWindow(window); if (document) { [NXApp addDocument:[document filename]]; foundOne = YES; } } return foundOne; } static id openFile(STR directory, id newClass, char* name, BOOL display) /* * Opens a file with the given name in the specified directory. If we already * have that file open, it is ordered front. Returns the document if * successful, nil otherwise. */ { id window; char path[MAXPATHLEN + 1]; if (name && *name) { if (!directory) directory = pwd(); else if (*directory != '/') directory = fullPath(pwd(), directory); if (isDirectory(directory)) { strcpy(path, fullPath(directory, name)); window = findDocument(path); if (window) { if (display) [window makeKeyAndOrderFront:window]; return[window delegate]; } else { return [newClass newFromFile:path]; } } else { NXRunAlertPanel("Open", "Invalid path: %s", "OK", NULL, NULL, directory); } } return nil; } /* Public methods */ + initialize /* * Initializes the defaults. */ { const NXDefaultsVector AppDefaults = { {"WinLocX", "50"}, {"WinLocY", "100"}, {"WinLocDx", "15"}, {"WinLocDy", "15"}, {"Quit", NULL}, {NULL, NULL}}; NXRegisterDefaults(appName, AppDefaults); return self; } + new /* * Defaults to Document; 0, 'document' */ { return[self newClass:[Document class] extension:"document" version:0]; } + newClass:newDoc extension:(STR)newExt version:(int)newVersion { self = [super new]; docClass = newDoc; extension = newExt; version = newVersion / 10.0; [[self class] setVersion:newVersion]; return self; } /* General application status and information querying/modifying methods. */ - readFromFile:(const char *)filename { NXTypedStream *tstream; if (! (tstream=NXOpenTypedStreamForFile(filename, NX_READONLY)) ) { NotifyArg("Read","Error opening file %s", filename); return nil; } self = NXReadObject(tstream); NXCloseTypedStream(tstream); return self; } - write:obj toFile:(const char *)filename { NXTypedStream *tstream; if (! (tstream=NXOpenTypedStreamForFile(filename, NX_WRITEONLY)) ) { NotifyArg("Write", "Error opening file %s", filename); return nil; } NXWriteObject(tstream, obj); NXCloseTypedStream(tstream); return obj; } - locateWindow:window { static NXCoord x, dx, y, dy; if (!x) { x = atof(NXGetDefaultValue(appName, "WinLocX")); dx = atof(NXGetDefaultValue(appName, "WinLocDx")); y = atof(NXGetDefaultValue(appName, "WinLocY")); dy = atof(NXGetDefaultValue(appName, "WinLocDy")); } [window moveTo:x :y]; x += dx; y += dy; return window; } /* Application-wide shared info */ - currentDocument { return documentInWindow(mainWindow); } - (STR)extension { return extension; } - saveAsPanel /* * Returns a regular SavePanel with extension of the required file type. */ { id savepanel = [SavePanel new]; [savepanel setAccessoryView:nil]; [savepanel setRequiredFileType:extension]; return savepanel; } /* Target/Action methods */ - info:sender /* * Brings up the information panel. */ { char buf[6]; if (!infoPanel) { infoPanel = [self loadNibSection:"InfoPanel.nib" owner:self withNames:YES]; sprintf(buf, VERSION_STRING, version); [versionField setStringValue:buf]; } [infoPanel orderFront:self]; return self; } - new:sender /* * Called by pressing New in the Window menu. */ { [docClass new]; return self; } - open:sender /* * Called by pressing Open... in the Window menu. */ { const char *directory; const char* const* files; const char *const docType[] = {extension, NULL}; id openpanel = [[OpenPanel new] allowMultipleFiles:YES]; directory = [[self currentDocument] directory]; if (directory && (*directory == '/')) { [openpanel setDirectory:directory]; } if ([openpanel runModalForTypes:docType]) { files = [openpanel filenames]; directory = [openpanel directory]; while (files && *files) { openFile(directory, docClass, *files, YES); files++; } } return self; } - print:sender /* * Print the View */ { [[[self currentDocument] view] printPSCode:sender]; } - terminate:sender /* * Overridden to be sure all documents get an opportunity to be saved before * exiting the program. */ { id window, document; int count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = [window delegate]; if ([document respondsTo:@selector(windowWillClose:)]) { if ([document windowWillClose:window]) [window close]; else return self; } } return[super terminate:sender]; } /* * Application object delegate methods. Since we don't have an application * delegate, messages that would normally be sent there are sent to the * Application object itself instead. */ - appDidInit:sender /* * Check for files to open specified on the command line. Initialize the * menus and get a handle on the activate menu. Add all opened documents to * the activate menu. If there are no open documents, then open a blank one. */ { int i; char buffer[MAXPATHLEN + 1]; char type[MAXPATHLEN + 1]; char *directory, *name, *ext; if (NXArgc > 1) { for (i = 1; i < NXArgc; i++) { strcpy(buffer, NXArgv[i]); strcpy(type, "."); strcat(type, extension); ext = strrchr(buffer, '.'); if (!ext || strcmp(ext, type)) strcat(buffer, type); directory = parentname(buffer); name = basename(buffer); openFile(directory, docClass, name, YES); } } activateMenu = initMenu([NXApp mainMenu]); if (!loadActivateMenu()) [self new:self]; /* if none opened, open one */ if (NXGetDefaultValue(appName, "Quit")) { [self activateSelf:YES]; NXPing(); [self terminate:self]; } return self; } - (int)appOpenFile:(char* )path type:(char* )type /* * This method is performed whenever a user double-clicks on an icon in the * Workspace Manager representing the document. */ { char *name; char *directory; if (type && !strcmp(type, extension)) { directory = parentname(path); name = basename(path); return (openFile(directory, docClass, name, YES) ? YES : NO); } return NO; } - (BOOL) appAcceptsAnotherFile:sender /* * We accept any number of appOpenFile:type: messages. */ { return YES; } /* Listener/Speaker remote methods */ - (int)msgVersion:(char* *)aString ok:(int *)flag { char buf[6]; sprintf(buf, VERSION_STRING, version); *aString = NXCopyStringBuffer(buf); *flag = YES; return 0; } - (int)msgDirectory:(const char **)totalPath ok:(int *)flag { *totalPath = [[self currentDocument] directory]; if (!*totalPath) *totalPath = [[OpenPanel new] directory]; if (*totalPath) { *flag = YES; } else { *totalPath = ""; *flag = NO; } return 0; } - (int)msgFile:(const char **)totalPath ok:(int *)flag { const char *file; file = [[self currentDocument] filename]; if (file) { *totalPath = file; *flag = YES; } else { *totalPath = ""; *flag = NO; } return 0; } - (int)msgPrint:(char* )totalPath ok:(int *)flag { BOOL close; id document = nil; char *directory, *name; char *path; directory = parentname(totalPath); name = basename(totalPath); if (isDirectory(directory)) { path = NXCopyStringBuffer(fullPath(directory, name)); document = [findDocument(path) delegate]; } if (document) { close = NO; } else { document = openFile(directory, docClass, name, NO); close = YES; } if (document && [document contents]) { [document print:self]; if (close) [[document window] close]; *flag = YES; } else { *flag = NO; } return 0; } /* Activate menu updating */ static int cellcmp(const void *first, const void *second) { const char *s1 = [*((id *) first) title]; const char *s2 = [*((id *) second) title]; return (s1 && s2) ? strcmp(s1, s2) : 0; } #define CellSortIf(COND) if (COND) {\ qsort(NX_ADDRESS(cells), [cells count], sizeof(id *), cellcmp);\ [activateMenu sizeToFit]; } - addDocument:(char *)name /* * Adds the name passed in to the Activate menu (if it isn't already there). */ { int i; const char *s; id matrix = [activateMenu itemList]; id cells = [matrix cellList]; if (!name) return self; for (i = [cells count] - 1; i >= 0; i--) { s = [[cells objectAt:i] title]; if (s && !strcmp(s, name)) return self; } [activateMenu addItem:name action:@selector(activateDocument:) keyEquivalent :0]; CellSortIf(cells); return self; } - removeDocument:(char *)name /* * Removes the passed name from the Activate menu. */ { int i; const char *s; id matrix = [activateMenu itemList]; id cells = [matrix cellList]; int count = [cells count]; if (!name) return self; for (i = 0; i < count; i++) { s = [[cells objectAt:i] title]; if (s && !strcmp(s, name)) { [matrix removeRowAt:i andFree:YES]; break; } } CellSortIf(i != count); return self; } - renameDocument:(char *)oldName to:(char *)newName /* * Finds the passed oldName in the Activate menu and changes the title of * that menu entry to newName. */ { int i; const char *s; id matrix = [activateMenu itemList]; id cell, cells = [matrix cellList]; int count = [cells count]; if (!oldName) return self; for (i = 0; i < count; i++) { cell = [cells objectAt:i]; s = [cell title]; if (s && !strcmp(s, oldName)) { [cell setTitle:newName]; break; } } CellSortIf(i != count); return self; } - activateDocument:sender /* * Sent by items in the Activate menu. Queries back to the sender to find * out which document to activate, then activates it. */ { [findDocument([[sender selectedCell] title]) makeKeyAndOrderFront :self]; return self; } - setVersionField:anObject { versionField = anObject; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.