Annotation of Examples/AppKit/Draw/DrawApp.m, revision 1.1.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.