This is DrawApp.m in view mode; [Download] [Up]
#import "draw.h" const int DrawVersion = 39; /* minor version of the program */ @implementation DrawApp : Object /* * This class is used primarily to handle the opening of new documents * and other application-wide activity (such as responding to messages from * the tool palette). It listens for requests from the Workspace Manager * to open a draw-format file as well as target/action messages from the * New and Open... menu items. It also keeps the menus in sync by * fielding the menu items' updateActions. */ /* Private C functions used to implement methods in this class. */ static void initMenu(Menu *menu) /* * Sets the updateAction for every menu item which sends to the * First Responder (i.e. their target is nil). When autoupdate is on, * every event will be followed by an update of each of the menu items * which is visible. This keep all unavailable menu items dimmed out * so that the user knows what options are available at any given time. * Returns the activate menu if is found in this menu. */ { int count; Matrix *matrix; MenuCell *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); } } } static DrawDocument *documentInWindow(Window *window) /* * Checks to see if the passed window's delegate is a DrawDocument. * If it is, it returns that document, otherwise it returns nil. */ { id document = [window delegate]; return [document isKindOf:[DrawDocument class]] ? document : nil; } static Window *findDocument(const char *name) /* * Searches the window list looking for a DrawDocument with the specified name. * Returns the window containing the document if found. * If name == NULL then the first document found is returned. */ { int count; DrawDocument *document; Window *window; List *windowList; windowList = [NXApp windowList]; count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = documentInWindow(window); if ([document isSameAs:name]) return window; } return nil; } static DrawDocument *openFile(const char *directory, const 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. */ { Window *window; char buffer[MAXPATHLEN+1], path[MAXPATHLEN+1]; if (name && *name) { if (!directory) { directory = "."; } else if (*directory != '/') { strcpy(buffer, "./"); strcat(buffer, directory); directory = buffer; } if (!chdir(directory) && getwd(path)) { strcat(path, "/"); strcat(path, name); window = findDocument(path); if (window) { if (display) [window makeKeyAndOrderFront:window]; return [window delegate]; } else { return [DrawDocument newFromFile:path andDisplay:display]; } } else { NXRunLocalizedAlertPanel(NULL, "Open", "Invalid path: %s", NULL, NULL, NULL, directory, "Alert shown to user when he has tried to open up a file and has specified a file in a directory which is inaccessible. The directory is either unreadable or does not exist for some reason."); } } return nil; } static DrawDocument *openDocument(const char *document, BOOL display) { char *directory, *name, *ext; char buffer[MAXPATHLEN+1]; strcpy(buffer, document); ext = strrchr(buffer, '.'); if (!ext || strcmp(ext, ".hdraw")) strcat(buffer, ".hdraw"); name = strrchr(buffer, '/'); if (name) { *name++ = '\0'; directory = buffer; } else { name = buffer; directory = NULL; } return openFile(directory, name, display); } /* Public methods */ + initialize /* * Initializes the defaults. */ { const NXDefaultsVector DrawDefaults = { { "KnobWidth", "5" }, { "KnobHeight", "5" }, { "KeyMotionDelta", "1"}, { "Quit", NULL }, { "RemoteControl", "YES" }, { "HideCursorOnMove", NULL }, { NULL, NULL } }; NXRegisterDefaults("Draw", DrawDefaults); return self; } - init /* * setAutoupdate:YES means that updateWindows will be called after * every event is processed (this is how we keep our inspector and * our menu items up to date). */ { NXBundle *bundle; char buffer[MAXPATHLEN+1]; [super init]; bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"DrawApp" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:self withNames:YES fromZone:[self zone]]; } NXNameObject( "DrawInstance", self, NXApp ); PSInit(); [NXApp setAutoupdate:YES]; return self; } /* General application status and information querying/modifying methods. */ - currentGraphic /* * The current factory to use to create new Graphics. */ { return currentGraphic; } - (DrawDocument *)currentDocument /* * The DrawDocument in the main window (dark gray title bar). */ { return documentInWindow([NXApp mainWindow]); } - (const char *)currentDirectory /* * Directory where Draw is currently "working." */ { const char *cdir = [[self currentDocument] directory]; return (cdir && *cdir) ? cdir : (haveOpenedDocument ? [[OpenPanel new] directory] : NXHomeDirectory()); } /* * Call these to enter/exit the TextGraphic tool. * These are used currently by the Undo architecture. */ - startEditMode { [tools selectCellAt:1 :0]; [tools sendAction]; return self; } - endEditMode { [tools selectCellAt:0 :0]; [tools sendAction]; return self; } /* Application-wide shared panels */ static const char *cleanTitle(const char *menuItem, char *realTitle) /* * Just strips off trailing "..."'s. */ { int length = menuItem ? strlen(menuItem) : 0; if (length > 3 && !strcmp(menuItem+length-3, "...")) { strcpy(realTitle, menuItem); realTitle[length-3] = '\0'; return realTitle; } else if (length > 1 && (menuItem[length-1] == '\274')) { strcpy(realTitle, menuItem); realTitle[length-1] = '\0'; return realTitle; } else { return menuItem; } } - (SavePanel *)saveToPanel:sender /* * Returns a SavePanel with the accessory view which allows the user to * pick which type of file she wants to save. The title of the Panel is * set to whatever was on the menu item that brought it up (it is assumed * that sender is the menu that has the item in it that was chosen to * cause the action which requires the SavePanel to come up). */ { NXBundle *bundle; char buffer[MAXPATHLEN+1]; char title[100]; const char *theTitle; SavePanel *savepanel = [SavePanel new]; if (!savePanelAccessory) { bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"SavePanelAccessory" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:self withNames:NO fromZone:[savepanel zone]]; } [spamatrix selectCellAt:0 :0]; } theTitle = cleanTitle([[sender selectedCell] title], title); if (theTitle) [savepanel setTitle:theTitle]; [savepanel setAccessoryView:savePanelAccessory]; [ [self currentDocument] changeSaveType: spamatrix]; return savepanel; } - (SavePanel *)saveAsPanel:sender /* * Returns a regular SavePanel with "draw" as the required file type. */ { char title[100]; const char *theTitle; SavePanel *savepanel = [SavePanel new]; [savepanel setAccessoryView:nil]; theTitle = cleanTitle([[sender selectedCell] title], title); if (theTitle) [savepanel setTitle:theTitle]; [savepanel setRequiredFileType:"hdraw"]; return savepanel; } - (GridView *)gridInspector /* * Returns the application-wide inspector for a document's grid. * Note that if we haven't yet loaded the GridView panel, we do it. * The instance variable gridInspector is set in setGridInspector: * since it is set as an outlet of the owner (self, i.e. DrawApp). */ { NXBundle *bundle; char buffer[MAXPATHLEN+1]; if (!gridInspector) { NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES); NXNameZone(zone, "GridView"); bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"GridView" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:self withNames:NO fromZone:zone]; } NXMergeZone(zone); } return gridInspector; } - (Panel *)inspectorPanel /* * Returns the application-wide inspector for Graphics. */ { NXBundle *bundle; char buffer[MAXPATHLEN+1]; if (!inspectorPanel) { NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES); NXNameZone(zone, "Inspector"); bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"InspectorPanel" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:self withNames:NO fromZone:zone]; } [inspectorPanel setFrameAutosaveName:"Inspector"]; [inspectorPanel setBecomeKeyOnlyIfNeeded:YES]; [[inspectorPanel delegate] preset]; NXMergeZone(zone); } return inspectorPanel; } - (Box *)contentBox { if ( !inspectorPanel) { inspectorPanel = [self inspectorPanel]; } return contentBox; } - (DrawPageLayout *)pageLayout /* * Returns the application-wide DrawPageLayout panel. */ { static DrawPageLayout *dpl = nil; NXBundle *bundle; char buffer[MAXPATHLEN+1]; if (!dpl) { dpl = [DrawPageLayout new]; bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"PageLayoutAccessory" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:dpl withNames:NO fromZone:[dpl zone]]; } } return dpl; } - orderFrontInspectorPanel:sender /* * Creates the inspector panel if it doesn't exist, then orders it front. */ { [[self inspectorPanel] orderFront:self]; return self; } /* Setting up the Fax Cover Sheet menu */ #define FAX_NOTE NXLocalString("Notes", NULL, "Fax Cover Sheet item which allows the user to type in any thing he/she wants.") - setFaxCoverSheetMenu:anObject /* * This goes through all the entries in CoverSheet.strings and makes * an entry in the cover sheet menu for them. This is kind of a kooky * method, but it makes Draw much more usable as a Fax Cover Sheet * editor. */ { Menu *fcsMenu; MenuCell *cell; const char *key, *value; NXStringTable *table; char path[MAXPATHLEN+1]; if (fcsMenu = [anObject target]) { if ([[NXBundle mainBundle] getPath:path forResource:"CoverSheet" ofType:"strings"]) { if (table = [[[NXStringTable allocFromZone:[self zone]] init] readFromFile:path]) { NXHashState state = [table initState]; while ([table nextState:&state key:(void **)&key value:(void **)&value]) { cell = [fcsMenu addItem:value action:@selector(addLocalizableCoverSheetEntry:) keyEquivalent:0]; [cell setAltTitle:key]; } [table free]; } } [fcsMenu addItem:FAX_NOTE action:@selector(addCoverSheetEntry:) keyEquivalent:0]; } return self; } /* Target/Action methods */ - info:sender /* * Brings up the information panel. */ { NXBundle *bundle; char buffer[MAXPATHLEN+1]; char buf[20]; if (!infoPanel) { NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES); NXNameZone(zone, "InfoPanel"); bundle = [NXBundle bundleForClass:[self class]]; if ( [bundle getPath:buffer forResource:"InfoPanel" ofType:"nib"] ) { [NXApp loadNibFile:buffer owner:self withNames:NO fromZone:zone]; } [infoPanel setFrameAutosaveName:"InfoPanel"]; sprintf(buf, "(v%2d)", DrawVersion); [version setStringValue:buf]; NXMergeZone(zone); } [infoPanel orderFront:self]; return self; } #define NO_HELP NXLocalString("Couldn't find any help! Sorry ...", NULL, "Message given to the user when the help document could not be found.") - help:sender /* * Loads up the Help draw document. * Note the use of NXBundle so that the Help document can be localized. */ { DrawDocument *document = nil; char path[MAXPATHLEN+1]; if ([[NXBundle mainBundle] getPath:path forResource:"Help" ofType:"draw"]) { if (document = openDocument(path, NO)) { [document setTemporaryTitle:NXLocalString("Help", NULL, "The title of the help document.")]; [[[document view] window] makeKeyAndOrderFront:self]; } } if (!document) NXRunAlertPanel(NULL, NO_HELP, NULL, NULL, NULL); return self; } - new:sender /* * Creates a new document--called by pressing New in the Document menu. */ { [DrawDocument new]; return self; } - open:sender /* * Called by pressing Open... in the Window menu. */ { const char *directory; const char *const *files; char **myfiles; const char *const drawFileTypes[] = { "hdraw", "hdraw~", NULL }; OpenPanel *openpanel = [[OpenPanel new] allowMultipleFiles:YES]; int i, nfiles; directory = [self currentDirectory]; if (directory && (*directory == '/')) [openpanel setDirectory:directory]; if ([openpanel runModalForTypes:drawFileTypes]) { files = [openpanel filenames]; directory = [openpanel directory]; nfiles = 0; while ( files && *files ) { nfiles++; files++; } files = [openpanel filenames]; NX_MALLOC( myfiles, char *, nfiles ); for ( i = 0; i < nfiles; i++ ) { myfiles[i] = NXCopyStringBuffer(*files); files++; } for ( i = 0; i < nfiles; i++ ) { haveOpenedDocument = openFile(directory, myfiles[i], YES) || haveOpenedDocument; NX_FREE( myfiles[i] ); } NX_FREE( myfiles ); } return self; } - saveAll:sender /* * Saves all the documents. */ { int count; Window *window; id windowList; windowList = [NXApp windowList]; count = [windowList count]; while (count--) { window = [windowList objectAt:count]; [documentInWindow(window) save:sender]; } return nil; } #define UNSAVED_DOCUMENTS NXLocalString("You have unsaved documents.", NULL, "Message given to user when he tries to quit the application without saving all of his documents.") #define REVIEW_UNSAVED NXLocalString("Review Unsaved", NULL, "Choice (on a button) given to user which allows him to review all his unsaved documents if he quits the application without saving them all first.") #define QUIT_ANYWAY NXLocalString("Quit Anyway", NULL, "Choice (on a button) given to user which allows him to quit the application even though he has not saved all of his documents first.") #define QUIT NXLocalString("Quit", NULL, "The operation of exiting the application.") - terminate:sender cancellable:(BOOL)cancellable /* * Makes sure all documents get an opportunity * to be saved before exiting the program. If we are terminating because * the user logged out of the workspace (or powered off), then we cannot * give the user the option of cancelling the quit (that's what the * cancellable flag is for). */ { int count, choice; Window *window; id document; id windowList; windowList = [NXApp windowList]; count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = [window delegate]; if ([document respondsTo:@selector(isDirty)] && [document isDirty]) { if (cancellable) { choice = NXRunAlertPanel(QUIT, UNSAVED_DOCUMENTS, REVIEW_UNSAVED, QUIT_ANYWAY, CANCEL); } else { choice = NXRunAlertPanel(QUIT, UNSAVED_DOCUMENTS, REVIEW_UNSAVED, QUIT_ANYWAY, NULL); } if (choice == NX_ALERTOTHER) { return self; } else if (choice == NX_ALERTDEFAULT) { count = [windowList count]; while (count--) { window = [windowList objectAt:count]; document = [window delegate]; if ([document respondsTo:@selector(windowWillClose:cancellable:)]) { if (![document windowWillClose:sender cancellable:cancellable] && cancellable) { return self; } else { [document close]; [window close]; } } } } break; } } return nil; } - terminate:sender /* * Overridden to give user the opportunity to save unsaved files. */ { if (![self terminate:sender cancellable:YES]) { return [NXApp terminate:sender]; } else { return self; } } /* * 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:(Application *)sender /* * Makes the tool palette not ever become the key window. * Check for files to open specified on the command line. * Initialize the menus. * If there are no open documents (and we are not being * launched to service a Services request or otherwise * being invoked due to interapplication communication), * then open a blank one. */ { int i; Panel *toolWindow; // [[NXApp appListener ] setServicesDelegate:self]; toolWindow = [tools window]; [toolWindow setFrameAutosaveName:[toolWindow title]]; [toolWindow setBecomeKeyOnlyIfNeeded:YES]; [toolWindow setFloatingPanel:YES]; [toolWindow orderFront:self]; if (NXArgc > 1) { for (i = 1; i < NXArgc; i++) { haveOpenedDocument = openDocument(NXArgv[i], YES) || haveOpenedDocument; } } // if (!haveOpenedDocument && !NXGetDefaultValue([NXApp appName], "NXServiceLaunch")) [self new:self]; if (NXGetDefaultValue([NXApp appName], "Quit")) { [NXApp activateSelf:YES]; NXPing(); [self terminate:nil]; } initMenu([NXApp mainMenu]); return self; } - (int)app:sender openFile:(const char *)path type:(const char *)type /* * This method is performed whenever a user double-clicks on an icon * in the Workspace Manager representing a Draw program document. */ { if (type && !strcmp(type, "hdraw")) { if (openDocument(path, YES)) { haveOpenedDocument = YES; return YES; } } return NO; } - (BOOL)appAcceptsAnotherFile:(Application *)sender /* * We accept any number of appOpenFile:type: messages. */ { return YES; } - app:sender powerOffIn:(int)ms andSave:(int)andSave /* * Give the user a chance to save his documents. */ { if (andSave) [self terminate:nil cancellable:NO]; return self; } /* Listener/Speaker remote methods */ /* * Note that anybody can send these messages to a running version * of Draw, so we protect the ones that can affect the current * document by only allowing them to happen if the Draw default * "RemoteControl" is set. */ - (int)msgDirectory:(const char **)fullPath ok:(int *)flag { *fullPath = [self currentDirectory]; if (*fullPath) { *flag = YES; } else { *fullPath = ""; *flag = NO; } return 0; } - (int)msgVersion:(const char **)aString ok:(int *)flag { char buf[20]; sprintf(buf, "3.0 (v%2d)", DrawVersion); *aString = NXCopyStringBuffer(buf); *flag = YES; return 0; } - (int)msgFile:(const char **)fullPath ok:(int *)flag { const char *file; file = [[self currentDocument] filename]; if (file) { *fullPath = file; *flag = YES; } else { *fullPath = ""; *flag = NO; } return 0; } - (BOOL)shouldRunPrintPanel:sender /* * When NXApp prints, don't bring up the PrintPanel. */ { return NO; } - (int)msgPrint:(const char *)fullPath ok:(int *)flag { BOOL close; id document = nil; if (NXGetDefaultValue("Draw", "RemoteControl")) { InMsgPrint = YES; if (document = [findDocument(fullPath) delegate]) { close = NO; } else { document = openDocument(fullPath, NO); if (document) haveOpenedDocument = YES; close = YES; } if (document && ![[document view] isEmpty]) { [NXApp setPrintInfo:[document printInfo]]; [[document view] printPSCode:self]; if (close) [[[document view] window] close]; *flag = YES; } else { *flag = NO; } InMsgPrint = NO; } else { *flag = NO; } return 0; } - (int)msgSelection:(const char **)bytes length:(int *)len asType:(const char *)aType ok:(int *)flag { int maxlen; NXStream *stream; if (NXGetDefaultValue("Draw", "RemoteControl")) { if (!strcmp(aType, DrawPboardType)) { stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if ([[[self currentDocument] view] copySelectionToStream:stream]) { NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen); *flag = YES; } else { *flag = NO; } NXClose(stream); } else if (!strcmp(aType, NXPostScriptPboardType)) { stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if ([[[self currentDocument] view] copySelectionAsPSToStream:stream]) { NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen); *flag = YES; } else { *flag = NO; } NXClose(stream); } else if (!strcmp(aType, NXTIFFPboardType)) { stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); if ([[[self currentDocument] view] copySelectionAsTIFFToStream:stream]) { NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen); *flag = YES; } else { *flag = NO; } NXClose(stream); } else { *flag = NO; } } else { *flag = NO; } if (!*flag) { *bytes = ""; *len = 0; } return 0; } - (int)msgCopyAsType:(const char *)aType ok:(int *)flag { if (NXGetDefaultValue("Draw", "RemoteControl")) { if (!strcmp(aType, NXPostScriptPboardType) || !strcmp(aType, NXTIFFPboardType) || !strcmp(aType, DrawPboardType)) { *flag = ([[[self currentDocument] view] copy:self] ? YES : NO); } else { *flag = NO; } } else { *flag = NO; } return 0; } - (int)msgCutAsType:(const char *)aType ok:(int *)flag { if (NXGetDefaultValue("Draw", "RemoteControl")) { if (!strcmp(aType, NXPostScriptPboardType) || !strcmp(aType, NXTIFFPboardType) || !strcmp(aType, DrawPboardType)) { *flag = ([[[self currentDocument] view] cut:self] ? YES : NO); } else { *flag = NO; } } else { *flag = NO; } return 0; } - (int)msgPaste:(int *)flag; { if (NXGetDefaultValue("Draw", "RemoteControl")) { *flag = ([[[self currentDocument] view] paste:self] ? YES : NO); } else { *flag = NO; } return 0; } - (int)msgQuit:(int *)flag { if (NXGetDefaultValue("Draw", "RemoteControl")) { *flag = ([self terminate:self cancellable:YES] ? NO : YES); if (*flag) [NXApp perform:@selector(terminate:) with:nil afterDelay:1 cancelPrevious:YES]; } else { *flag = NO; } return 0; } /* Global cursor setting */ - cursor /* * This is called by DrawDocument objects who want to set the cursor * depending on what the currently selected tool is (as well as on whether * the Control key has been pressed indicating that the select tool is * temporarily set--see sendEvent:). */ { id theCursor = nil; if (!cursorPushed) theCursor = [[self currentGraphic] cursor]; return theCursor ? theCursor : NXArrow; } - sendEvent:(NXEvent *)event /* * We override this because we need to find out when the control key is down * so we can set the arrow cursor so the user knows she is (temporarily) in * select mode. */ { if (event && event->type < NX_KITDEFINED) { /* mouse or keyboard event */ if (event->flags & NX_CONTROLMASK) { if (!cursorPushed && currentGraphic) { cursorPushed = YES; [[self currentDocument] resetCursor]; } } else if (cursorPushed) { cursorPushed = NO; [[self currentDocument] resetCursor]; } } return [NXApp sendEvent:event]; } /* Automatic update methods */ - (BOOL)menuItemUpdate:(MenuCell *)menuCell /* * Method called by all menu items which send their actions to the * First Responder. First, if the object which would respond were the * action sent down the responder chain also responds to the message * validateCommand:, then it is sent validateCommand: to determine * whether that command is valid now, otherwise, if there is a responder * to the message, then it is assumed that the item is valid. * The method returns YES if the cell has changed its appearance (so that * the caller (a Menu) knows to redraw it). */ { SEL action; id responder, target; BOOL enable; target = [menuCell target]; enable = [menuCell isEnabled]; if (!target) { action = [menuCell action]; responder = [NXApp calcTargetForAction:action]; if ([responder respondsTo:@selector(validateCommand:)]) { enable = [responder validateCommand:menuCell]; } else { enable = responder ? YES : NO; } } if ([menuCell isEnabled] != enable) { [menuCell setEnabled:enable]; return YES; } else { return NO; } } - (BOOL)validateCommand:(MenuCell *)menuCell /* * The only command DrawApp itself controls is saveAll:. * Save All is enabled only if there are any documents open. */ { SEL action = [menuCell action]; if (action == @selector(saveAll:)) { return findDocument(NULL) ? YES : NO; } return YES; } /* * This is a very funky method and tricks of this sort are not generally * recommended, but this hack is done so that new Graphic subclasses can * be added to the program without changing even one line of code (except, * of course, to implement the subclass itself). * * The objective-C method objc_lookUpClass() is used to find the factory object * corresponding to the name of the icon of the cell sending the setCurrentGraphic: * message. * * Again, this is not recommended procedure, but it illustrates how * objective-C can be used to make some funky runtime dependent decisions. */ - setCurrentGraphic:sender /* * The sender's icon is queried. If that name corresponds to the name * of a class, then that class is set as the currentGraphic. If not, * then the select tool is put into effect. */ { id cell; const char *className; if (cell = [sender selectedCell]) { if (className = [cell icon]) { currentGraphic = objc_lookUpClass(className); } else { currentGraphic = nil; } if (!currentGraphic) [tools selectCellAt:0 :0]; [[self currentDocument] resetCursor]; } return self; } /* Added to support HippoDraw since DrawApp is not subclass of * Application */ - orderFrontTools:sender { [[tools window] orderFront:self]; return self; } - setHaveOpenedDocument:(BOOL) flag { haveOpenedDocument = flag; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.