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

1.1       root        1: #import "draw.h"
                      2: 
                      3: @implementation GraphicView(Pasteboard)
                      4: 
                      5: /* Methods to search through Pasteboard types lists. */
                      6: 
                      7: BOOL IncludesType(const NXAtom *types, NXAtom type)
                      8: {
                      9:     if (types) while (*types) if (*types++ == type) return YES;
                     10:     return NO;
                     11: }
                     12: 
                     13: NXAtom MatchTypes(const NXAtom *typesToMatch, const NXAtom *orderedTypes)
                     14: {
                     15:     while (orderedTypes && *orderedTypes) {
                     16:        if (IncludesType(typesToMatch, *orderedTypes)) return *orderedTypes;
                     17:        orderedTypes++;
                     18:     }
                     19:     return NULL;
                     20: }
                     21: 
                     22: NXAtom TextPasteType(const NXAtom *types)
                     23: /*
                     24:  * Returns the pasteboard type in the passed list of types which is preferred
                     25:  * by the Draw program for pasting.  The Draw program prefers PostScript over TIFF.
                     26:  */
                     27: {
                     28:     if (IncludesType(types, NXRTFPboardType)) return NXRTFPboardType;
                     29:     if (IncludesType(types, NXAsciiPboardType)) return NXAsciiPboardType;
                     30:     return NULL;
                     31: }
                     32: 
                     33: NXAtom ForeignPasteType(const NXAtom *types)
                     34: /*
                     35:  * Returns the pasteboard type in the passed list of types which is preferred
                     36:  * by the Draw program for pasting.  The Draw program prefers PostScript over TIFF.
                     37:  */
                     38: {
                     39:     NXAtom retval = TextPasteType(types);
                     40:     return retval ? retval : MatchTypes(types, [NXImage imagePasteboardTypes]);
                     41: }
                     42: 
                     43: NXAtom DrawPasteType(const NXAtom *types)
                     44: /*
                     45:  * Returns the pasteboard type in the passed list of types which is preferred
                     46:  * by the Draw program for pasting.  The Draw program prefers its own type
                     47:  * of course, then it prefers Text, then something NXImage can handle.
                     48:  */
                     49: {
                     50:     if (IncludesType(types, DrawPboardType)) return DrawPboardType;
                     51:     return ForeignPasteType(types);
                     52: }
                     53: 
                     54: NXAtom *TypesDrawExports(void)
                     55: {
                     56:     static NXAtom *exportList = NULL;
                     57:     if (!exportList) {
                     58:        NX_MALLOC(exportList, NXAtom, NUM_TYPES_DRAW_EXPORTS);
                     59:        exportList[0] = DrawPboardType;
                     60:        exportList[1] = NXPostScriptPboardType;
                     61:        exportList[2] = NXTIFFPboardType;
                     62:     }
                     63:     return exportList;
                     64: }
                     65: 
                     66: /* Lazy Pasteboard evaluation handler */
                     67: 
                     68: /*
                     69:  * IMPORTANT: The pasteboard:provideData: method is a factory method since the
                     70:  * factory object is persistent and there is no guarantee that the INSTANCE of
                     71:  * GraphicView that put the Draw format into the Pasteboard will be around
                     72:  * to lazily put PostScript or TIFF in there, so we keep one around (actually
                     73:  * we only create it when we need it) to do the conversion (scrapper).
                     74:  *
                     75:  * If you find this part of the code confusing, then you need not even
                     76:  * use the provideData: mechanism--simply put the data for all the different
                     77:  * types your program knows how to put in the Pasteboard in at the time
                     78:  * that you declareTypes:.
                     79:  */
                     80: 
                     81: /*
                     82:  * Converts the data in the Pasteboard from Draw internal format to
                     83:  * either PostScript or TIFF using the writeTIFFToStream: and writePSToStream:
                     84:  * methods.  It sends these messages to the scrapper (a GraphicView cached
                     85:  * to perform this very function).  Note that the scrapper view is put in
                     86:  * a window, but that window is off-screen, has no backing store, and no
                     87:  * title (and is thus very cheap).
                     88:  */
                     89: 
                     90: + convert:(NXTypedStream *)ts to:(const char *)type using:(SEL)writer toPasteboard:(Pasteboard *)pb
                     91: {
                     92:     Window *w;
                     93:     List *list;
                     94:     NXZone *zone;
                     95:     NXStream *stream;
                     96:     GraphicView *scrapper;
                     97:     NXRect scrapperFrame = {{0.0, 0.0}, {11.0*72.0, 14.0*72.0}};
                     98: 
                     99:     if (!ts) return self;
                    100: 
                    101:     zone = NXCreateZone(vm_page_size, vm_page_size, NO);
                    102:     NXNameZone(zone, "Scrapper");
                    103:     scrapper = [[GraphicView allocFromZone:zone] initFrame:&scrapperFrame];
                    104:     NXSetTypedStreamZone(ts, zone);
                    105:     list = NXReadObject(ts);
                    106:     [scrapper getBBox:&scrapperFrame of:list];
                    107:     scrapperFrame.size.width += scrapperFrame.origin.x;
                    108:     scrapperFrame.size.height += scrapperFrame.origin.y;
                    109:     scrapperFrame.origin.x = scrapperFrame.origin.y = 0.0;
                    110:     [scrapper sizeTo:scrapperFrame.size.width :scrapperFrame.size.height];
                    111:     w = [[Window allocFromZone:zone] initContent:&scrapperFrame
                    112:                                           style:NX_PLAINSTYLE
                    113:                                         backing:NX_NONRETAINED
                    114:                                      buttonMask:0
                    115:                                           defer:NO];
                    116:     [w reenableDisplay];
                    117:     [w setContentView:scrapper];
                    118:     stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
                    119:     [scrapper perform:writer with:(id)stream with:list];
                    120:     [pb writeType:type fromStream:stream];
                    121:     NXCloseMemory(stream, NX_FREEBUFFER);
                    122:     [list freeObjects];
                    123:     [list free];
                    124:     [w free];
                    125:     NXDestroyZone(zone);
                    126: 
                    127:     return self;
                    128: }
                    129: 
                    130: 
                    131: /*
                    132:  * Called by the Pasteboard whenever PostScript or TIFF data is requested
                    133:  * from the Pasteboard by some other application.  The current contents of
                    134:  * the Pasteboard (which is in the Draw internal format) is taken out and loaded
                    135:  * into a stream, then convert:to:using:toPasteboard: is called.  This
                    136:  * returns self if successful, nil otherwise.
                    137:  */
                    138: 
                    139: + pasteboard:(Pasteboard *)sender provideData:(const char *)type
                    140: {
                    141:     id retval = nil;
                    142:     NXStream *stream;
                    143:     NXTypedStream *ts;
                    144: 
                    145:     if ((type == NXPostScriptPboardType) || (type == NXTIFFPboardType)) {
                    146:        if (stream = [sender readTypeToStream:DrawPboardType]) {
                    147:            if (ts = NXOpenTypedStream(stream, NX_READONLY)) {
                    148:                retval = self;
                    149:                if (type == NXPostScriptPboardType) {
                    150:                    [self convert:ts to:type using:@selector(writePSToStream:usingList:) toPasteboard:sender];
                    151:                } else if (type == NXTIFFPboardType) {
                    152:                    [self convert:ts to:type using:@selector(writeTIFFToStream:usingList:) toPasteboard:sender];
                    153:                } else {
                    154:                    retval = nil;
                    155:                }
                    156:                NXCloseTypedStream(ts);
                    157:            }
                    158:            NXCloseMemory(stream, NX_FREEBUFFER);
                    159:        }
                    160:     }
                    161: 
                    162:     return retval;
                    163: }
                    164: 
                    165: /* Writing data in different forms (other than the internal Draw format) */
                    166: 
                    167: /*
                    168:  * Writes out the PostScript generated by drawing all the objects in the
                    169:  * glist.  The bounding box of the generated encapsulated PostScript will
                    170:  * be equal to the bounding box of the objects in the glist (NOT the
                    171:  * bounds of the view).
                    172:  */
                    173: 
                    174: - writePSToStream:(NXStream *)stream
                    175: {
                    176:     NXRect bbox;
                    177: 
                    178:     if (stream) {
                    179:        if (([glist count] == 1) && [[glist objectAt:0] canEmitEPS]) {
                    180:            [[glist objectAt:0] writeEPSToStream:stream];
                    181:        } else {
                    182:            [self getBBox:&bbox of:glist];
                    183:            [self copyPSCodeInside:&bbox to:stream];
                    184:        }
                    185:     }
                    186: 
                    187:     return self;
                    188: }
                    189: 
                    190: /*
                    191:  * This is the same as writePSToStream:, but it lets you specify the list
                    192:  * of Graphics you want to generate PostScript for (does its job by swapping
                    193:  * the glist for the list you provide temporarily).
                    194:  */
                    195: 
                    196: - writePSToStream:(NXStream *)stream usingList:list
                    197: {
                    198:     List *savedglist;
                    199: 
                    200:     savedglist = glist;
                    201:     glist = list;
                    202:     [self writePSToStream:stream];
                    203:     glist = savedglist;
                    204: 
                    205:     return self;
                    206: }
                    207: 
                    208: /*
                    209:  * Images all of the objects in the glist and writes out the result in
                    210:  * the Tagged Image File Format (TIFF).  The image will not have alpha in it.
                    211:  */
                    212: 
                    213: - writeTIFFToStream:(NXStream *)stream
                    214: {
                    215:     NXRect sbounds;
                    216:     Window *tiffCache;
                    217:     NXBitmapImageRep *bm;
                    218: 
                    219:     if (!stream) return self;
                    220: 
                    221:     if (([glist count] == 1) && [[glist objectAt:0] canEmitTIFF]) {
                    222:        [[glist objectAt:0] writeTIFFToStream:stream];
                    223:     } else {
                    224:        tiffCache = [self createCacheWindow:nil];
                    225:        [tiffCache setDepthLimit:NX_TwentyFourBitRGBDepth];
                    226:        [self cacheList:glist into:tiffCache withTransparentBackground:NO];
                    227:        [self getBBox:&sbounds of:glist];
                    228:        [[tiffCache contentView] lockFocus];
                    229:        sbounds.origin.x = sbounds.origin.y = 0.0;
                    230:        bm = [[NXBitmapImageRep alloc] initData:NULL fromRect:&sbounds];
                    231:        [[tiffCache contentView] unlockFocus];
                    232:        [bm writeTIFF:stream usingCompression:NX_TIFF_COMPRESSION_LZW];
                    233:        [bm free];
                    234:        [tiffCache free];
                    235:     }
                    236: 
                    237:     return self;
                    238: }
                    239: 
                    240: /*
                    241:  * This is the same as writeTIFFToStream:, but it lets you specify the list
                    242:  * of Graphics you want to generate TIFF for (does its job by swapping
                    243:  * the glist for the list you provide temporarily).
                    244:  */
                    245: 
                    246: - writeTIFFToStream:(NXStream *)stream usingList:list
                    247: {
                    248:     List *savedglist;
                    249: 
                    250:     savedglist = glist;
                    251:     glist = list;
                    252:     [self writeTIFFToStream:stream];
                    253:     glist = savedglist;
                    254: 
                    255:     return self;
                    256: }
                    257: 
                    258: /* Writing the selection to a stream */
                    259: 
                    260: - copySelectionAsPSToStream:(NXStream *)stream
                    261: {
                    262:     return (stream && [slist count]) ? [self writePSToStream:stream usingList:slist] : nil;
                    263: }
                    264: 
                    265: - copySelectionAsTIFFToStream:(NXStream *)stream
                    266: {
                    267:     return (stream && [slist count]) ? [self writeTIFFToStream:stream usingList:slist] : nil;
                    268: }
                    269: 
                    270: - copySelectionToStream:(NXStream *)stream
                    271: {
                    272:     NXTypedStream *ts;
                    273: 
                    274:     if ([slist count]) {
                    275:        ts = NXOpenTypedStream(stream, NX_WRITEONLY);
                    276:        NXWriteRootObject(ts, slist);
                    277:        NXCloseTypedStream(ts);
                    278:     } else {
                    279:        return nil;
                    280:     }
                    281: 
                    282:     return self;
                    283: }
                    284: 
                    285: /* Pasteboard-related target/action methods */
                    286: 
                    287: - cut:sender
                    288: /*
                    289:  * Calls copy: then delete:.
                    290:  */
                    291: {
                    292:     id change;
                    293: 
                    294:     if ([slist count] > 0) {
                    295:        change = [[CutGraphicsChange alloc] initGraphicView:self];
                    296:        [change startChange];
                    297:            [self copy:sender];
                    298:            lastCutChangeCount = lastCopiedChangeCount;
                    299:            [self delete:sender];
                    300:            consecutivePastes = 0;
                    301:         [change endChange];
                    302:        return self;
                    303:     } else {
                    304:        return nil;
                    305:     }
                    306: }
                    307: 
                    308: - copy:sender
                    309: {
                    310:     if ([slist count]) {
                    311:        [self copyToPasteboard:[Pasteboard new]];
                    312:        lastPastedChangeCount = [[Pasteboard new] changeCount];
                    313:        lastCopiedChangeCount = [[Pasteboard new] changeCount];
                    314:        consecutivePastes = 1;
                    315:        originalPaste = [slist objectAt:0];
                    316:     }
                    317:     return self;
                    318: }
                    319: 
                    320: - paste:sender
                    321: {
                    322:     return [self paste:sender andLink:DontLink];
                    323: }
                    324: 
                    325: - pasteAndLink:sender
                    326: {
                    327:     return [self paste:sender andLink:Link];
                    328: }
                    329: 
                    330: - link:sender
                    331: {
                    332:     return [self paste:sender andLink:LinkOnly];
                    333: }
                    334: 
                    335: /* Methods to write to/read from the pasteboard */
                    336: 
                    337: /*
                    338:  * Puts all the objects in the slist into the Pasteboard by archiving
                    339:  * the slist itself.  Also registers the PostScript and TIFF types since
                    340:  * the GraphicView knows how to convert its internal type to PostScript
                    341:  * or TIFF via the write{PS,TIFF}ToStream: methods.
                    342:  */
                    343: 
                    344: - copyToPasteboard:(Pasteboard *)pboard types:(NXAtom *)typesList
                    345: {
                    346:     char *data;
                    347:     NXStream *stream;
                    348:     const char *types[4];
                    349:     int i = 0, length, maxlen;
                    350: 
                    351:     if ([slist count]) {
                    352:        types[i++] = DrawPboardType;
                    353:        if (!typesList || IncludesType(typesList, NXPostScriptPboardType)) {
                    354:            types[i++] = NXPostScriptPboardType;
                    355:        }
                    356:        if (!typesList || IncludesType(typesList, NXTIFFPboardType)) {
                    357:            types[i++] = NXTIFFPboardType;
                    358:        }
                    359:        [pboard declareTypes:types num:i owner:[self class]];
                    360:        stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
                    361:        [self copySelectionToStream:stream];
                    362:        NXGetMemoryBuffer(stream, &data, &length, &maxlen);
                    363:        [pboard writeType:DrawPboardType data:data length:length];
                    364:        NXCloseMemory(stream, NX_FREEBUFFER);
                    365:        [self writeLinkToPasteboard:pboard types:typesList];
                    366:        return self;
                    367:     } else {
                    368:        return nil;
                    369:     }
                    370: }
                    371: 
                    372: - copyToPasteboard:(Pasteboard *)pboard
                    373: {
                    374:     return [self copyToPasteboard:pboard types:NULL];
                    375: }
                    376: 
                    377: /*
                    378:  * Pastes any data that comes from another application.
                    379:  * Basically this is the "else" in pasteFromPasteboard: below if there
                    380:  * is no Draw internal format in the Pasteboard.  This is also called
                    381:  * from the drag stuff (see gvDrag.m).
                    382:  */
                    383: 
                    384: - (BOOL)pasteForeignDataFromPasteboard:(Pasteboard *)pboard andLink:(LinkType)doLink at:(const NXPoint *)center
                    385: {
                    386:     NXDataLink *link = nil;
                    387:     Graphic *graphic = nil;
                    388: 
                    389:     if (!linkManager) doLink = DontLink;
                    390: 
                    391:     if (doLink) link = [[NXDataLink alloc] initFromPasteboard:pboard];
                    392:     if (link && (doLink == LinkOnly)) {
                    393:        graphic = [[Image allocFromZone:[self zone]] initWithLinkButton];
                    394:     } else {
                    395:        graphic = [[TextGraphic allocFromZone:[self zone]] initFromPasteboard:pboard];
                    396:        if (!graphic) graphic = [[Image allocFromZone:[self zone]] initFromPasteboard:pboard];
                    397:     }
                    398:     [self deselectAll:self];
                    399:     if (doLink && link) {
                    400:        if ([self addLink:link toGraphic:graphic at:center update:UPDATE_NORMALLY]) return YES;
                    401:     } else if (graphic) {
                    402:        if ([self placeGraphic:graphic at:center]) return YES;
                    403:     }
                    404: 
                    405:     return NO;
                    406: }
                    407: 
                    408: /*
                    409:  * Pastes any type available from the specified Pasteboard into the GraphicView.
                    410:  * If the type in the Pasteboard is the internal type, then the objects
                    411:  * are simply added to the slist and glist.  If it is PostScript or TIFF,
                    412:  * then an Image object is created using the contents of
                    413:  * the Pasteboard.  Returns a list of the pasted objects (which should be freed
                    414:  * by the caller).
                    415:  */
                    416: 
                    417: - pasteFromPasteboard:(Pasteboard *)pboard andLink:(LinkType)doLink at:(const NXPoint *)center
                    418: {
                    419:     int i;
                    420:     id change;
                    421:     NXStream *stream;
                    422:     NXTypedStream *ts;
                    423:     List *pblist = nil;
                    424:     Graphic *graphic = nil;
                    425:     BOOL pasteDrawType = NO;
                    426: 
                    427:     if (!linkManager) doLink = DontLink;
                    428:     if (!doLink) pasteDrawType = IncludesType([pboard types], DrawPboardType);
                    429: 
                    430:     if (pasteDrawType) {
                    431:        stream = [pboard readTypeToStream:DrawPboardType];
                    432:        ts = NXOpenTypedStream(stream, NX_READONLY);
                    433:        pblist = NXReadObject(ts);
                    434:        if (i = [pblist count]) {
                    435:            change = [[PasteGraphicsChange alloc] initGraphicView:self graphics:pblist];
                    436:            [change startChange];
                    437:                [self deselectAll:self];
                    438:                while (i--) {
                    439:                    graphic = [pblist objectAt:i];
                    440:                    [slist insertObject:graphic at:0];
                    441:                    [glist insertObject:graphic at:0];
                    442:                    if ([graphic mightBeLinked]) {
                    443:                        BOOL useNewId = ([pboard changeCount] != lastCutChangeCount) || consecutivePastes;
                    444:                        [graphic readLinkFromPasteboard:pboard usingManager:linkManager useNewIdentifier:useNewId];
                    445:                    }
                    446:                    gvFlags.groupInSlist = gvFlags.groupInSlist || [graphic isKindOf:[Group class]];
                    447:                }
                    448:            [change endChange];
                    449:        } else {
                    450:            [pblist free];
                    451:            pblist = nil;
                    452:        }
                    453:        NXCloseTypedStream(ts);
                    454:        NXCloseMemory(stream, NX_FREEBUFFER);
                    455:     } else {
                    456:        [self pasteForeignDataFromPasteboard:pboard andLink:doLink at:center];
                    457:     }
                    458: 
                    459:     return pblist;
                    460: }
                    461: 
                    462: /*
                    463:  * Pastes from the normal pasteboard.
                    464:  * This paste implements "smart paste" which goes like this: if the user
                    465:  * pastes in a single item (a Group is considered a single item), then
                    466:  * pastes that item again and moves that second item somewhere, then
                    467:  * subsequent pastes will be positioned at the same offset between the
                    468:  * first and second pastes (this is also known as "transform again").
                    469:  */
                    470: 
                    471: - paste:sender andLink:(LinkType)doLink
                    472: {
                    473:     List *pblist;
                    474:     NXPoint offset;
                    475:     Graphic *graphic;
                    476:     Pasteboard *pboard;
                    477:     NXRect originalBounds, secondBounds;
                    478:     static Graphic *secondPaste;
                    479:     static NXPoint pasteOffset;
                    480: 
                    481:     pboard = [Pasteboard new];
                    482:     pblist = [self pasteFromPasteboard:pboard andLink:doLink at:NULL];
                    483: 
                    484:     if (pblist && IncludesType([pboard types], DrawPboardType)) {
                    485:        graphic = ([pblist count] == 1) ? [pblist objectAt:0] : nil;
                    486:        if (lastPastedChangeCount != [pboard changeCount]) {
                    487:            consecutivePastes = 0;
                    488:            lastPastedChangeCount = [pboard changeCount];
                    489:            originalPaste = graphic;
                    490:        } else {
                    491:            if (consecutivePastes == 1) {       /* smart paste */
                    492:                if (gvFlags.gridDisabled) {     /* offset to grid if turned off */
                    493:                    pasteOffset.x = 10.0;
                    494:                    pasteOffset.y = -10.0;
                    495:                } else {
                    496:                    pasteOffset.x = (float)gvFlags.grid;
                    497:                    pasteOffset.y = -(float)gvFlags.grid;
                    498:                }
                    499:                secondPaste = graphic;
                    500:            } else if ((consecutivePastes == 2) && graphic) {
                    501:                [originalPaste getBounds:&originalBounds];
                    502:                [secondPaste getBounds:&secondBounds];
                    503:                pasteOffset.x = secondBounds.origin.x - originalBounds.origin.x;
                    504:                pasteOffset.y = secondBounds.origin.y - originalBounds.origin.y;
                    505:            }
                    506:            offset.x = pasteOffset.x * consecutivePastes;
                    507:            offset.y = pasteOffset.y * consecutivePastes;
                    508:            [slist makeObjectsPerform:@selector(moveBy:) with:(id)&offset];
                    509:        }
                    510:        consecutivePastes++;
                    511:        [self recacheSelection];
                    512:     }
                    513: 
                    514:     [pblist free];
                    515: 
                    516:     return self;
                    517: }
                    518: 
                    519: @end

unix.superglobalmegacorp.com

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