Annotation of Examples/AppKit/Draw/DrawApp.m, revision 1.1

1.1     ! root        1: #import "draw.h"
        !             2: 
        !             3: const int DrawVersion = 48;    /* minor version of the program */
        !             4: 
        !             5: @implementation DrawApp : Application
        !             6: /*
        !             7:  * This class is used primarily to handle the opening of new documents
        !             8:  * and other application-wide activity (such as responding to messages from
        !             9:  * the tool palette).  It listens for requests from the Workspace Manager
        !            10:  * to open a draw-format file as well as target/action messages from the
        !            11:  * New and Open... menu items.  It also keeps the menus in sync by
        !            12:  * fielding the menu items' updateActions.
        !            13:  */
        !            14: 
        !            15: /* Private C functions used to implement methods in this class. */
        !            16: 
        !            17: static void initMenu(Menu *menu)
        !            18: /*
        !            19:  * Sets the updateAction for every menu item which sends to the
        !            20:  * First Responder (i.e. their target is nil).  When autoupdate is on,
        !            21:  * every event will be followed by an update of each of the menu items
        !            22:  * which is visible.  This keep all unavailable menu items dimmed out
        !            23:  * so that the user knows what options are available at any given time.
        !            24:  * Returns the activate menu if is found in this menu.
        !            25:  */ 
        !            26: {
        !            27:     int count;
        !            28:     Matrix *matrix;
        !            29:     MenuCell *cell;
        !            30:     id matrixTarget, cellTarget;
        !            31: 
        !            32:     matrix = [menu itemList];
        !            33:     matrixTarget = [matrix target];
        !            34: 
        !            35:     count = [matrix cellCount];
        !            36:     while (count--) {
        !            37:        cell = [matrix cellAt:count :0];
        !            38:        cellTarget = [cell target];
        !            39:        if (!matrixTarget && !cellTarget) {
        !            40:            [cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
        !            41:        } else if ([cell hasSubmenu]) {
        !            42:            initMenu(cellTarget);
        !            43:        }
        !            44:     }
        !            45: }
        !            46: 
        !            47: static DrawDocument *documentInWindow(Window *window)
        !            48: /*
        !            49:  * Checks to see if the passed window's delegate is a DrawDocument.
        !            50:  * If it is, it returns that document, otherwise it returns nil.
        !            51:  */
        !            52: {
        !            53:     id document = [window delegate];
        !            54:     return [document isKindOf:[DrawDocument class]] ? document : nil;
        !            55: }
        !            56: 
        !            57: static Window *findDocument(const char *name)
        !            58: /*
        !            59:  * Searches the window list looking for a DrawDocument with the specified name.
        !            60:  * Returns the window containing the document if found.
        !            61:  * If name == NULL then the first document found is returned.
        !            62:  */
        !            63: {
        !            64:     int count;
        !            65:     DrawDocument *document;
        !            66:     Window *window;
        !            67:     List *windowList;
        !            68: 
        !            69:     windowList = [NXApp windowList];
        !            70:     count = [windowList count];
        !            71:     while (count--) {
        !            72:        window = [windowList objectAt:count];
        !            73:        document = documentInWindow(window);
        !            74:        if ([document isSameAs:name]) return window;
        !            75:     }
        !            76: 
        !            77:     return nil;
        !            78: }
        !            79: 
        !            80: static DrawDocument *openFile(const char *directory, const char *name, BOOL display)
        !            81: /*
        !            82:  * Opens a file with the given name in the specified directory.
        !            83:  * If we already have that file open, it is ordered front.
        !            84:  * Returns the document if successful, nil otherwise.
        !            85:  */
        !            86: {
        !            87:     Window *window;
        !            88:     char buffer[MAXPATHLEN+1], path[MAXPATHLEN+1];
        !            89: 
        !            90:     if (name && *name) {
        !            91:        if (!directory) {
        !            92:            directory = ".";
        !            93:        } else if (*directory != '/') {
        !            94:            strcpy(buffer, "./");
        !            95:            strcat(buffer, directory);
        !            96:            directory = buffer;
        !            97:        }
        !            98:        if (!chdir(directory) && getwd(path)) {
        !            99:            strcat(path, "/");
        !           100:            strcat(path, name);
        !           101:            window = findDocument(path);
        !           102:            if (window) {
        !           103:                if (display) [window makeKeyAndOrderFront:window];
        !           104:                return [window delegate];
        !           105:            } else {
        !           106:                return [DrawDocument newFromFile:path andDisplay:display];
        !           107:            }
        !           108:        } else {
        !           109:            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.");
        !           110:        }
        !           111:     }
        !           112: 
        !           113:     return nil;
        !           114: }
        !           115: 
        !           116: static DrawDocument *openDocument(const char *document, BOOL display)
        !           117: {
        !           118:     char *directory, *name, *ext;
        !           119:     char buffer[MAXPATHLEN+1];
        !           120: 
        !           121:     strcpy(buffer, document);
        !           122:     ext = strrchr(buffer, '.');
        !           123:     if (!ext || strcmp(ext, ".draw")) strcat(buffer, ".draw");
        !           124:     name = strrchr(buffer, '/');
        !           125:     if (name) {
        !           126:        *name++ = '\0';
        !           127:        directory = buffer;
        !           128:     } else {
        !           129:        name = buffer;
        !           130:        directory = NULL;
        !           131:     }
        !           132: 
        !           133:     return openFile(directory, name, display);
        !           134: }
        !           135: 
        !           136: /* Public methods */
        !           137: 
        !           138: + initialize
        !           139: /*
        !           140:  * Initializes the defaults.
        !           141:  */
        !           142: {
        !           143:     const NXDefaultsVector DrawDefaults = {
        !           144:        { "KnobWidth", "5" },
        !           145:        { "KnobHeight", "5" },
        !           146:        { "KeyMotionDelta", "1"},
        !           147:        { "Quit", NULL },
        !           148:        { "RemoteControl", "YES" },
        !           149:        { "HideCursorOnMove", NULL },
        !           150:        { NULL, NULL }
        !           151:     };
        !           152: 
        !           153:     NXRegisterDefaults("Draw", DrawDefaults);
        !           154: 
        !           155:     return self;
        !           156: }
        !           157: 
        !           158: + new
        !           159: /*
        !           160:  * setAutoupdate:YES means that updateWindows will be called after
        !           161:  * every event is processed (this is how we keep our inspector and
        !           162:  * our menu items up to date).
        !           163:  */
        !           164: {
        !           165:     self = [super new];
        !           166:     [self setAutoupdate:YES];
        !           167:     return self;
        !           168: }
        !           169: 
        !           170: /* General application status and information querying/modifying methods. */
        !           171: 
        !           172: - currentGraphic
        !           173: /*
        !           174:  * The current factory to use to create new Graphics.
        !           175:  */
        !           176: {
        !           177:     return currentGraphic;
        !           178: }
        !           179: 
        !           180: - (DrawDocument *)currentDocument
        !           181: /*
        !           182:  * The DrawDocument in the main window (dark gray title bar).
        !           183:  */
        !           184: {
        !           185:     return documentInWindow(mainWindow);
        !           186: }
        !           187: 
        !           188: - (const char *)currentDirectory
        !           189: /*
        !           190:  * Directory where Draw is currently "working."
        !           191:  */
        !           192: {
        !           193:     const char *cdir = [[self currentDocument] directory];
        !           194:     return (cdir && *cdir) ? cdir : (haveOpenedDocument ? [[OpenPanel new] directory] : NXHomeDirectory());
        !           195: }
        !           196: 
        !           197: /*
        !           198:  * Call these to enter/exit the TextGraphic tool.
        !           199:  * These are used currently by the Undo architecture.
        !           200:  */
        !           201: 
        !           202: - startEditMode
        !           203: {
        !           204:     [tools selectCellAt:1 :0];
        !           205:     [tools sendAction];
        !           206:     return self;
        !           207: }
        !           208: 
        !           209: - endEditMode
        !           210: {
        !           211:     [tools selectCellAt:0 :0];
        !           212:     [tools sendAction];
        !           213:     return self;
        !           214: }
        !           215: 
        !           216: /* Application-wide shared panels */
        !           217: 
        !           218: static const char *cleanTitle(const char *menuItem, char *realTitle)
        !           219: /*
        !           220:  * Just strips off trailing "..."'s.
        !           221:  */
        !           222: {
        !           223:     int length = menuItem ? strlen(menuItem) : 0;
        !           224: 
        !           225:     if (length > 3 && !strcmp(menuItem+length-3, "...")) {
        !           226:        strcpy(realTitle, menuItem);
        !           227:        realTitle[length-3] = '\0';
        !           228:        return realTitle;
        !           229:     } else if (length > 1 && (menuItem[length-1] == '\274')) {
        !           230:        strcpy(realTitle, menuItem);
        !           231:        realTitle[length-1] = '\0';
        !           232:        return realTitle;
        !           233:     } else {
        !           234:        return menuItem;
        !           235:     }
        !           236: }
        !           237: 
        !           238: - (SavePanel *)saveToPanel:sender
        !           239: /*
        !           240:  * Returns a SavePanel with the accessory view which allows the user to
        !           241:  * pick which type of file she wants to save.  The title of the Panel is
        !           242:  * set to whatever was on the menu item that brought it up (it is assumed
        !           243:  * that sender is the menu that has the item in it that was chosen to
        !           244:  * cause the action which requires the SavePanel to come up).
        !           245:  */
        !           246: {
        !           247:     char title[100];
        !           248:     const char *theTitle;
        !           249:     SavePanel *savepanel = [SavePanel new];
        !           250: 
        !           251:     if (!savePanelAccessory) {
        !           252:        [self loadNibSection:"SavePanelAccessory.nib" owner:self withNames:NO fromZone:[savepanel zone]];
        !           253:     }
        !           254:     theTitle = cleanTitle([[sender selectedCell] title], title);
        !           255:     if (theTitle) [savepanel setTitle:theTitle];
        !           256:     [savepanel setAccessoryView:savePanelAccessory];
        !           257:     [spamatrix selectCellAt:0 :0];
        !           258:     [savepanel setRequiredFileType:"draw"];
        !           259: 
        !           260:     return savepanel;
        !           261: }
        !           262: 
        !           263: - (SavePanel *)saveAsPanel:sender
        !           264: /*
        !           265:  * Returns a regular SavePanel with "draw" as the required file type.
        !           266:  */
        !           267: {
        !           268:     char title[100];
        !           269:     const char *theTitle;
        !           270:     SavePanel *savepanel = [SavePanel new];
        !           271: 
        !           272:     [savepanel setAccessoryView:nil];
        !           273:     theTitle = cleanTitle([[sender selectedCell] title], title);
        !           274:     if (theTitle) [savepanel setTitle:theTitle];
        !           275:     [savepanel setRequiredFileType:"draw"];
        !           276: 
        !           277:     return savepanel;
        !           278: }
        !           279: 
        !           280: - (GridView *)gridInspector
        !           281: /*
        !           282:  * Returns the application-wide inspector for a document's grid.
        !           283:  * Note that if we haven't yet loaded the GridView panel, we do it.
        !           284:  * The instance variable gridInspector is set in setGridInspector:
        !           285:  * since it is set as an outlet of the owner (self, i.e. DrawApp).
        !           286:  */
        !           287: {
        !           288:     if (!gridInspector) {
        !           289:        NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
        !           290:        NXNameZone(zone, "GridView");
        !           291:        [self loadNibSection:"GridView.nib" owner:self withNames:NO fromZone:zone];
        !           292:        NXMergeZone(zone);
        !           293:     }
        !           294:     return gridInspector;
        !           295: }
        !           296: 
        !           297: - (Panel *)inspectorPanel
        !           298: /*
        !           299:  * Returns the application-wide inspector for Graphics.
        !           300:  */
        !           301: {
        !           302:     if (!inspectorPanel) {
        !           303:        NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
        !           304:        NXNameZone(zone, "Inspector");
        !           305:        [self loadNibSection:"InspectorPanel.nib" owner:self withNames:NO fromZone:zone];
        !           306:        [inspectorPanel setFrameAutosaveName:"Inspector"];
        !           307:        [inspectorPanel setBecomeKeyOnlyIfNeeded:YES];
        !           308:        [[inspectorPanel delegate] preset];
        !           309:        NXMergeZone(zone);
        !           310:     }
        !           311:     return inspectorPanel;
        !           312: }
        !           313: 
        !           314: - (DrawPageLayout *)pageLayout
        !           315: /*
        !           316:  * Returns the application-wide DrawPageLayout panel.
        !           317:  */
        !           318: {
        !           319:     static DrawPageLayout *dpl = nil;
        !           320: 
        !           321:     if (!dpl) {
        !           322:        dpl = [DrawPageLayout new];
        !           323:        [self loadNibSection:"PageLayoutAccessory.nib" owner:dpl withNames:NO fromZone:[dpl zone]];
        !           324:     }
        !           325: 
        !           326:     return dpl;
        !           327: }
        !           328: 
        !           329: - orderFrontInspectorPanel:sender
        !           330: /*
        !           331:  * Creates the inspector panel if it doesn't exist, then orders it front.
        !           332:  */
        !           333: {
        !           334:     [[self inspectorPanel] orderFront:self];
        !           335:     return self;
        !           336: }
        !           337: 
        !           338: /* Setting up the Fax Cover Sheet menu */
        !           339: 
        !           340: #define FAX_NOTE NXLocalString("Notes", NULL, "Fax Cover Sheet item which allows the user to type in any thing he/she wants.")
        !           341: 
        !           342: - setFaxCoverSheetMenu:anObject
        !           343: /*
        !           344:  * This goes through all the entries in CoverSheet.strings and makes
        !           345:  * an entry in the cover sheet menu for them.  This is kind of a kooky
        !           346:  * method, but it makes Draw much more usable as a Fax Cover Sheet
        !           347:  * editor.
        !           348:  */
        !           349: {
        !           350:     Menu *fcsMenu;
        !           351:     MenuCell *cell;
        !           352:     const char *key, *value;
        !           353:     NXStringTable *table;
        !           354:     char path[MAXPATHLEN+1];
        !           355: 
        !           356:     if (fcsMenu = [anObject target]) {
        !           357:        if ([[NXBundle mainBundle] getPath:path forResource:"CoverSheet" ofType:"strings"]) {
        !           358:            if (table = [[[NXStringTable allocFromZone:[self zone]] init] readFromFile:path]) {
        !           359:                NXHashState state = [table initState];
        !           360:                while ([table nextState:&state key:(void **)&key value:(void **)&value]) {
        !           361:                    cell = [fcsMenu addItem:value action:@selector(addLocalizableCoverSheetEntry:) keyEquivalent:0];
        !           362:                    [cell setAltTitle:key];
        !           363:                }
        !           364:                [table free];
        !           365:            }
        !           366:        }
        !           367:        [fcsMenu addItem:FAX_NOTE action:@selector(addCoverSheetEntry:) keyEquivalent:0];
        !           368:     }
        !           369: 
        !           370:     return self;
        !           371: }
        !           372: 
        !           373: /* Target/Action methods */
        !           374: 
        !           375: - info:sender
        !           376: /*
        !           377:  * Brings up the information panel.
        !           378:  */
        !           379: {
        !           380:     char buf[20];
        !           381: 
        !           382:     if (!infoPanel) {
        !           383:        NXZone *zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
        !           384:        NXNameZone(zone, "InfoPanel");
        !           385:        [self loadNibSection:"InfoPanel.nib" owner:self withNames:NO fromZone:zone];
        !           386:        [infoPanel setFrameAutosaveName:"InfoPanel"];
        !           387:        sprintf(buf, "(v%2d)", DrawVersion);
        !           388:        [version setStringValue:buf];
        !           389:        NXMergeZone(zone);
        !           390:     }
        !           391: 
        !           392:     [infoPanel orderFront:self];
        !           393: 
        !           394:     return self;
        !           395: }
        !           396: 
        !           397: #define NO_HELP NXLocalString("Couldn't find any help!  Sorry ...", NULL, "Message given to the user when the help document could not be found.")
        !           398: 
        !           399: - help:sender
        !           400: /*
        !           401:  * Loads up the Help draw document.
        !           402:  * Note the use of NXBundle so that the Help document can be localized.
        !           403:  */
        !           404: {
        !           405:     DrawDocument *document = nil;
        !           406:     char path[MAXPATHLEN+1];
        !           407: 
        !           408:     if ([[NXBundle mainBundle] getPath:path forResource:"Help" ofType:"draw"]) {
        !           409:        if (document = openDocument(path, NO)) {
        !           410:            [document setTemporaryTitle:NXLocalString("Help", NULL, "The title of the help document.")];
        !           411:            [[[document view] window] makeKeyAndOrderFront:self];
        !           412:        }
        !           413:     }
        !           414: 
        !           415:     if (!document) NXRunAlertPanel(NULL, NO_HELP, NULL, NULL, NULL);
        !           416: 
        !           417:     return self;
        !           418: }
        !           419: 
        !           420: - new:sender
        !           421: /*
        !           422:  * Creates a new document--called by pressing New in the Document menu.
        !           423:  */
        !           424: {
        !           425:     [DrawDocument new];
        !           426:     return self;
        !           427: }
        !           428: 
        !           429: - open:sender
        !           430: /*
        !           431:  * Called by pressing Open... in the Window menu.
        !           432:  */
        !           433: {
        !           434:     const char *directory;
        !           435:     const char *const *files;
        !           436:     const char *const drawFileTypes[2] = { "draw", NULL };
        !           437:     OpenPanel *openpanel = [[OpenPanel new] allowMultipleFiles:YES];
        !           438: 
        !           439:     directory = [self currentDirectory];
        !           440:     if (directory && (*directory == '/')) [openpanel setDirectory:directory];
        !           441:     if ([openpanel runModalForTypes:drawFileTypes]) {
        !           442:        files = [openpanel filenames];
        !           443:        directory = [openpanel directory];
        !           444:        while (files && *files) {
        !           445:            if (*files) haveOpenedDocument = openFile(directory, *files, YES) || haveOpenedDocument;
        !           446:            files++;
        !           447:        }
        !           448:     }
        !           449: 
        !           450:     return self;
        !           451: }
        !           452: 
        !           453: - saveAll:sender
        !           454: /*
        !           455:  * Saves all the documents.
        !           456:  */
        !           457: {
        !           458:     int count;
        !           459:     Window *window;
        !           460: 
        !           461:     count = [windowList count];
        !           462:     while (count--) {
        !           463:        window = [windowList objectAt:count];
        !           464:        [documentInWindow(window) save:sender];
        !           465:     }
        !           466: 
        !           467:     return nil;
        !           468: }
        !           469: 
        !           470: #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.")
        !           471: #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.")
        !           472: #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.")
        !           473: #define QUIT NXLocalString("Quit", NULL, "The operation of exiting the application.")
        !           474: 
        !           475: - terminate:sender cancellable:(BOOL)cancellable
        !           476: /*
        !           477:  * Makes sure all documents get an opportunity
        !           478:  * to be saved before exiting the program.  If we are terminating because
        !           479:  * the user logged out of the workspace (or powered off), then we cannot
        !           480:  * give the user the option of cancelling the quit (that's what the
        !           481:  * cancellable flag is for).
        !           482:  */
        !           483: {
        !           484:     int count, choice;
        !           485:     Window *window;
        !           486:     id document;
        !           487: 
        !           488:     count = [windowList count];
        !           489:     while (count--) {
        !           490:        window = [windowList objectAt:count];
        !           491:        document = [window delegate];
        !           492:        if ([document respondsTo:@selector(isDirty)] && [document isDirty]) {
        !           493:            if (cancellable) {
        !           494:                choice = NXRunAlertPanel(QUIT, UNSAVED_DOCUMENTS, REVIEW_UNSAVED, QUIT_ANYWAY, CANCEL);
        !           495:            } else {
        !           496:                choice = NXRunAlertPanel(QUIT, UNSAVED_DOCUMENTS, REVIEW_UNSAVED, QUIT_ANYWAY, NULL);
        !           497:            }
        !           498:            if (choice == NX_ALERTOTHER)  {
        !           499:                return self;
        !           500:            } else if (choice == NX_ALERTDEFAULT) {
        !           501:                count = [windowList count];
        !           502:                while (count--) {
        !           503:                    window = [windowList objectAt:count];
        !           504:                    document = [window delegate];
        !           505:                    if ([document respondsTo:@selector(windowWillClose:cancellable:)]) {
        !           506:                        if (![document windowWillClose:sender cancellable:cancellable] && cancellable) {
        !           507:                            return self;
        !           508:                        } else {
        !           509:                            [document close];
        !           510:                            [window close];
        !           511:                        }
        !           512:                    }
        !           513:                }
        !           514:            }
        !           515:            break;
        !           516:        }
        !           517:     }
        !           518: 
        !           519:     return nil;
        !           520: }
        !           521: 
        !           522: - terminate:sender
        !           523: /*
        !           524:  * Overridden to give user the opportunity to save unsaved files.
        !           525:  */
        !           526: {
        !           527:     if (![self terminate:sender cancellable:YES]) {
        !           528:        return [super terminate:sender];
        !           529:     } else {
        !           530:        return self;
        !           531:     }
        !           532: }
        !           533: 
        !           534: /*
        !           535:  * Application object delegate methods.
        !           536:  * Since we don't have an application delegate, messages that would
        !           537:  * normally be sent there are sent to the Application object itself instead.
        !           538:  */
        !           539: 
        !           540: - appDidInit:(Application *)sender
        !           541: /*
        !           542:  * Makes the tool palette not ever become the key window.
        !           543:  * Check for files to open specified on the command line.
        !           544:  * Initialize the menus.
        !           545:  * If there are no open documents (and we are not being
        !           546:  * launched to service a Services request or otherwise
        !           547:  * being invoked due to interapplication communication),
        !           548:  * then open a blank one.
        !           549:  */
        !           550: {
        !           551:     int i;
        !           552:     Panel *toolWindow;
        !           553: 
        !           554:     toolWindow = [tools window];
        !           555:     [toolWindow setFrameAutosaveName:[toolWindow title]];
        !           556:     [toolWindow setBecomeKeyOnlyIfNeeded:YES];
        !           557:     [toolWindow setFloatingPanel:YES];
        !           558:     [toolWindow orderFront:self];
        !           559: 
        !           560:     if (NXArgc > 1) {
        !           561:        for (i = 1; i < NXArgc; i++) {
        !           562:            haveOpenedDocument = openDocument(NXArgv[i], YES) || haveOpenedDocument;
        !           563:        }
        !           564:     }
        !           565: 
        !           566:     if (!haveOpenedDocument && !NXGetDefaultValue([self appName], "NXServiceLaunch")) [self new:self];
        !           567: 
        !           568:     if (NXGetDefaultValue([self appName], "Quit")) {
        !           569:        [self activateSelf:YES];
        !           570:        NXPing();
        !           571:        [self terminate:nil];
        !           572:     }
        !           573: 
        !           574:     initMenu([NXApp mainMenu]);
        !           575: 
        !           576:     return self;
        !           577: }
        !           578: 
        !           579: - (int)app:sender openFile:(const char *)path type:(const char *)type
        !           580: /*
        !           581:  * This method is performed whenever a user double-clicks on an icon
        !           582:  * in the Workspace Manager representing a Draw program document.
        !           583:  */
        !           584: {
        !           585:     if (type && !strcmp(type, "draw")) {
        !           586:        if (openDocument(path, YES)) {
        !           587:            haveOpenedDocument = YES;
        !           588:            return YES;
        !           589:        }
        !           590:     }
        !           591: 
        !           592:     return NO;
        !           593: }
        !           594: 
        !           595: - (BOOL)appAcceptsAnotherFile:(Application *)sender
        !           596: /*
        !           597:  * We accept any number of appOpenFile:type: messages.
        !           598:  */
        !           599: {
        !           600:     return YES;
        !           601: }
        !           602: 
        !           603: - app:sender powerOffIn:(int)ms andSave:(int)andSave
        !           604: /*
        !           605:  * Give the user a chance to save his documents.
        !           606:  */
        !           607: {
        !           608:     if (andSave) [self terminate:nil cancellable:NO];
        !           609:     return self;
        !           610: }
        !           611: 
        !           612: /* Listener/Speaker remote methods */
        !           613: 
        !           614: /*
        !           615:  * Note that anybody can send these messages to a running version
        !           616:  * of Draw, so we protect the ones that can affect the current
        !           617:  * document by only allowing them to happen if the Draw default
        !           618:  * "RemoteControl" is set.
        !           619:  */
        !           620: 
        !           621: - (int)msgDirectory:(const char **)fullPath ok:(int *)flag
        !           622: {
        !           623:     *fullPath = [self currentDirectory];
        !           624:     if (*fullPath) {
        !           625:        *flag = YES;
        !           626:     } else {
        !           627:        *fullPath = "";
        !           628:        *flag = NO;
        !           629:     }
        !           630:     return 0;
        !           631: }
        !           632: 
        !           633: - (int)msgVersion:(const char **)aString ok:(int *)flag
        !           634: {
        !           635:     char buf[20];
        !           636: 
        !           637:     sprintf(buf, "3.0 (v%2d)", DrawVersion);
        !           638:     *aString = NXCopyStringBuffer(buf);
        !           639:     *flag = YES;
        !           640: 
        !           641:     return 0;
        !           642: }
        !           643: 
        !           644: - (int)msgFile:(const char **)fullPath ok:(int *)flag
        !           645: {
        !           646:     const char *file;
        !           647: 
        !           648:     file = [[self currentDocument] filename];
        !           649:     if (file) {
        !           650:        *fullPath = file;
        !           651:        *flag = YES;
        !           652:     } else {
        !           653:        *fullPath = "";
        !           654:        *flag = NO;
        !           655:     }
        !           656: 
        !           657:     return 0;
        !           658: }
        !           659: 
        !           660: - (BOOL)shouldRunPrintPanel:sender
        !           661: /*
        !           662:  * When NXApp prints, don't bring up the PrintPanel.
        !           663:  */
        !           664: {
        !           665:     return NO;
        !           666: }
        !           667: 
        !           668: - (int)msgPrint:(const char *)fullPath ok:(int *)flag
        !           669: {
        !           670:     BOOL close;
        !           671:     id document = nil;
        !           672: 
        !           673:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           674:        InMsgPrint = YES;
        !           675:        if (document = [findDocument(fullPath) delegate]) {
        !           676:            close = NO;
        !           677:        } else {
        !           678:            document = openDocument(fullPath, NO);
        !           679:            if (document) haveOpenedDocument = YES;
        !           680:            close = YES;
        !           681:        }
        !           682:        if (document && ![[document view] isEmpty]) {
        !           683:            [NXApp setPrintInfo:[document printInfo]];
        !           684:            [[document view] printPSCode:self];
        !           685:            if (close) [[[document view] window] close];
        !           686:            *flag = YES;
        !           687:        } else {
        !           688:            *flag = NO;
        !           689:        }
        !           690:        InMsgPrint = NO;
        !           691:     } else {
        !           692:        *flag = NO;
        !           693:     }
        !           694: 
        !           695:     return 0;
        !           696: }
        !           697: 
        !           698: - (int)msgSelection:(const char **)bytes length:(int *)len asType:(const char *)aType ok:(int *)flag
        !           699: {
        !           700:     int maxlen;
        !           701:     NXStream *stream;
        !           702: 
        !           703:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           704:        if (!strcmp(aType, DrawPboardType)) {
        !           705:            stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           706:            if ([[[self currentDocument] view] copySelectionToStream:stream]) {
        !           707:                NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen);
        !           708:                *flag = YES;
        !           709:            } else {
        !           710:                *flag = NO;
        !           711:            }
        !           712:            NXClose(stream);
        !           713:        } else if (!strcmp(aType, NXPostScriptPboardType)) {
        !           714:            stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           715:            if ([[[self currentDocument] view] copySelectionAsPSToStream:stream]) {
        !           716:                NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen);
        !           717:                *flag = YES;
        !           718:            } else {
        !           719:                *flag = NO;
        !           720:            }
        !           721:            NXClose(stream);
        !           722:        } else if (!strcmp(aType, NXTIFFPboardType)) {
        !           723:            stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
        !           724:            if ([[[self currentDocument] view] copySelectionAsTIFFToStream:stream]) {
        !           725:                NXGetMemoryBuffer(stream, (char **)bytes, len, &maxlen);
        !           726:                *flag = YES;
        !           727:            } else {
        !           728:                *flag = NO;
        !           729:            }
        !           730:            NXClose(stream);
        !           731:        } else {
        !           732:            *flag = NO;
        !           733:        }
        !           734:     } else {
        !           735:        *flag = NO;
        !           736:     }
        !           737:     
        !           738:     if (!*flag) {
        !           739:        *bytes = "";
        !           740:        *len = 0;
        !           741:     }
        !           742: 
        !           743:     return 0;
        !           744: }
        !           745: 
        !           746: - (int)msgCopyAsType:(const char *)aType ok:(int *)flag
        !           747: {
        !           748:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           749:        if (!strcmp(aType, NXPostScriptPboardType) ||
        !           750:            !strcmp(aType, NXTIFFPboardType) ||
        !           751:            !strcmp(aType, DrawPboardType)) {
        !           752:            *flag = ([[[self currentDocument] view] copy:self] ? YES : NO);
        !           753:        } else {
        !           754:            *flag = NO;
        !           755:        }
        !           756:     } else {
        !           757:        *flag = NO;
        !           758:     }
        !           759:     return 0;
        !           760: }
        !           761: 
        !           762: - (int)msgCutAsType:(const char *)aType ok:(int *)flag
        !           763: {
        !           764:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           765:        if (!strcmp(aType, NXPostScriptPboardType) ||
        !           766:            !strcmp(aType, NXTIFFPboardType) ||
        !           767:            !strcmp(aType, DrawPboardType)) {
        !           768:            *flag = ([[[self currentDocument] view] cut:self] ? YES : NO);
        !           769:        } else {
        !           770:            *flag = NO;
        !           771:        }
        !           772:     } else {
        !           773:        *flag = NO;
        !           774:     }
        !           775:     return 0;
        !           776: }
        !           777: 
        !           778: - (int)msgPaste:(int *)flag;
        !           779: {
        !           780:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           781:        *flag = ([[[self currentDocument] view] paste:self] ? YES : NO);
        !           782:     } else {
        !           783:        *flag = NO;
        !           784:     }
        !           785:     return 0;
        !           786: }
        !           787: 
        !           788: - (int)msgQuit:(int *)flag
        !           789: {
        !           790:     if (NXGetDefaultValue("Draw", "RemoteControl")) {
        !           791:        *flag = ([self terminate:self cancellable:YES] ? NO : YES);
        !           792:        if (*flag) [NXApp perform:@selector(terminate:) with:nil afterDelay:1 cancelPrevious:YES];
        !           793:     } else {
        !           794:        *flag = NO;
        !           795:     }
        !           796:     return 0;
        !           797: }
        !           798: 
        !           799: /* Global cursor setting */
        !           800: 
        !           801: - cursor
        !           802: /*
        !           803:  * This is called by DrawDocument objects who want to set the cursor
        !           804:  * depending on what the currently selected tool is (as well as on whether
        !           805:  * the Control key has been pressed indicating that the select tool is
        !           806:  * temporarily set--see sendEvent:).
        !           807:  */
        !           808: {
        !           809:     id theCursor = nil;
        !           810:     if (!cursorPushed) theCursor = [[self currentGraphic] cursor];
        !           811:     return theCursor ? theCursor : NXArrow;
        !           812: }
        !           813: 
        !           814: - sendEvent:(NXEvent *)event
        !           815: /*
        !           816:  * We override this because we need to find out when the control key is down
        !           817:  * so we can set the arrow cursor so the user knows she is (temporarily) in
        !           818:  * select mode.
        !           819:  */
        !           820: {
        !           821:     if (event && event->type < NX_KITDEFINED) {        /* mouse or keyboard event */
        !           822:        if (event->flags & NX_CONTROLMASK) {
        !           823:            if (!cursorPushed && currentGraphic) {
        !           824:                cursorPushed = YES;
        !           825:                [[self currentDocument] resetCursor];
        !           826:            }
        !           827:        } else if (cursorPushed) {
        !           828:            cursorPushed = NO;
        !           829:            [[self currentDocument] resetCursor];
        !           830:        }
        !           831:     }
        !           832: 
        !           833:     return [super sendEvent:event];
        !           834: }
        !           835: 
        !           836: /* Automatic update methods */
        !           837: 
        !           838: - (BOOL)menuItemUpdate:(MenuCell *)menuCell
        !           839: /*
        !           840:  * Method called by all menu items which send their actions to the
        !           841:  * First Responder.  First, if the object which would respond were the
        !           842:  * action sent down the responder chain also responds to the message
        !           843:  * validateCommand:, then it is sent validateCommand: to determine
        !           844:  * whether that command is valid now, otherwise, if there is a responder
        !           845:  * to the message, then it is assumed that the item is valid.
        !           846:  * The method returns YES if the cell has changed its appearance (so that
        !           847:  * the caller (a Menu) knows to redraw it).
        !           848:  */
        !           849: {
        !           850:     SEL action;
        !           851:     id responder, target;
        !           852:     BOOL enable;
        !           853: 
        !           854:     target = [menuCell target];
        !           855:     enable = [menuCell isEnabled];
        !           856: 
        !           857:     if (!target) {
        !           858:        action = [menuCell action];
        !           859:        responder = [self calcTargetForAction:action];
        !           860:        if ([responder respondsTo:@selector(validateCommand:)]) {
        !           861:            enable = [responder validateCommand:menuCell];
        !           862:        } else {
        !           863:            enable = responder ? YES : NO;
        !           864:        }
        !           865:     }
        !           866: 
        !           867:     if ([menuCell isEnabled] != enable) {
        !           868:        [menuCell setEnabled:enable];
        !           869:        return YES;
        !           870:     } else {
        !           871:        return NO;
        !           872:     }
        !           873: }
        !           874: 
        !           875: - (BOOL)validateCommand:(MenuCell *)menuCell
        !           876: /*
        !           877:  * The only command DrawApp itself controls is saveAll:.
        !           878:  * Save All is enabled only if there are any documents open.
        !           879:  */
        !           880: {
        !           881:     SEL action = [menuCell action];
        !           882: 
        !           883:     if (action == @selector(saveAll:)) {
        !           884:        return findDocument(NULL) ? YES : NO;
        !           885:     }
        !           886: 
        !           887:     return YES;
        !           888: }
        !           889: 
        !           890: /*
        !           891:  * This is a very funky method and tricks of this sort are not generally
        !           892:  * recommended, but this hack is done so that new Graphic subclasses can
        !           893:  * be added to the program without changing even one line of code (except,
        !           894:  * of course, to implement the subclass itself).
        !           895:  *
        !           896:  * The objective-C method objc_lookUpClass() is used to find the factory object
        !           897:  * corresponding to the name of the icon of the cell sending the setCurrentGraphic:
        !           898:  * message.
        !           899:  *
        !           900:  * Again, this is not recommended procedure, but it illustrates how
        !           901:  * objective-C can be used to make some funky runtime dependent decisions.
        !           902:  */
        !           903: 
        !           904: - setCurrentGraphic:sender
        !           905: /*
        !           906:  * The sender's icon is queried.  If that name corresponds to the name
        !           907:  * of a class, then that class is set as the currentGraphic.  If not,
        !           908:  * then the select tool is put into effect.
        !           909:  */
        !           910: {
        !           911:     id cell;
        !           912:     const char *className;
        !           913: 
        !           914:     if (cell = [sender selectedCell]) {
        !           915:        if (className = [cell icon]) {
        !           916:            currentGraphic = objc_lookUpClass(className);
        !           917:        } else {
        !           918:            currentGraphic = nil;
        !           919:        }
        !           920:        if (!currentGraphic) [tools selectCellAt:0 :0];
        !           921:        [[self currentDocument] resetCursor];
        !           922:     }
        !           923: 
        !           924:     return self;
        !           925: }
        !           926: 
        !           927: @end

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.