|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.