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