|
|
1.1 ! root 1: #import "draw.h" ! 2: ! 3: @implementation Graphic : Object ! 4: ! 5: static int KNOB_WIDTH = 0.0; ! 6: static int KNOB_HEIGHT = 0.0; ! 7: ! 8: #define MINSIZE 5.0 /* minimum size of a Graphic */ ! 9: ! 10: NXCursor *CrossCursor = nil; /* global since subclassers may need it */ ! 11: ! 12: /* Optimization method. */ ! 13: ! 14: /* ! 15: * The fastKnobFill optimization just keeps a list of black and dark gray ! 16: * rectangles (the knobbies are made out of black and dark gray rectangles) ! 17: * and emits them in a single NXRectFillList() which is much faster than ! 18: * doing individual rectfills (we also save the repeated setgrays). ! 19: */ ! 20: ! 21: static NXRect *blackRectList = NULL; ! 22: static int blackRectSize = 0; ! 23: static int blackRectCount = 0; ! 24: static NXRect *dkgrayRectList = NULL; ! 25: static int dkgrayRectSize = 0; ! 26: static int dkgrayRectCount = 0; ! 27: ! 28: + fastKnobFill:(const NXRect *)aRect isBlack:(BOOL)isBlack ! 29: { ! 30: if (!aRect) return self; ! 31: ! 32: if (isBlack) { ! 33: if (!blackRectList) { ! 34: blackRectSize = 16; ! 35: NX_ZONEMALLOC([NXApp zone], blackRectList, NXRect, blackRectSize); ! 36: } else { ! 37: while (blackRectCount >= blackRectSize) blackRectSize <<= 1; ! 38: NX_ZONEREALLOC([NXApp zone], blackRectList, NXRect, blackRectSize); ! 39: } ! 40: blackRectList[blackRectCount++] = *aRect; ! 41: } else { ! 42: if (!dkgrayRectList) { ! 43: dkgrayRectSize = 16; ! 44: NX_ZONEMALLOC([NXApp zone], dkgrayRectList, NXRect, dkgrayRectSize); ! 45: } else { ! 46: while (dkgrayRectCount >= dkgrayRectSize) dkgrayRectSize <<= 1; ! 47: NX_ZONEREALLOC([NXApp zone], dkgrayRectList, NXRect, dkgrayRectSize); ! 48: } ! 49: dkgrayRectList[dkgrayRectCount++] = *aRect; ! 50: } ! 51: ! 52: return self; ! 53: } ! 54: ! 55: + showFastKnobFills ! 56: { ! 57: PSsetgray(NX_BLACK); ! 58: NXRectFillList(blackRectList, blackRectCount); ! 59: PSsetgray(NX_DKGRAY); ! 60: NXRectFillList(dkgrayRectList, dkgrayRectCount); ! 61: blackRectCount = 0; ! 62: dkgrayRectCount = 0; ! 63: return self; ! 64: } ! 65: ! 66: /* Factory methods. */ ! 67: ! 68: + initialize ! 69: /* ! 70: * This sets the class version so that we can compatibly read ! 71: * old Graphic objects out of an archive. ! 72: */ ! 73: { ! 74: [Graphic setVersion:3]; ! 75: return self; ! 76: } ! 77: ! 78: + (BOOL)isEditable ! 79: /* ! 80: * Any Graphic which can be edited should return YES from this ! 81: * and its instances should do something in the response to the ! 82: * edit:in: method. ! 83: */ ! 84: { ! 85: return NO; ! 86: } ! 87: ! 88: + cursor ! 89: /* ! 90: * Any Graphic that doesn't have a special cursor gets the default cross. ! 91: */ ! 92: { ! 93: NXPoint spot; ! 94: ! 95: if (!CrossCursor) { ! 96: CrossCursor = [NXCursor newFromImage:[NXImage newFromSection:"Cross.tiff"]]; ! 97: spot.x = 7.0; spot.y = 7.0; ! 98: [CrossCursor setHotSpot:&spot]; ! 99: } ! 100: ! 101: return CrossCursor; ! 102: } ! 103: ! 104: static void initClassVars() ! 105: { ! 106: const char *value; ! 107: NXCoord w = 2.0, h = 2.0; ! 108: ! 109: if (!KNOB_WIDTH) { ! 110: value = NXGetDefaultValue([NXApp appName], "KnobWidth"); ! 111: if (value) w = floor(atof(value) / 2.0); ! 112: value = NXGetDefaultValue([NXApp appName], "KnobHeight"); ! 113: if (value) h = floor(atof(value) / 2.0); ! 114: w = MAX(w, 1.0); h = MAX(h, 1.0); ! 115: KNOB_WIDTH = w * 2.0 + 1.0; /* size must be odd */ ! 116: KNOB_HEIGHT = h * 2.0 + 1.0; ! 117: } ! 118: } ! 119: ! 120: /* ! 121: * The currentGraphicIdentifier is a number that is kept unique for a given ! 122: * Draw document by being monotonically increasing and is bumped each time a ! 123: * new Graphic is created. The method of the same name is used during the ! 124: * archiving of a Draw document to write out what the number is at save-time. ! 125: * updateCurrentGraphicIdentifer: is used at document load time to reset ! 126: * the number to that level (if it's already higher, then we don't need to ! 127: * bump it). ! 128: */ ! 129: ! 130: static int currentGraphicIdentifier = 1; ! 131: ! 132: + (int)currentGraphicIdentifier ! 133: { ! 134: return currentGraphicIdentifier; ! 135: } ! 136: ! 137: + updateCurrentGraphicIdentifier:(int)newMaxIdentifier ! 138: { ! 139: if (newMaxIdentifier > currentGraphicIdentifier) currentGraphicIdentifier = newMaxIdentifier; ! 140: return self; ! 141: } ! 142: ! 143: - init ! 144: { ! 145: [super init]; ! 146: gFlags.active = YES; ! 147: gFlags.selected = YES; ! 148: initClassVars(); ! 149: identifier = currentGraphicIdentifier++; ! 150: return self; ! 151: } ! 152: ! 153: - awake ! 154: { ! 155: initClassVars(); ! 156: return [super awake]; ! 157: } ! 158: ! 159: /* Private C functions and macros used to implement methods in this class. */ ! 160: ! 161: static void drawKnobs(const NXRect *rect, int cornerMask, BOOL black) ! 162: /* ! 163: * Draws either the knobs or their shadows (not both). ! 164: */ ! 165: { ! 166: NXRect knob; ! 167: NXCoord dx, dy; ! 168: BOOL oddx, oddy; ! 169: ! 170: knob = *rect; ! 171: dx = knob.size.width / 2.0; ! 172: dy = knob.size.height / 2.0; ! 173: oddx = (floor(dx) != dx); ! 174: oddy = (floor(dy) != dy); ! 175: knob.size.width = KNOB_WIDTH; ! 176: knob.size.height = KNOB_HEIGHT; ! 177: knob.origin.x -= ((KNOB_WIDTH - 1.0) / 2.0); ! 178: knob.origin.y -= ((KNOB_HEIGHT - 1.0) / 2.0); ! 179: ! 180: if (cornerMask & LOWER_LEFT_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 181: knob.origin.y += dy; ! 182: if (oddy) knob.origin.y -= 0.5; ! 183: if (cornerMask & LEFT_SIDE_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 184: knob.origin.y += dy; ! 185: if (oddy) knob.origin.y += 0.5; ! 186: if (cornerMask & UPPER_LEFT_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 187: knob.origin.x += dx; ! 188: if (oddx) knob.origin.x -= 0.5; ! 189: if (cornerMask & TOP_SIDE_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 190: knob.origin.x += dx; ! 191: if (oddx) knob.origin.x += 0.5; ! 192: if (cornerMask & UPPER_RIGHT_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 193: knob.origin.y -= dy; ! 194: if (oddy) knob.origin.y -= 0.5; ! 195: if (cornerMask & RIGHT_SIDE_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 196: knob.origin.y -= dy; ! 197: if (oddy) knob.origin.y += 0.5; ! 198: if (cornerMask & LOWER_RIGHT_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 199: knob.origin.x -= dx; ! 200: if (oddx) knob.origin.x += 0.5; ! 201: if (cornerMask & BOTTOM_SIDE_MASK) [Graphic fastKnobFill:&knob isBlack:black]; ! 202: } ! 203: ! 204: /* Private methods sometimes overridden by subclassers */ ! 205: ! 206: - setGraphicsState ! 207: /* ! 208: * Emits a gsave, must be balanced by grestore. ! 209: */ ! 210: { ! 211: PSSetParameters(gFlags.linecap, gFlags.linejoin, linewidth); ! 212: return self; ! 213: } ! 214: ! 215: - setLineColor ! 216: { ! 217: if (lineColor) { ! 218: NXSetColor(*lineColor); ! 219: } else { ! 220: NXSetColor(NX_COLORBLACK); ! 221: } ! 222: return self; ! 223: } ! 224: ! 225: - setFillColor ! 226: { ! 227: if (fillColor) NXSetColor(*fillColor); ! 228: return self; ! 229: } ! 230: ! 231: - (int)cornerMask ! 232: /* ! 233: * Returns a mask of the corners which should have a knobby in them. ! 234: */ ! 235: { ! 236: return ALL_CORNERS; ! 237: } ! 238: ! 239: /* Data link methods -- see Links.rtf and gvLinks.m for more info */ ! 240: ! 241: /* ! 242: * Most Graphics aren't linked (i.e. their visual display is ! 243: * not determined by some other document). See Image and ! 244: * TextGraphic for examples of Graphics that sometimes do. ! 245: */ ! 246: ! 247: - setLink:(NXDataLink *)aLink ! 248: { ! 249: return nil; ! 250: } ! 251: ! 252: - (NXDataLink *)link ! 253: { ! 254: return nil; ! 255: } ! 256: ! 257: - (Graphic *)graphicLinkedBy:(NXDataLink *)aLink ! 258: /* ! 259: * The reason we implement this method (instead of just relying on ! 260: * saying if ([graphic link] == aLink)) is for the sake of Group ! 261: * objects which may have a linked Graphic embedded in them. ! 262: */ ! 263: { ! 264: NXDataLink *link = [self link]; ! 265: ! 266: if (link) { ! 267: if (!aLink) { /* !aLink means any link */ ! 268: return ([link disposition] != NX_LinkBroken) ? self : nil; ! 269: } else { ! 270: return (aLink == link) ? self : nil; ! 271: } ! 272: } ! 273: ! 274: return nil; ! 275: } ! 276: ! 277: - reviveLink:(NXDataLinkManager *)linkManager ! 278: /* ! 279: * We never archive link information (but, of course, the unique identifer ! 280: * is always archived with a Graphic). Thus, when our document is reloaded, ! 281: * we just asked the NXDataLinkManager which NXDataLink object is associated ! 282: * with the NXSelection which represents this Graphic. ! 283: */ ! 284: { ! 285: if (![self link]) [self setLink:[linkManager findDestinationLinkWithSelection:[self selection]]]; ! 286: return self; ! 287: } ! 288: ! 289: - (NXSelection *)selection ! 290: /* ! 291: * Just creates an NXSelection "bag o' bits" with our unique identifier in it. ! 292: */ ! 293: { ! 294: char buffer[200]; ! 295: sprintf(buffer, "%d %d", ByGraphic, [self identifier]); ! 296: return [[NXSelection allocFromZone:[self zone]] initWithDescription:buffer length:strlen(buffer)+1]; ! 297: } ! 298: ! 299: - (BOOL)mightBeLinked ! 300: /* ! 301: * This is set whenever our Graphic has a link set in it. ! 302: * It is never cleared. ! 303: * We use it during copy/paste to determine whether we have ! 304: * to check with the data link manager to possibly reestablish ! 305: * a link to this object. ! 306: */ ! 307: { ! 308: return gFlags.mightBeLinked; ! 309: } ! 310: ! 311: - readLinkFromPasteboard:(Pasteboard *)pboard usingManager:(NXDataLinkManager *)linkManager useNewIdentifier:(BOOL)useNewIdentifier ! 312: /* ! 313: * This is called by pasteFromPasteboard: when we paste a Graphic (i.e. copied/pasted from ! 314: * another Draw document) in case that Graphic was linked to something when it was copied. ! 315: * Since we called writeLinksToPasteboard: when we put the Graphic into the pasteboard (see ! 316: * writeLinkToPasteboard:types: above) we can simply retrieve all the link information for ! 317: * that graphic by using the linkManager method addLinkPreviouslyAt:fromPasteboard:at:. ! 318: */ ! 319: { ! 320: NXDataLink *link; ! 321: NXSelection *oldSelection, *newSelection; ! 322: ! 323: oldSelection = [self selection]; ! 324: if (linkManager && oldSelection) { ! 325: if (useNewIdentifier) [self resetIdentifier]; ! 326: newSelection = [self selection]; ! 327: link = [linkManager addLinkPreviouslyAt:oldSelection ! 328: fromPasteboard:pboard ! 329: at:newSelection]; ! 330: if (!link) [newSelection free]; ! 331: [self setLink:link]; ! 332: } ! 333: if (useNewIdentifier) [oldSelection free]; ! 334: ! 335: return self; ! 336: } ! 337: ! 338: /* Notification messages */ ! 339: ! 340: /* ! 341: * These methods are sent when a Graphic is added to or removed ! 342: * from a GraphicView (respectively). Currently we only use them ! 343: * to break and reestablish links if any. ! 344: */ ! 345: ! 346: - wasAddedTo:(GraphicView *)sender ! 347: { ! 348: NXDataLink *link; ! 349: NXDataLinkManager *linkManager; ! 350: ! 351: if ((linkManager = [sender linkManager]) && (link = [self link])) { ! 352: if ([link disposition] == NX_LinkBroken) { ! 353: [linkManager addLink:link at:[self selection]]; ! 354: } ! 355: } ! 356: ! 357: return self; ! 358: } ! 359: ! 360: - wasRemovedFrom:(GraphicView *)sender ! 361: { ! 362: [[self link] break]; ! 363: return self; ! 364: } ! 365: ! 366: /* Methods for uniquely identifying a Graphic. */ ! 367: ! 368: - resetIdentifier ! 369: { ! 370: identifier = currentGraphicIdentifier++; ! 371: return self; ! 372: } ! 373: ! 374: - writeIdentifierTo:(char *)buffer ! 375: /* ! 376: * This method is necessary to support a Group which never writes out ! 377: * its own identifier, but, instead has its components each write out ! 378: * their own identifier. ! 379: */ ! 380: { ! 381: sprintf(buffer, "%d", identifier); ! 382: return self; ! 383: } ! 384: ! 385: - (int)identifier ! 386: { ! 387: return identifier; ! 388: } ! 389: ! 390: - (Graphic *)graphicIdentifiedBy:(int)anIdentifier ! 391: { ! 392: return (identifier == anIdentifier) ? self : nil; ! 393: } ! 394: ! 395: /* Event handling */ ! 396: ! 397: - (BOOL)handleEvent:(NXEvent *)event at:(const NXPoint *)p inView:(View *)view ! 398: /* ! 399: * Currently the only Graphic's that handle events are Image Graphic's that ! 400: * are linked to something else (they follow the link on double-click and ! 401: * the track the mouse for link buttons, for example). This method should ! 402: * return YES only if it tracked the mouse until it went up. ! 403: */ ! 404: { ! 405: return NO; ! 406: } ! 407: ! 408: /* Number of Graphics this Graphic represents (always 1 for non-Group). */ ! 409: ! 410: - (int)graphicCount ! 411: /* ! 412: * This is really only here to support Groups. It is used by the ! 413: * Object Link stuff purely to know how much space will be needed to ! 414: * create an NXSelection with the unique identifiers of all the objects ! 415: * in the selection. ! 416: */ ! 417: { ! 418: return 1; ! 419: } ! 420: ! 421: /* Public routines mostly called by GraphicView's. */ ! 422: ! 423: - (const char *)title ! 424: { ! 425: return NXLoadLocalStringFromTableInBundle(NULL, nil, [self name], NULL); ! 426: } ! 427: ! 428: - (BOOL)isSelected ! 429: { ! 430: return gFlags.selected; ! 431: } ! 432: ! 433: - (BOOL)isActive ! 434: { ! 435: return gFlags.active; ! 436: } ! 437: ! 438: - (BOOL)isLocked ! 439: { ! 440: return gFlags.locked; ! 441: } ! 442: ! 443: - select ! 444: { ! 445: gFlags.selected = YES; ! 446: return self; ! 447: } ! 448: ! 449: - deselect ! 450: { ! 451: gFlags.selected = NO; ! 452: return self; ! 453: } ! 454: ! 455: - activate ! 456: /* ! 457: * Activation is used to *temporarily* take a Graphic out of the GraphicView. ! 458: */ ! 459: { ! 460: gFlags.active = YES; ! 461: return self; ! 462: } ! 463: ! 464: - deactivate ! 465: { ! 466: gFlags.active = NO; ! 467: return self; ! 468: } ! 469: ! 470: - lock ! 471: /* ! 472: * A locked graphic cannot be selected, resized or moved. ! 473: */ ! 474: { ! 475: gFlags.locked = YES; ! 476: return self; ! 477: } ! 478: ! 479: - unlock ! 480: { ! 481: gFlags.locked = NO; ! 482: return self; ! 483: } ! 484: ! 485: /* See TextGraphic for more info about form entries. */ ! 486: ! 487: - (BOOL)isFormEntry ! 488: { ! 489: return NO; ! 490: } ! 491: ! 492: - setFormEntry:(int)flag ! 493: { ! 494: return self; ! 495: } ! 496: ! 497: - (BOOL)hasFormEntries ! 498: { ! 499: return NO; ! 500: } ! 501: ! 502: - (BOOL)writeFormEntryToStream:(NXStream *)stream ! 503: { ! 504: return NO; ! 505: } ! 506: ! 507: /* See Group and Image for more info about cacheability. */ ! 508: ! 509: - setCacheable:(BOOL)flag ! 510: { ! 511: return self; ! 512: } ! 513: ! 514: - (BOOL)isCacheable ! 515: { ! 516: return YES; ! 517: } ! 518: ! 519: /* Getting and setting the bounds. */ ! 520: ! 521: - getBounds:(NXRect *)theRect ! 522: { ! 523: *theRect = bounds; ! 524: return self; ! 525: } ! 526: ! 527: - setBounds:(const NXRect *)aRect ! 528: { ! 529: bounds = *aRect; ! 530: return self; ! 531: } ! 532: ! 533: - (NXRect *)getExtendedBounds:(NXRect *)theRect ! 534: /* ! 535: * Returns, by reference, the rectangle which encloses the Graphic ! 536: * AND ITS KNOBBIES and its increased line width (if appropriate). ! 537: */ ! 538: { ! 539: if (bounds.size.width < 0.0) { ! 540: theRect->origin.x = bounds.origin.x + bounds.size.width; ! 541: theRect->size.width = - bounds.size.width; ! 542: } else { ! 543: theRect->origin.x = bounds.origin.x; ! 544: theRect->size.width = bounds.size.width; ! 545: } ! 546: if (bounds.size.height < 0.0) { ! 547: theRect->origin.y = bounds.origin.y + bounds.size.height; ! 548: theRect->size.height = - bounds.size.height; ! 549: } else { ! 550: theRect->origin.y = bounds.origin.y; ! 551: theRect->size.height = bounds.size.height; ! 552: } ! 553: ! 554: theRect->size.width = MAX(1.0, theRect->size.width); ! 555: theRect->size.height = MAX(1.0, theRect->size.height); ! 556: ! 557: NXInsetRect(theRect, - ((KNOB_WIDTH - 1.0) + linewidth + 1.0), ! 558: - ((KNOB_HEIGHT - 1.0) + linewidth + 1.0)); ! 559: ! 560: if (gFlags.arrow) { ! 561: if (linewidth) { ! 562: NXInsetRect(theRect, - linewidth * 2.5, - linewidth * 2.5); ! 563: } else { ! 564: NXInsetRect(theRect, - 13.0, - 13.0); ! 565: } ! 566: } ! 567: ! 568: NXIntegralRect(theRect); ! 569: ! 570: return theRect; ! 571: } ! 572: ! 573: - (int)knobHit:(const NXPoint *)p ! 574: /* ! 575: * Returns 0 if point is in bounds, and Graphic isOpaque, and no knobHit. ! 576: * Returns -1 if outside bounds or not opaque or not active. ! 577: * Returns corner number if there is a hit on a corner. ! 578: * We have to be careful when the bounds are off an odd size since the ! 579: * knobs on the sides are one pixel larger. ! 580: */ ! 581: { ! 582: NXRect eb; ! 583: NXRect knob; ! 584: NXCoord dx, dy; ! 585: BOOL oddx, oddy; ! 586: int cornerMask = [self cornerMask]; ! 587: ! 588: [self getExtendedBounds:&eb]; ! 589: ! 590: if (!gFlags.active) { ! 591: return -1; ! 592: } else if (!gFlags.selected) { ! 593: return (NXMouseInRect(p, &bounds, NO) && [self isOpaque]) ? 0 : -1; ! 594: } else { ! 595: if (!NXMouseInRect(p, &eb, NO)) return -1; ! 596: } ! 597: ! 598: knob = bounds; ! 599: dx = knob.size.width / 2.0; ! 600: dy = knob.size.height / 2.0; ! 601: oddx = (floor(dx) != dx); ! 602: oddy = (floor(dy) != dy); ! 603: knob.size.width = KNOB_WIDTH; ! 604: knob.size.height = KNOB_HEIGHT; ! 605: knob.origin.x -= ((KNOB_WIDTH - 1.0) / 2.0); ! 606: knob.origin.y -= ((KNOB_HEIGHT - 1.0) / 2.0); ! 607: ! 608: if ((cornerMask & LOWER_LEFT_MASK) && NXMouseInRect(p, &knob, NO)) ! 609: return(LOWER_LEFT); ! 610: knob.origin.y += dy; ! 611: if (oddy) knob.origin.y -= 0.5; ! 612: if ((cornerMask & LEFT_SIDE_MASK) && NXMouseInRect(p, &knob, NO)) ! 613: return(LEFT_SIDE); ! 614: knob.origin.y += dy; ! 615: if (oddy) knob.origin.y += 0.5; ! 616: if ((cornerMask & UPPER_LEFT_MASK) && NXMouseInRect(p, &knob, NO)) ! 617: return(UPPER_LEFT); ! 618: knob.origin.x += dx; ! 619: if (oddx) knob.origin.x -= 0.5; ! 620: if ((cornerMask & TOP_SIDE_MASK) && NXMouseInRect(p, &knob, NO)) ! 621: return(TOP_SIDE); ! 622: knob.origin.x += dx; ! 623: if (oddx) knob.origin.x += 0.5; ! 624: if ((cornerMask & UPPER_RIGHT_MASK) && NXMouseInRect(p, &knob, NO)) ! 625: return(UPPER_RIGHT); ! 626: knob.origin.y -= dy; ! 627: if (oddy) knob.origin.y -= 0.5; ! 628: if ((cornerMask & RIGHT_SIDE_MASK) && NXMouseInRect(p, &knob, NO)) ! 629: return(RIGHT_SIDE); ! 630: knob.origin.y -= dy; ! 631: if (oddy) knob.origin.y += 0.5; ! 632: if ((cornerMask & LOWER_RIGHT_MASK) && NXMouseInRect(p, &knob, NO)) ! 633: return(LOWER_RIGHT); ! 634: knob.origin.x -= dx; ! 635: if (oddx) knob.origin.x += 0.5; ! 636: if ((cornerMask & BOTTOM_SIDE_MASK) && NXMouseInRect(p, &knob, NO)) ! 637: return(BOTTOM_SIDE); ! 638: ! 639: return NXMouseInRect(p, &bounds, NO) ? ([self isOpaque] ? 0 : -1) : -1; ! 640: } ! 641: ! 642: /* This method is analogous to display (not drawSelf::) in View. */ ! 643: ! 644: - draw:(const NXRect *)rect ! 645: /* ! 646: * Draws the graphic inside rect. If rect is NULL, then it draws the ! 647: * entire Graphic. If the Graphic is not intersected by rect, then it ! 648: * is not drawn at all. If the Graphic is selected, it is drawn with ! 649: * its knobbies. This method is not intended to be overridden. It ! 650: * calls the overrideable method "draw" which doesn't have to worry ! 651: * about drawing the knobbies. ! 652: * ! 653: * Note the showFastKnobFills optimization here. If this Graphic is ! 654: * opaque then there is a possibility that it might obscure knobbies ! 655: * of Graphics underneath it, so we must emit the cached rectfills ! 656: * before drawing this Graphic. ! 657: */ ! 658: { ! 659: NXRect r; ! 660: ! 661: [self getExtendedBounds:&r]; ! 662: if (gFlags.active && (!rect || NXIntersectsRect(rect, &r))) { ! 663: if ([self isOpaque]) [Graphic showFastKnobFills]; ! 664: [self setGraphicsState]; /* does a gsave */ ! 665: [self draw]; ! 666: PSgrestore(); /* so we need a grestore here */ ! 667: if (NXDrawingStatus == NX_DRAWING) { ! 668: if (gFlags.selected) { ! 669: r.origin.x = floor(bounds.origin.x); ! 670: r.origin.y = floor(bounds.origin.y); ! 671: r.size.width = floor(bounds.origin.x + bounds.size.width + 0.99) - r.origin.x; ! 672: r.size.height = floor(bounds.origin.y + bounds.size.height + 0.99) - r.origin.y; ! 673: r.origin.x += 1.0; ! 674: r.origin.y -= 1.0; ! 675: drawKnobs(&r, [self cornerMask], YES); /* shadows */ ! 676: r.origin.x = floor(bounds.origin.x); ! 677: r.origin.y = floor(bounds.origin.y); ! 678: r.size.width = floor(bounds.origin.x + bounds.size.width + 0.99) - r.origin.x; ! 679: r.size.height = floor(bounds.origin.y + bounds.size.height + 0.99) - r.origin.y; ! 680: drawKnobs(&r, [self cornerMask], NO); /* knobs */ ! 681: } ! 682: } ! 683: return self; ! 684: } ! 685: ! 686: return nil; ! 687: } ! 688: ! 689: /* ! 690: * Returns whether this Graphic can emit, all by itself, fully ! 691: * encapsulated PostScript (or fully conforming TIFF) representing ! 692: * itself. This is an optimization for copy/paste. ! 693: */ ! 694: ! 695: - (BOOL)canEmitEPS ! 696: { ! 697: return NO; ! 698: } ! 699: ! 700: - (BOOL)canEmitTIFF ! 701: { ! 702: return NO; ! 703: } ! 704: ! 705: /* Sizing, aligning and moving. */ ! 706: ! 707: - moveLeftEdgeTo:(const NXCoord *)x ! 708: { ! 709: bounds.origin.x = *x; ! 710: return self; ! 711: } ! 712: ! 713: - moveRightEdgeTo:(const NXCoord *)x ! 714: { ! 715: bounds.origin.x = *x - bounds.size.width; ! 716: return self; ! 717: } ! 718: ! 719: - moveTopEdgeTo:(const NXCoord *)y ! 720: { ! 721: bounds.origin.y = *y - bounds.size.height; ! 722: return self; ! 723: } ! 724: ! 725: - moveBottomEdgeTo:(const NXCoord *)y ! 726: { ! 727: bounds.origin.y = *y; ! 728: return self; ! 729: } ! 730: ! 731: - moveHorizontalCenterTo:(const NXCoord *)x ! 732: { ! 733: bounds.origin.x = *x - floor(bounds.size.width / 2.0); ! 734: return self; ! 735: } ! 736: ! 737: - moveVerticalCenterTo:(const NXCoord *)y ! 738: { ! 739: bounds.origin.y = *y - floor(bounds.size.height / 2.0); ! 740: return self; ! 741: } ! 742: ! 743: - (NXCoord)baseline ! 744: { ! 745: return 0.0; ! 746: } ! 747: ! 748: - moveBaselineTo:(const NXCoord *)y ! 749: { ! 750: return self; ! 751: } ! 752: ! 753: - moveBy:(const NXPoint *)offset ! 754: { ! 755: bounds.origin.x += floor(offset->x); ! 756: bounds.origin.y += floor(offset->y); ! 757: return self; ! 758: } ! 759: ! 760: - moveTo:(const NXPoint *)p ! 761: { ! 762: bounds.origin.x = floor(p->x); ! 763: bounds.origin.y = floor(p->y); ! 764: return self; ! 765: } ! 766: ! 767: - centerAt:(const NXPoint *)p ! 768: { ! 769: bounds.origin.x = floor(p->x - bounds.size.width / 2.0); ! 770: bounds.origin.y = floor(p->y - bounds.size.height / 2.0); ! 771: return self; ! 772: } ! 773: ! 774: - sizeTo:(const NXSize *)size ! 775: { ! 776: bounds.size.width = floor(size->width); ! 777: bounds.size.height = floor(size->height); ! 778: return self; ! 779: } ! 780: ! 781: - sizeToNaturalAspectRatio ! 782: { ! 783: return [self constrainCorner:UPPER_RIGHT toAspectRatio:[self naturalAspectRatio]]; ! 784: } ! 785: ! 786: - sizeToGrid:(GraphicView *)graphicView ! 787: { ! 788: NXPoint p; ! 789: ! 790: [graphicView grid:&bounds.origin]; ! 791: p.x = bounds.origin.x + bounds.size.width; ! 792: p.y = bounds.origin.y + bounds.size.height; ! 793: [graphicView grid:&p]; ! 794: bounds.size.width = p.x - bounds.origin.x; ! 795: bounds.size.height = p.y - bounds.origin.y; ! 796: ! 797: return self; ! 798: } ! 799: ! 800: - alignToGrid:(GraphicView *)graphicView ! 801: { ! 802: [graphicView grid:&bounds.origin]; ! 803: return self; ! 804: } ! 805: ! 806: /* Compatibility method for old PSGraphic and Tiff classes. */ ! 807: ! 808: - replaceWithImage ! 809: { ! 810: return self; ! 811: } ! 812: ! 813: /* Public routines. */ ! 814: ! 815: - setLineWidth:(const float *)value ! 816: /* ! 817: * This is called with value indirected so that it can be called via ! 818: * a perform:with: method. Kind of screwy, but ... ! 819: */ ! 820: { ! 821: if (value) linewidth = *value; ! 822: return self; ! 823: } ! 824: ! 825: - (float)lineWidth ! 826: { ! 827: return linewidth; ! 828: } ! 829: ! 830: - setLineColor:(const NXColor *)color ! 831: { ! 832: if (color) { ! 833: if (NXEqualColor(*color, NX_COLORBLACK)) { ! 834: NX_FREE(lineColor); ! 835: lineColor = NULL; ! 836: gFlags.nooutline = NO; ! 837: } else { ! 838: if (!lineColor) NX_ZONEMALLOC([self zone], lineColor, NXColor, 1); ! 839: *lineColor = *color; ! 840: gFlags.nooutline = NO; ! 841: } ! 842: } ! 843: return self; ! 844: } ! 845: ! 846: - (NXColor)lineColor ! 847: { ! 848: return lineColor ? *lineColor : NX_COLORBLACK; ! 849: } ! 850: ! 851: - setFillColor:(const NXColor *)color ! 852: { ! 853: if (color) { ! 854: if (!fillColor) NX_ZONEMALLOC([self zone], fillColor, NXColor, 1); ! 855: *fillColor = *color; ! 856: if (![self fill]) [self setFill:FILL_NZWR]; ! 857: } ! 858: return self; ! 859: } ! 860: ! 861: - (NXColor)fillColor ! 862: { ! 863: return fillColor ? *fillColor : NX_COLORWHITE; ! 864: } ! 865: ! 866: - (Graphic *)colorAcceptorAt:(const NXPoint *)point ! 867: /* ! 868: * This method supports dragging and dropping colors on Graphics. ! 869: * Whatever object is returned from this may well be sent ! 870: * setFillColor: if the color actually gets dropped on it. ! 871: * See gvDrag.m's acceptsColor:atPoint: method. ! 872: */ ! 873: { ! 874: return nil; ! 875: } ! 876: ! 877: - changeFont:sender ! 878: { ! 879: return self; ! 880: } ! 881: ! 882: - font ! 883: { ! 884: return nil; ! 885: } ! 886: ! 887: - setGray:(const float *)value ! 888: /* ! 889: * This is called with value indirected so that it can be called via ! 890: * a perform:with: method. Kind of screwy, but ... ! 891: * Now that we have converted to using NXColor's, we'll interpret this ! 892: * method as a request to set the lineColor. ! 893: */ ! 894: { ! 895: NXColor color; ! 896: ! 897: if (value) { ! 898: color = NXConvertGrayToColor(*value); ! 899: [self setLineColor:&color]; ! 900: } ! 901: ! 902: return self; ! 903: } ! 904: ! 905: - (float)gray ! 906: { ! 907: float retval; ! 908: ! 909: if (lineColor) { ! 910: NXConvertColorToGray(*lineColor, &retval); ! 911: } else { ! 912: retval = NX_BLACK; ! 913: } ! 914: ! 915: return retval; ! 916: } ! 917: ! 918: - setFill:(int)mode ! 919: { ! 920: switch (mode) { ! 921: case FILL_NONE: gFlags.eofill = gFlags.fill = NO; break; ! 922: case FILL_EO: gFlags.eofill = YES; gFlags.fill = NO; break; ! 923: case FILL_NZWR: gFlags.eofill = NO; gFlags.fill = YES; break; ! 924: } ! 925: return self; ! 926: } ! 927: ! 928: - (int)fill ! 929: { ! 930: if (gFlags.eofill) { ! 931: return FILL_EO; ! 932: } else if (gFlags.fill) { ! 933: return FILL_NZWR; ! 934: } else { ! 935: return FILL_NONE; ! 936: } ! 937: } ! 938: ! 939: - setOutlined:(BOOL)outlinedFlag ! 940: { ! 941: gFlags.nooutline = outlinedFlag ? NO : YES; ! 942: return self; ! 943: } ! 944: ! 945: - (BOOL)isOutlined ! 946: { ! 947: return gFlags.nooutline ? NO : YES; ! 948: } ! 949: ! 950: - setLineCap:(int)capValue ! 951: { ! 952: if (capValue >= 0 && capValue <= 2) { ! 953: gFlags.linecap = capValue; ! 954: } ! 955: return self; ! 956: } ! 957: ! 958: - (int)lineCap ! 959: { ! 960: return gFlags.linecap; ! 961: } ! 962: ! 963: - setLineArrow:(int)arrowValue ! 964: { ! 965: if (arrowValue >= 0 && arrowValue <= 3) { ! 966: gFlags.arrow = arrowValue; ! 967: } ! 968: return self; ! 969: } ! 970: ! 971: - (int)lineArrow ! 972: { ! 973: return gFlags.arrow; ! 974: } ! 975: ! 976: - setLineJoin:(int)joinValue ! 977: { ! 978: if (joinValue >= 0 && joinValue <= 2) { ! 979: gFlags.linejoin = joinValue; ! 980: } ! 981: return self; ! 982: } ! 983: ! 984: - (int)lineJoin ! 985: { ! 986: return gFlags.linejoin; ! 987: } ! 988: ! 989: /* Archiver-related methods. */ ! 990: ! 991: - write:(NXTypedStream *)stream ! 992: /* ! 993: * Since a typical document has many Graphics, we want to try and make ! 994: * the archived document small, so we don't write out the linewidth and ! 995: * gray values if they are the most common 0 and NX_BLACK. To accomplish ! 996: * this, we note that we haven't written them out by setting the ! 997: * bits in gFlags. ! 998: */ ! 999: { ! 1000: [super write:stream]; ! 1001: gFlags.linewidthSet = (linewidth != 0.0); ! 1002: gFlags.lineColorSet = lineColor ? YES : NO; ! 1003: gFlags.fillColorSet = fillColor ? YES : NO; ! 1004: NXWriteTypes(stream, "ffffii", &bounds.origin.x, &bounds.origin.y, ! 1005: &bounds.size.width, &bounds.size.height, &gFlags, &identifier); ! 1006: if (gFlags.linewidthSet) NXWriteTypes(stream, "f", &linewidth); ! 1007: if (gFlags.lineColorSet) NXWriteColor(stream, *lineColor); ! 1008: if (gFlags.fillColorSet) NXWriteColor(stream, *fillColor); ! 1009: return self; ! 1010: } ! 1011: ! 1012: - read:(NXTypedStream *)stream ! 1013: { ! 1014: int version; ! 1015: float gray = NX_BLACK; ! 1016: ! 1017: [super read:stream]; ! 1018: version = NXTypedStreamClassVersion(stream, "Graphic"); ! 1019: if (version > 2) { ! 1020: NXReadTypes(stream, "ffffii", &bounds.origin.x, &bounds.origin.y, ! 1021: &bounds.size.width, &bounds.size.height, &gFlags, &identifier); ! 1022: } else if (version > 1) { ! 1023: NXReadTypes(stream, "ffffsi", &bounds.origin.x, &bounds.origin.y, ! 1024: &bounds.size.width, &bounds.size.height, &gFlags, &identifier); ! 1025: #ifdef __LITTLE_ENDIAN__ ! 1026: *(unsigned int *)&gFlags = *(unsigned int *)&gFlags <<= 16; ! 1027: #endif ! 1028: } else { ! 1029: NXReadTypes(stream, "ffffs", &bounds.origin.x, &bounds.origin.y, ! 1030: &bounds.size.width, &bounds.size.height, &gFlags); ! 1031: #ifdef __LITTLE_ENDIAN__ ! 1032: *(unsigned int *)&gFlags = *(unsigned int *)&gFlags <<= 16; ! 1033: #endif ! 1034: identifier = currentGraphicIdentifier++; ! 1035: } ! 1036: if (version > 1 && identifier >= currentGraphicIdentifier) currentGraphicIdentifier = identifier+1; ! 1037: if (gFlags.linewidthSet) NXReadTypes(stream, "f", &linewidth); ! 1038: if (version < 1) { ! 1039: if (gFlags.lineColorSet) NXReadTypes(stream, "f", &gray); ! 1040: if (gFlags.fillColorSet && (gFlags.eofill | gFlags.fill)) { ! 1041: NX_ZONEMALLOC([self zone], lineColor, NXColor, 1); ! 1042: *lineColor = NXConvertGrayToColor(NX_BLACK); ! 1043: NX_ZONEMALLOC([self zone], fillColor, NXColor, 1); ! 1044: *fillColor = NXConvertGrayToColor(gray); ! 1045: } else if (gFlags.eofill | gFlags.fill) { ! 1046: NX_ZONEMALLOC([self zone], fillColor, NXColor, 1); ! 1047: *fillColor = NXConvertGrayToColor(gray); ! 1048: [self setOutlined:NO]; ! 1049: } else { ! 1050: NX_ZONEMALLOC([self zone], lineColor, NXColor, 1); ! 1051: *lineColor = NXConvertGrayToColor(gray); ! 1052: } ! 1053: } else { ! 1054: if (gFlags.lineColorSet) { ! 1055: NX_ZONEMALLOC([self zone], lineColor, NXColor, 1); ! 1056: *lineColor = NXReadColor(stream); ! 1057: if (NXEqualColor(*lineColor, NX_COLORCLEAR)) { ! 1058: free(lineColor); ! 1059: lineColor = NULL; ! 1060: [self setOutlined:NO]; ! 1061: } ! 1062: } ! 1063: if (gFlags.fillColorSet) { ! 1064: NX_ZONEMALLOC([self zone], fillColor, NXColor, 1); ! 1065: *fillColor = NXReadColor(stream); ! 1066: if (NXEqualColor(*fillColor, NX_COLORCLEAR) || (NXAlphaComponent(*fillColor) == 0.0)) { ! 1067: free(fillColor); ! 1068: fillColor = NULL; ! 1069: [self setFill:FILL_NONE]; ! 1070: // } else if (!gFlags.eofill && !gFlags.fill) { // why did I add this code here? ! 1071: // gFlags.fill = YES; ! 1072: } ! 1073: } ! 1074: } ! 1075: ! 1076: return self; ! 1077: } ! 1078: ! 1079: /* Routines which may need subclassing for different Graphic types. */ ! 1080: ! 1081: - (BOOL)constrainByDefault ! 1082: { ! 1083: return NO; ! 1084: } ! 1085: ! 1086: - constrainCorner:(int)corner toAspectRatio:(float)aspect ! 1087: /* ! 1088: * Modifies the bounds rectangle by moving the specified corner so that ! 1089: * the Graphic maintains the specified aspect ratio. This is used during ! 1090: * constrained resizing. Can be overridden if the aspect ratio is not ! 1091: * sufficient to constrain resizing. ! 1092: */ ! 1093: { ! 1094: int newcorner; ! 1095: float actualAspect; ! 1096: ! 1097: if (!bounds.size.height || !bounds.size.width || !aspect) return self; ! 1098: actualAspect = bounds.size.width / bounds.size.height; ! 1099: if (actualAspect == aspect) return self; ! 1100: ! 1101: switch (corner) { ! 1102: case LEFT_SIDE: ! 1103: bounds.origin.x -= bounds.size.height * aspect-bounds.size.width; ! 1104: case RIGHT_SIDE: ! 1105: bounds.size.width = bounds.size.height * aspect; ! 1106: if (bounds.size.width) NXIntegralRect(&bounds); ! 1107: return self; ! 1108: case BOTTOM_SIDE: ! 1109: bounds.origin.y -= bounds.size.width / aspect-bounds.size.height; ! 1110: case TOP_SIDE: ! 1111: bounds.size.height = bounds.size.width / aspect; ! 1112: if (bounds.size.height) NXIntegralRect(&bounds); ! 1113: return self; ! 1114: case LOWER_LEFT: ! 1115: corner = 0; ! 1116: case 0: ! 1117: case UPPER_RIGHT: ! 1118: case UPPER_LEFT: ! 1119: case LOWER_RIGHT: ! 1120: if (actualAspect > aspect) { ! 1121: newcorner = ((corner|KNOB_DY_ONCE)&(~(KNOB_DY_TWICE))); ! 1122: } else { ! 1123: newcorner = ((corner|KNOB_DX_ONCE)&(~(KNOB_DX_TWICE))); ! 1124: } ! 1125: return [self constrainCorner:newcorner toAspectRatio:aspect]; ! 1126: default: ! 1127: return self; ! 1128: } ! 1129: } ! 1130: ! 1131: #define RESIZE_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK|NX_TIMERMASK) ! 1132: ! 1133: - resize:(NXEvent *)event by:(int)corner in:(GraphicView *)view ! 1134: /* ! 1135: * Resizes the graphic by the specified corner. If corner == CREATE, ! 1136: * then it is resized by the UPPER_RIGHT corner, but the initial size ! 1137: * is reset to 1 by 1. ! 1138: */ ! 1139: { ! 1140: NXPoint p, last; ! 1141: float aspect = 0.0; ! 1142: Window *window = [view window]; ! 1143: BOOL constrain, canScroll; ! 1144: DrawStatusType oldDrawStatus; ! 1145: NXTrackingTimer *timer = NULL; ! 1146: NXRect eb, starteb, oldeb, visibleRect; ! 1147: ! 1148: if (!gFlags.active || !gFlags.selected || !corner) return self; ! 1149: ! 1150: constrain = ((event->flags & NX_ALTERNATEMASK) && ! 1151: ((bounds.size.width && bounds.size.height) || corner == CREATE)); ! 1152: if ([self constrainByDefault]) constrain = !constrain; ! 1153: if (constrain) aspect = bounds.size.width / bounds.size.height; ! 1154: if (corner == CREATE) { ! 1155: bounds.size.width = bounds.size.height = 1.0; ! 1156: corner = UPPER_RIGHT; ! 1157: } ! 1158: ! 1159: gFlags.selected = NO; ! 1160: ! 1161: [self getExtendedBounds:&eb]; ! 1162: [view lockFocus]; ! 1163: gFlags.active = NO; ! 1164: [view cache:&eb andUpdateLinks:NO]; ! 1165: gFlags.active = YES; ! 1166: starteb = eb; ! 1167: [self draw:NULL]; ! 1168: [window flushWindow]; ! 1169: ! 1170: oldDrawStatus = DrawStatus; ! 1171: DrawStatus = Resizing; ! 1172: ! 1173: [view getVisibleRect:&visibleRect]; ! 1174: canScroll = !NXEqualRect(&visibleRect, &bounds); ! 1175: if (canScroll && !timer) timer = NXBeginTimer(NULL, 0.1, 0.1); ! 1176: ! 1177: last.x = last.y = -1.0; ! 1178: while (event->type != NX_MOUSEUP) { ! 1179: p = event->location; ! 1180: event = [NXApp getNextEvent:RESIZE_MASK]; ! 1181: if (event->type == NX_TIMER) event->location = p; ! 1182: p = event->location; ! 1183: [view convertPoint:&p fromView:nil]; ! 1184: [view grid:&p]; ! 1185: if (p.x != last.x || p.y != last.y) { ! 1186: corner = [self moveCorner:corner to:&p constrain:constrain]; ! 1187: if (constrain) [self constrainCorner:corner toAspectRatio:aspect]; ! 1188: oldeb = eb; ! 1189: [self getExtendedBounds:&eb]; ! 1190: [window disableFlushWindow]; ! 1191: [view drawSelf:&oldeb :1]; ! 1192: if (canScroll) { ! 1193: [view scrollPointToVisible:&p]; // actually we want to keep the "edges" of the ! 1194: // Graphic being resized that were visible when ! 1195: // the resize started visible throughout the ! 1196: // resizing time (this will be difficult if those ! 1197: // edges flip from being the left edge to the ! 1198: // right edge in the middle of the resize!). ! 1199: } ! 1200: [self draw:NULL]; ! 1201: [view tryToPerform:@selector(updateRulers:) with:(void *)&bounds]; ! 1202: [window reenableFlushWindow]; ! 1203: [window flushWindow]; ! 1204: last = p; ! 1205: NXPing(); ! 1206: } ! 1207: } ! 1208: ! 1209: if (canScroll && timer) { ! 1210: NXEndTimer(timer); ! 1211: timer = NULL; ! 1212: } ! 1213: ! 1214: gFlags.selected = YES; ! 1215: DrawStatus = oldDrawStatus; ! 1216: ! 1217: [view cache:&eb andUpdateLinks:NO]; // redraw after resizing a Graphic ! 1218: NXUnionRect(&eb, &starteb); ! 1219: [view updateTrackedLinks:&starteb]; ! 1220: [view tryToPerform:@selector(updateRulers:) with:nil]; ! 1221: [window flushWindow]; ! 1222: [view unlockFocus]; ! 1223: ! 1224: return self; ! 1225: } ! 1226: ! 1227: - (BOOL)create:(NXEvent *)event in:(GraphicView *)view ! 1228: /* ! 1229: * This method rarely needs to be subclassed. ! 1230: * It sets up an initial bounds, and calls resize:by:in:. ! 1231: */ ! 1232: { ! 1233: BOOL valid; ! 1234: NXCoord gridSpacing; ! 1235: ! 1236: bounds.origin = event->location; ! 1237: [view convertPoint:&bounds.origin fromView:nil]; ! 1238: [view grid:&bounds.origin]; ! 1239: ! 1240: gridSpacing = (NXCoord)[view gridSpacing]; ! 1241: bounds.size.height = gridSpacing; ! 1242: bounds.size.width = gridSpacing * [self naturalAspectRatio]; ! 1243: ! 1244: [self resize:event by:CREATE in:view]; ! 1245: ! 1246: valid = [self isValid]; ! 1247: ! 1248: if (valid) { ! 1249: gFlags.selected = YES; ! 1250: gFlags.active = YES; ! 1251: } else { ! 1252: gFlags.selected = NO; ! 1253: gFlags.active = NO; ! 1254: [view display]; ! 1255: } ! 1256: ! 1257: return valid; ! 1258: } ! 1259: ! 1260: - (BOOL)hit:(const NXPoint *)p ! 1261: { ! 1262: return (!gFlags.locked && gFlags.active && NXMouseInRect(p, &bounds, NO)); ! 1263: } ! 1264: ! 1265: - (BOOL)isOpaque ! 1266: { ! 1267: return [self fill] ? YES : NO; ! 1268: } ! 1269: ! 1270: - (BOOL)isValid ! 1271: /* ! 1272: * Called after a Graphic is created to see if it is valid (this usually ! 1273: * means "is it big enough?"). ! 1274: */ ! 1275: { ! 1276: return (bounds.size.width > MINSIZE && bounds.size.height > MINSIZE); ! 1277: } ! 1278: ! 1279: - (float)naturalAspectRatio ! 1280: /* ! 1281: * A natural aspect ratio of zero means it doesn't have a natural aspect ratio. ! 1282: */ ! 1283: { ! 1284: return 0.0; ! 1285: } ! 1286: ! 1287: - (int)moveCorner:(int)corner to:(const NXPoint *)p constrain:(BOOL)flag ! 1288: /* ! 1289: * Moves the specified corner to the specified point. ! 1290: * Returns the position of the corner after it was moved. ! 1291: */ ! 1292: { ! 1293: int newcorner = corner; ! 1294: ! 1295: if ((corner & KNOB_DX_ONCE) && (corner & KNOB_DX_TWICE)) { ! 1296: bounds.size.width += p->x - (bounds.origin.x + bounds.size.width); ! 1297: if (bounds.size.width <= 0.0) { ! 1298: newcorner &= ~ (KNOB_DX_ONCE | KNOB_DX_TWICE); ! 1299: bounds.origin.x += bounds.size.width; ! 1300: bounds.size.width = - bounds.size.width; ! 1301: } ! 1302: } else if (!(corner & KNOB_DX_ONCE)) { ! 1303: bounds.size.width += bounds.origin.x - p->x; ! 1304: bounds.origin.x = p->x; ! 1305: if (bounds.size.width <= 0.0) { ! 1306: newcorner |= KNOB_DX_ONCE | KNOB_DX_TWICE; ! 1307: bounds.origin.x += bounds.size.width; ! 1308: bounds.size.width = - bounds.size.width; ! 1309: } ! 1310: } ! 1311: ! 1312: if ((corner & KNOB_DY_ONCE) && (corner & KNOB_DY_TWICE)) { ! 1313: bounds.size.height += p->y - (bounds.origin.y + bounds.size.height); ! 1314: if (bounds.size.height <= 0.0) { ! 1315: newcorner &= ~ (KNOB_DY_ONCE | KNOB_DY_TWICE); ! 1316: bounds.origin.y += bounds.size.height; ! 1317: bounds.size.height = - bounds.size.height; ! 1318: } ! 1319: } else if (!(corner & KNOB_DY_ONCE)) { ! 1320: bounds.size.height += bounds.origin.y - p->y; ! 1321: bounds.origin.y = p->y; ! 1322: if (bounds.size.height <= 0.0) { ! 1323: newcorner |= KNOB_DY_ONCE | KNOB_DY_TWICE; ! 1324: bounds.origin.y += bounds.size.height; ! 1325: bounds.size.height = - bounds.size.height; ! 1326: } ! 1327: } ! 1328: ! 1329: if (newcorner != LOWER_LEFT) newcorner &= 0xf; ! 1330: if (!newcorner) newcorner = LOWER_LEFT; ! 1331: ! 1332: return newcorner; ! 1333: } ! 1334: ! 1335: - unitDraw ! 1336: /* ! 1337: * If a Graphic just wants to draw itself in the bounding box of ! 1338: * {{0.0,0.0},{1.0,1.0}}, it can simply override this method. ! 1339: * Everything else will work fine. ! 1340: */ ! 1341: { ! 1342: return self; ! 1343: } ! 1344: ! 1345: - draw ! 1346: /* ! 1347: * Almost all Graphics need to override this method. ! 1348: * It does the Graphic-specific drawing. ! 1349: * By default, it scales the coordinate system and calls unitDraw. ! 1350: */ ! 1351: { ! 1352: if (bounds.size.width >= 1.0 && bounds.size.height >= 1.0) { ! 1353: PStranslate(bounds.origin.x, bounds.origin.y); ! 1354: PSscale(bounds.size.width, bounds.size.height); ! 1355: [self unitDraw]; ! 1356: } ! 1357: return self; ! 1358: } ! 1359: ! 1360: - (BOOL)edit:(NXEvent *)event in:(View *)view ! 1361: /* ! 1362: * Any Graphic which has editable text should override this method ! 1363: * to edit that text. TextGraphic is an example. ! 1364: */ ! 1365: { ! 1366: return NO; ! 1367: } ! 1368: ! 1369: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.