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

1.1     ! root        1: #import "draw.h"
        !             2: 
        !             3: @implementation GraphicView(Links)
        !             4: 
        !             5: /* See the Links.rtf file for overview about Object Links in Draw. */
        !             6: 
        !             7: #define BUFFER_SIZE 1100
        !             8: #define INT_WIDTH 11
        !             9: 
        !            10: /*
        !            11:  * Returns an NXSelection describe the current Graphic's selected in the view.
        !            12:  * If the user did Select All, then the gvFlags.selectAll bit is set and we
        !            13:  * return the allSelection NXSelection.  If the user dragged out a rectangle,
        !            14:  * then the dragRect rectangle is set and we return a ByRect NXSelection.
        !            15:  * Otherwise, we return a ByGraphic NXSelection.
        !            16:  *
        !            17:  * We use the writeIdentifierTo: mechanism so that Group Graphic's can ask
        !            18:  * their components to write out all their identifiers so that we have a
        !            19:  * maximal chance of getting all the objects.
        !            20:  */
        !            21: 
        !            22: - (NXSelection *)currentSelection
        !            23: {
        !            24:     NXRect sbounds;
        !            25:     int i, graphicCount;
        !            26:     NXSelection *retval = nil;
        !            27:     char *s, *selbuf, buffer[BUFFER_SIZE];
        !            28: 
        !            29:     if (![slist count]) return [NXSelection emptySelection];
        !            30: 
        !            31:     if (gvFlags.selectAll) {
        !            32:        if ([slist count] == [glist count]) {
        !            33:            return [NXSelection allSelection];
        !            34:        } else {
        !            35:            gvFlags.selectAll = NO;
        !            36:        }
        !            37:     }
        !            38: 
        !            39:     if (dragRect) {
        !            40:        sbounds = *dragRect;
        !            41:        sprintf(buffer, "%d %d %d %d %d", ByRect, 
        !            42:            (int)sbounds.origin.x, (int)sbounds.origin.y,
        !            43:            (int)(sbounds.size.width+0.5), (int)(sbounds.size.height+0.5));
        !            44:        selbuf = buffer;
        !            45:     } else {
        !            46:        graphicCount = 0;
        !            47:        i = [slist count];
        !            48:        while (i--) graphicCount += [[slist objectAt:i] graphicCount];
        !            49:        if (graphicCount > (BUFFER_SIZE / INT_WIDTH)) {
        !            50:            NX_MALLOC(selbuf, char, graphicCount * INT_WIDTH);
        !            51:        } else {
        !            52:            selbuf = buffer;
        !            53:        }
        !            54:        sprintf(buffer, "%d %d", ByList, graphicCount);
        !            55:        s = selbuf + strlen(selbuf);
        !            56:        i = [slist count];
        !            57:        while (i--) {
        !            58:            *s++ = ' ';
        !            59:            [[slist objectAt:i] writeIdentifierTo:s];
        !            60:            s += strlen(s);
        !            61:        }
        !            62:     }
        !            63: 
        !            64:     retval = [[NXSelection allocFromZone:[self zone]] initWithDescription:selbuf length:strlen(selbuf)+1];
        !            65: 
        !            66:     if (selbuf != buffer) free(selbuf);
        !            67: 
        !            68:     return retval;
        !            69: }
        !            70: 
        !            71: /*
        !            72:  * Used for destination selections only.
        !            73:  * Just extracts the unique identifier for the destination Image
        !            74:  * or TextGraphic and then searches through the glist to find that
        !            75:  * Graphic and returns it.
        !            76:  *
        !            77:  * Again, we use the graphicIdentifiedBy: mechanism so that we
        !            78:  * descend into Group's of Graphics to find a destination.
        !            79:  */
        !            80: 
        !            81: - (Graphic *)findGraphicInSelection:(NXSelection *)selection
        !            82: {
        !            83:     int i;
        !            84:     Graphic *graphic;
        !            85:     const char *selectionInfo;
        !            86:     int selectionInfoLength, identifier, selectionType;
        !            87: 
        !            88:     selectionInfo = [selection descriptionOfLength:&selectionInfoLength];
        !            89:     if (selectionInfo) {
        !            90:        sscanf(selectionInfo, "%d %d", &selectionType, &identifier);
        !            91:        if (selectionType == ByGraphic) {
        !            92:            for (i = [glist count]-1; i >= 0; i--) {
        !            93:                if (graphic = [[glist objectAt:i] graphicIdentifiedBy:identifier]) return graphic;
        !            94:            }
        !            95:        }
        !            96:     }
        !            97: 
        !            98:     return nil;
        !            99: }
        !           100: 
        !           101: /*
        !           102:  * Returns YES and theRect is valid only if the selection is one which
        !           103:  * the user created by dragging out a rectangle.
        !           104:  */
        !           105: 
        !           106: - (BOOL)getRect:(NXRect *)theRect forSelection:(NXSelection *)selection
        !           107: {
        !           108:     NXRect stackRect;
        !           109:     const char *selectionInfo;
        !           110:     int selectionInfoLength;
        !           111:     DrawSelectionType selectionType;
        !           112: 
        !           113:     if (selectionInfo = [selection descriptionOfLength:&selectionInfoLength]) {
        !           114:        if (!theRect) theRect = &stackRect;
        !           115:        sscanf(selectionInfo, "%d %f %f %f %f", (int *)&selectionType,
        !           116:                &(theRect->origin.x), &(theRect->origin.y),
        !           117:                &(theRect->size.width), &(theRect->size.height));
        !           118:        if (selectionType == ByRect) return YES;
        !           119:     }
        !           120: 
        !           121:     return NO;
        !           122: }
        !           123: 
        !           124: /*
        !           125:  * For source selections only.
        !           126:  * Returns the list of Graphics in the current document which were
        !           127:  * in the selection passed to this method.  Note that any Group 
        !           128:  * which includes a Graphic in the passed selection will be included
        !           129:  * in its entirety.
        !           130:  */
        !           131: 
        !           132: - (List *)findGraphicsInSelection:(NXSelection *)selection
        !           133: {
        !           134:     int i, count;
        !           135:     Graphic *graphic;
        !           136:     List *list = nil;
        !           137:     NXRect sBounds, gBounds;
        !           138:     int selectionInfoLength;
        !           139:     const char *s, *theGraphics;
        !           140:     DrawSelectionType selectionType;
        !           141: 
        !           142:     if ([selection isEqual:[NXSelection allSelection]]) {
        !           143:        count = [glist count];
        !           144:        list = [[List allocFromZone:[self zone]] initCount:count];
        !           145:        for (i = 0; i < count; i++) [list addObject:[glist objectAt:i]];
        !           146:     } else if ([self getRect:&sBounds forSelection:selection]) {
        !           147:        count = [glist count];
        !           148:        list = [[List allocFromZone:[self zone]] init];
        !           149:        for (i = 0; i < count; i++) {
        !           150:            graphic = [glist objectAt:i];
        !           151:            [graphic getBounds:&gBounds];
        !           152:            NXInsetRect(&gBounds, -0.1, -0.1);
        !           153:            if (NXIntersectsRect(&gBounds, &sBounds)) [list addObject:graphic];
        !           154:        }
        !           155:     } else if (s = [selection descriptionOfLength:&selectionInfoLength]) {
        !           156:        sscanf(s, "%d %d", (int *)&selectionType, &count);
        !           157:        if (selectionType == ByList) {
        !           158:            if (s = strchr(s, ' ')) s = strchr(s+1, ' ');
        !           159:            if (s++) {
        !           160:                theGraphics = s;
        !           161:                list = [[List allocFromZone:[self zone]] init];
        !           162:                count = [glist count];
        !           163:                for (i = 0; i < count; i++) {
        !           164:                    graphic = [glist objectAt:i];
        !           165:                    s = theGraphics;
        !           166:                    while (s && *s) {
        !           167:                        if ([graphic graphicIdentifiedBy:atoi(s)]) {
        !           168:                            [list addObject:graphic];
        !           169:                            break;
        !           170:                        }
        !           171:                        if (s = strchr(s, ' ')) s++;
        !           172:                    }
        !           173:                }
        !           174:            }
        !           175:        }
        !           176:     }
        !           177: 
        !           178:     if (![list count]) {
        !           179:        [list free];
        !           180:        list = nil;
        !           181:     }
        !           182: 
        !           183:     return list;
        !           184: }
        !           185: 
        !           186: /*
        !           187:  * Importing/Exporting links.
        !           188:  */
        !           189: 
        !           190: /*
        !           191:  * This method is called by copyToPasteboard:.  It just puts a link to the currentSelection
        !           192:  * (presumably just written to the pasteboard by copyToPasteboard:) into the specified
        !           193:  * pboard.  Note that it only does all this if we are writing all possible types to the
        !           194:  * pasteboard (typesList == NULL) or if we explicitly ask for the link to be written
        !           195:  * (typesList includes NXDataLinkPboardType).
        !           196:  */
        !           197: 
        !           198: - writeLinkToPasteboard:(Pasteboard *)pboard types:(const NXAtom *)typesList
        !           199: {
        !           200:     NXDataLink *link;
        !           201: 
        !           202:     if (linkManager && (!typesList || IncludesType(typesList, NXDataLinkPboardType))) {
        !           203:        if (link = [[NXDataLink alloc] initLinkedToSourceSelection:[self currentSelection] managedBy:linkManager supportingTypes:TypesDrawExports() count:NUM_TYPES_DRAW_EXPORTS]) {
        !           204:            [pboard addTypes:&NXDataLinkPboardType num:1 owner:[self class]];
        !           205:            [link writeToPasteboard:pboard];
        !           206:            [link free];
        !           207:        }
        !           208:        [linkManager writeLinksToPasteboard:pboard]; // for embedded linked things
        !           209:     }
        !           210: 
        !           211:     return self;
        !           212: }
        !           213: 
        !           214: /*
        !           215:  * Sets up a link from the Draw document to another document.
        !           216:  * This is called by the drag stuff (gvDrag.m) and the normal copy/paste stuff (gvPasteboard.m).
        !           217:  * We allow for the case of graphic being nil as long as the link is capable of supplying
        !           218:  * data of a type we can handle (currently Text or Image).
        !           219:  */
        !           220: 
        !           221: - (BOOL)addLink:(NXDataLink *)link toGraphic:(Graphic *)graphic at:(const NXPoint *)p update:(int)update
        !           222: {
        !           223:     NXSelection *selection = nil;
        !           224: 
        !           225:     if (!graphic && link && update != UPDATE_NEVER) {
        !           226:        if (TextPasteType([link types])) {
        !           227:            graphic = [[TextGraphic allocFromZone:[self zone]] initEmpty];
        !           228:        } else if (MatchTypes([link types], [NXImage imagePasteboardTypes])) {
        !           229:            graphic = [[Image allocFromZone:[self zone]] initEmpty];
        !           230:        }
        !           231:        update = UPDATE_IMMEDIATELY;
        !           232:     }
        !           233: 
        !           234:     if (graphic && link) {
        !           235:        selection = [graphic selection];
        !           236:        if ([linkManager addLink:link at:selection]) {
        !           237:            if (!update) [link setUpdateMode:NX_UpdateNever];
        !           238:            [graphic setLink:link];
        !           239:            if (graphic = [self placeGraphic:graphic at:p]) {
        !           240:                if (update == UPDATE_IMMEDIATELY) {
        !           241:                    [link updateDestination];
        !           242:                    graphic = [self findGraphicInSelection:selection];
        !           243:                    if (![graphic isValid]) {
        !           244:                        NXRunLocalizedAlertPanel(NULL, "Import Link",
        !           245:                            "Unable to import linked data.", NULL, NULL, NULL,
        !           246:                            "Message given to user when import of linked data fails.");
        !           247:                        [self removeGraphic:graphic];
        !           248:                    } else {
        !           249:                        return YES;
        !           250:                    }
        !           251:                } else {
        !           252:                    return YES;
        !           253:                }
        !           254:            }
        !           255:        }
        !           256:     }
        !           257: 
        !           258:     [link free];
        !           259:     [selection free];
        !           260:     [graphic free];
        !           261: 
        !           262:     return NO;
        !           263: }
        !           264: 
        !           265: /*
        !           266:  * Keeping links up to date.
        !           267:  * These methods are called either to update a link that draw has to another
        !           268:  * document or to cause Draw to update another document that is linked to it.
        !           269:  */
        !           270: 
        !           271: /*
        !           272:  * Sent whenever NeXTSTEP wants us to update some data in our document which
        !           273:  * we get by being linked to some other document.
        !           274:  */
        !           275: 
        !           276: - pasteFromPasteboard:(Pasteboard *)pboard at:(NXSelection *)selection
        !           277: {
        !           278:     id graphic;
        !           279:     NXRect gBounds;
        !           280: 
        !           281:     if (graphic = [self findGraphicInSelection:selection]) {
        !           282:        gBounds = [graphic reinitFromPasteboard:pboard];
        !           283:        [self cache:&gBounds];  // updating a destination link
        !           284:        [window flushWindow];
        !           285:        [self dirty];
        !           286:        return self;
        !           287:     }
        !           288: 
        !           289:     return nil;
        !           290: }
        !           291: 
        !           292: /*
        !           293:  * Lazy pasteboard method for cheapCopyAllowed case ONLY.
        !           294:  * See copyToPasteboard:at:cheapCopyAllowed: below.
        !           295:  */
        !           296: 
        !           297: - pasteboard:(Pasteboard *)sender provideData:(const char *)type
        !           298: {
        !           299:     List *list;
        !           300:     NXStream *stream;
        !           301:     NXSelection *selection;
        !           302: 
        !           303:     selection = [[NXSelection allocFromZone:[self zone]] initFromPasteboard:sender];
        !           304:     list = [self findGraphicsInSelection:selection];
        !           305:     if (list && (stream = NXOpenMemory(NULL, 0, NX_WRITEONLY))) {
        !           306:        if (type == NXPostScriptPboardType) {
        !           307:            [self writePSToStream:stream usingList:list];
        !           308:        } else if (type == NXTIFFPboardType) {
        !           309:            [self writeTIFFToStream:stream usingList:list];
        !           310:        }
        !           311:        [sender writeType:type fromStream:stream];
        !           312:        NXCloseMemory(stream, NX_FREEBUFFER);
        !           313:     }
        !           314:     [list free];
        !           315:     [selection free];
        !           316: 
        !           317:     return self;
        !           318: }
        !           319: 
        !           320: /*
        !           321:  * Called by NeXTSTEP when some other document needs to be updated because
        !           322:  * they are linked to something in our document.
        !           323:  */
        !           324: 
        !           325: - copyToPasteboard:(Pasteboard *)pboard at:(NXSelection *)selection cheapCopyAllowed:(BOOL)cheapCopyAllowed
        !           326: {
        !           327:     List *list;
        !           328:     NXStream *stream;
        !           329:     NXTypedStream *ts;
        !           330:     id retval = self;
        !           331:     const char *types[3];
        !           332: 
        !           333:     types[1] = NXPostScriptPboardType;
        !           334:     types[2] = NXTIFFPboardType;
        !           335: 
        !           336:     if (cheapCopyAllowed) {
        !           337:        if (list = [self findGraphicsInSelection:selection]) {
        !           338:            types[0] = NXSelectionPboardType;
        !           339:            [pboard declareTypes:types num:3 owner:self];
        !           340:            [selection writeToPasteboard:pboard];
        !           341:        } else {
        !           342:            retval = nil;
        !           343:        }
        !           344:        [list free];
        !           345:     } else {
        !           346:        types[0] = DrawPboardType;
        !           347:        [pboard declareTypes:types num:3 owner:[self class]];
        !           348:        list = [self findGraphicsInSelection:selection];
        !           349:        if (list && (stream = NXOpenMemory(NULL, 0, NX_WRITEONLY))) {
        !           350:            if (ts = NXOpenTypedStream(stream, NX_WRITEONLY)) {
        !           351:                NXWriteRootObject(ts, list);
        !           352:                NXCloseTypedStream(ts);
        !           353:            }
        !           354:            [pboard writeType:DrawPboardType fromStream:stream];
        !           355:            NXCloseMemory(stream, NX_FREEBUFFER);
        !           356:        } else {
        !           357:            retval = nil;
        !           358:        }
        !           359:        [list free];
        !           360:     }
        !           361: 
        !           362:     return retval;
        !           363: }
        !           364: 
        !           365: 
        !           366: /*
        !           367:  * Supports linking to an entire file (not just a selection therein).
        !           368:  * This occurs when you drag a file into Draw and link (see gvDrag).
        !           369:  * This is very analogous to the pasteFromPasteboard:at: above.
        !           370:  */
        !           371: 
        !           372: - importFile:(const char *)filename at:(NXSelection *)selection
        !           373: {
        !           374:     id graphic;
        !           375:     NXRect gBounds;
        !           376: 
        !           377:     if (graphic = [self findGraphicInSelection:selection]) {
        !           378:        gBounds = [graphic reinitFromFile:filename];
        !           379:        [self cache:&gBounds];  // updating a link to an imported file
        !           380:        [window flushWindow];
        !           381:        [self dirty];
        !           382:        return self;
        !           383:     }
        !           384: 
        !           385:     return nil;
        !           386: }
        !           387: 
        !           388: /* Other Links methods */
        !           389: 
        !           390: /*
        !           391:  * Just makes the Link Inspector panel reflect whether any of the
        !           392:  * Graphic's currently selected are linked to some other document.
        !           393:  */
        !           394: 
        !           395: - updateLinksPanel
        !           396: {
        !           397:     int i, linkCount = 0;
        !           398:     Graphic *foundGraphic = nil, *graphic = nil;
        !           399: 
        !           400:     if (linkManager) {
        !           401:        for (i = [slist count]-1; i >= 0; i--) {
        !           402:            if (graphic = [[slist objectAt:i] graphicLinkedBy:NULL]) {
        !           403:                if ([graphic isKindOf:[Group class]]) {
        !           404:                    linkCount += 2;
        !           405:                    break;
        !           406:                } else {
        !           407:                    linkCount += 1;
        !           408:                    foundGraphic = graphic;
        !           409:                }
        !           410:            }
        !           411:        }
        !           412:        if (linkCount == 1) {
        !           413:            [NXDataLinkPanel setLink:[foundGraphic link] andManager:linkManager isMultiple:NO];
        !           414:        } else if (linkCount) {
        !           415:            [NXDataLinkPanel setLink:[foundGraphic link] andManager:linkManager isMultiple:YES];
        !           416:        } else {
        !           417:            [NXDataLinkPanel setLink:nil andManager:linkManager isMultiple:NO];
        !           418:        }
        !           419:     }
        !           420: 
        !           421:     return self;
        !           422: }
        !           423: 
        !           424: - (NXDataLinkManager *)linkManager
        !           425: {
        !           426:     return linkManager;
        !           427: }
        !           428: 
        !           429: /*
        !           430:  * When we get a linkManager via this method, we must go and revive all the links.
        !           431:  * This is due to the fact that we don't archive ANY link information when we
        !           432:  * save a Draw document.  However, the unique identifiers ARE archived, and thus,
        !           433:  * when we unarchive, we can recreate NXSelections with those unique identifiers
        !           434:  * and then ask the NXDataLinkManager for the link objects associated with those
        !           435:  * NXSelections.
        !           436:  *
        !           437:  * After we have revived all the links, we call breakLinkAndRedrawOutlines:
        !           438:  * with nil (meaning redraw the link outlines for all links).
        !           439:  */
        !           440: 
        !           441: - setLinkManager:(NXDataLinkManager *)aLinkManager
        !           442: {
        !           443:     if (!linkManager) {
        !           444:        linkManager = aLinkManager;
        !           445:        [glist makeObjectsPerform:@selector(reviveLink:) with:linkManager];
        !           446:        [self breakLinkAndRedrawOutlines:nil];
        !           447:     }
        !           448:     return self;
        !           449: }
        !           450: 
        !           451: /*
        !           452:  * This is called when the user chooses Open Source.
        !           453:  * It uses the trick of drawing directly into the GraphicView
        !           454:  * which, of course, is only ephemeral since the REAL contents
        !           455:  * of the GraphicView are stored in the backing store.
        !           456:  * This is convenient because Open Source is only a temporary
        !           457:  * the the user calls to see where the data for his link is
        !           458:  * coming from.
        !           459:  */
        !           460:  
        !           461: - showSelection:(NXSelection *)selection
        !           462: {
        !           463:     id retval = self;
        !           464:     List *graphics = nil;
        !           465:     NXRect *newInvalidRect;
        !           466:     NXRect sBounds, linkBounds;
        !           467:     
        !           468:     [self lockFocus];
        !           469:     if (invalidRect) {
        !           470:        [self drawSelf:invalidRect :1];
        !           471:        newInvalidRect = invalidRect;
        !           472:        invalidRect = NULL;
        !           473:     } else{
        !           474:        NX_MALLOC(newInvalidRect, NXRect, 1);
        !           475:     }
        !           476:     if ([self getRect:&linkBounds forSelection:selection]) {
        !           477:        PSsetgray(NX_LTGRAY);
        !           478:        NXFrameRectWithWidth(&linkBounds, 2.0);
        !           479:        *newInvalidRect = linkBounds;
        !           480:        graphics = [self findGraphicsInSelection:selection];
        !           481:        if (graphics) {
        !           482:            [self getBBox:&sBounds of:graphics];
        !           483:            NXUnionRect(&sBounds, newInvalidRect);
        !           484:        } else {
        !           485:            invalidRect = newInvalidRect;
        !           486:            [self scrollRectToVisible:invalidRect];
        !           487:            [window flushWindow];
        !           488:            retval = nil;
        !           489:        }
        !           490:     } else {
        !           491:        graphics = [self findGraphicsInSelection:selection];
        !           492:        if (graphics) {
        !           493:            [self getBBox:&sBounds of:graphics];
        !           494:            *newInvalidRect = sBounds;
        !           495:        } else {
        !           496:            retval = nil;
        !           497:        }
        !           498:     }
        !           499: 
        !           500:     if (retval) {
        !           501:        NXFrameLinkRect(&sBounds, NO);
        !           502:        invalidRect = newInvalidRect;
        !           503:        NXInsetRect(invalidRect, -NXLinkFrameThickness(), -NXLinkFrameThickness());
        !           504:        [self scrollRectToVisible:invalidRect];
        !           505:        [window flushWindow];
        !           506:     }
        !           507: 
        !           508:     [self unlockFocus];
        !           509:     [graphics free];
        !           510: 
        !           511:     return retval;
        !           512: }
        !           513: 
        !           514: /*
        !           515:  * Called when the Show Links button in the Link Inspector panel is clicked
        !           516:  * (the link argument will be nil in this case), or when a link is broken
        !           517:  * (the link argument will be the link that was broken).
        !           518:  */
        !           519: 
        !           520: - breakLinkAndRedrawOutlines:(NXDataLink *)link
        !           521: {
        !           522:     int i;
        !           523:     Graphic *graphic;
        !           524:     BOOL gotOne = NO;
        !           525:     NXRect eBounds, recacheBounds;
        !           526: 
        !           527:     for (i = [glist count]-1; i >= 0; i--) {
        !           528:        graphic = [glist objectAt:i];
        !           529:        if (graphic = [graphic graphicLinkedBy:link]) {
        !           530:            if (link && ([graphic link] == link) &&
        !           531:                ([link updateMode] == NX_UpdateNever)) {
        !           532:                    [self removeGraphic:graphic];
        !           533:            }
        !           534:            if (!link || [linkManager areLinkOutlinesVisible]) {
        !           535:                [graphic getExtendedBounds:&eBounds];
        !           536:                if (gotOne) {
        !           537:                    NXUnionRect(&eBounds, &recacheBounds);
        !           538:                } else {
        !           539:                    recacheBounds = eBounds;
        !           540:                    gotOne = YES;
        !           541:                }
        !           542:            }
        !           543:        }
        !           544:     }
        !           545:     if (gotOne) {
        !           546:        [self cache:&recacheBounds andUpdateLinks:NO];
        !           547:        [window flushWindow];
        !           548:     }
        !           549: 
        !           550:     return self;
        !           551: }
        !           552: 
        !           553: /*
        !           554:  * Tracking Link Changes.
        !           555:  *
        !           556:  * This is how we get "Continuous" updating links.
        !           557:  *
        !           558:  * We simply assume that a thing someone is linked to in our document
        !           559:  * changes whenever we have to redraw any rectangle in the GraphicView
        !           560:  * which intersects the linked-to rectangle.  See cache:andUpdateLinks:
        !           561:  * in GraphicView.m.
        !           562:  */
        !           563: 
        !           564: typedef struct {
        !           565:     NXRect linkRect;
        !           566:     NXDataLink *link;
        !           567:     BOOL dragged, all;
        !           568: } LinkRect;
        !           569: 
        !           570: - updateTrackedLinks:(const NXRect *)sRect
        !           571: {
        !           572:     int i;
        !           573:     LinkRect *lr;
        !           574:     List *graphics;
        !           575:     NXSelection *selection;
        !           576:     NXRect *lRect, newRect;
        !           577: 
        !           578:     for (i = [linkTrackingRects count]-1; i >= 0; i--) {
        !           579:        if (NXIntersectsRect(sRect, (NXRect *)[linkTrackingRects elementAt:i])) {
        !           580:            lr = ((LinkRect *)[linkTrackingRects elementAt:i]);
        !           581:            [lr->link sourceEdited];
        !           582:            lRect = (NXRect *)[linkTrackingRects elementAt:i];
        !           583:            if (!lr->dragged && !lr->all && !NXContainsRect(lRect, sRect)) {
        !           584:                selection = [lr->link sourceSelection];
        !           585:                if (graphics = [self findGraphicsInSelection:selection]) {
        !           586:                    [self getBBox:&newRect of:graphics];
        !           587:                    *lRect = newRect;
        !           588:                    [graphics free];
        !           589:                }
        !           590:            }
        !           591:        }
        !           592:     }
        !           593: 
        !           594:     return self;
        !           595: }
        !           596: 
        !           597: /* Add to linkTrackingRects. */
        !           598: 
        !           599: - startTrackingLink:(NXDataLink *)link
        !           600: {
        !           601:     LinkRect trackRect;
        !           602:     List *graphics = nil;
        !           603:     NXSelection *selection;
        !           604:     BOOL all = NO, dragged = NO, piecemeal = NO;
        !           605: 
        !           606:     selection = [link sourceSelection];
        !           607:     if ([selection isEqual:[NXSelection allSelection]]) {
        !           608:        all = YES;
        !           609:        trackRect.linkRect = bounds;
        !           610:     } else if ([self getRect:&trackRect.linkRect forSelection:selection]) {
        !           611:        dragged = YES;
        !           612:     } else if (graphics = [self findGraphicsInSelection:selection]) {
        !           613:        [self getBBox:&trackRect.linkRect of:graphics];
        !           614:        piecemeal = YES;
        !           615:        [graphics free];
        !           616:     } else {
        !           617:        return nil;
        !           618:     }
        !           619: 
        !           620:     if (all || dragged || piecemeal) {
        !           621:        if (!linkTrackingRects) {
        !           622:            linkTrackingRects = [[Storage allocFromZone:[self zone]] initCount:1 elementSize:sizeof(LinkRect) description:"{ffff@}"];
        !           623:        }
        !           624:        [self stopTrackingLink:link];
        !           625:        trackRect.link = link;
        !           626:        trackRect.dragged = dragged;
        !           627:        trackRect.all = all;
        !           628:        [linkTrackingRects addElement:&trackRect];
        !           629:     }
        !           630: 
        !           631:     return nil;
        !           632: }
        !           633: 
        !           634: /* Remove from linkTrackingRects. */
        !           635: 
        !           636: - stopTrackingLink:(NXDataLink *)link
        !           637: {
        !           638:     int i;
        !           639: 
        !           640:     for (i = [linkTrackingRects count]-1; i >= 0; i--) {
        !           641:        if (((LinkRect *)[linkTrackingRects elementAt:i])->link == link) {
        !           642:            [linkTrackingRects removeElementAt:i];
        !           643:            return self;
        !           644:        }
        !           645:     }
        !           646: 
        !           647:     return nil;
        !           648: }
        !           649: 
        !           650: @end

unix.superglobalmegacorp.com

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