Annotation of Examples/AppKit/Draw/Graphic.m, revision 1.1.1.1

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

unix.superglobalmegacorp.com

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