home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / Draw / gvPasteboard.m < prev    next >
Text File  |  1992-07-20  |  15KB  |  512 lines

  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.     staticRVitom *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.    RVpsetContentView: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 *saRVqlist;
  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 = lastCopieRVrngeCount;
  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 = DontLinkRVs   if (doLink) link = [[NXDataLink alloc] initFromPasteboard:pboard];
  390.     if (link && (doLink == LinkOnly)) {
  391.     graphic = [[Image allocFromZone:[self zone]] initWithLinkButton];
  392.     } else {
  393.     graphic = [[TextGraphic allocFromZone:[self zone]] initFromPasteboard:pboard];
  394.     if (!graphic) graphic = [[Image allocFromZone:[self zone]] initFromPasteboard:pboard];
  395.     }
  396.     [self deselectAll:self];
  397.     if (doLink && link) {
  398.     if ([self addLink:link toGraphic:graphic at:center update:UPDATE_NORMALLY]) return YES;
  399.     } else if (graphic) {
  400.     if ([self placeGraphic:graphic at:center]) return YES;
  401.     }
  402.  
  403.     return NO;
  404. }
  405.  
  406. /*
  407.  * Pastes any type available from the specified Pasteboard into the GraphicView.
  408.  * If the type in the Pasteboard is the internal type, then the objects
  409.  * are simply added to the slist and glist.  If it is PostScript or TIFF,
  410.  * then an Image object is created using the contents of
  411.  * the Pasteboard.  Returns a list of the pasted objects (which should be freed
  412.  * by the caller).
  413.  */
  414.  
  415. - pasteFromPasteboard:(Pasteboard *)pboard andLink:(LinkType)doLink at:(const NXPoint *)center
  416. {
  417.     int i;
  418.     id change;
  419.     NXStream *stream;
  420.     NXTypedStream *ts;
  421.     List *pblist = nil;
  422.     Graphic *graphic = nil;
  423.     BOOL pasteDrawType = NO;
  424.  
  425.     if (!linkManager) doLink = DontLink;
  426.     if (!doLink) pasteDrawType = IncludesType([pboard types], DrawPboardType);
  427.  
  428.     if (pasteDrawType) {
  429.     stream = [pboard readTypeToStream:DrawPboardType];
  430.     ts = NXOpenTypedStream(stream, NX_READONLY);
  431.     pblist = NXReadObject(ts);
  432.     if (i = [pblist count]) {
  433.         change = [[PasteGraphicsChange alloc] initGraphicView:self graphics:pblist];
  434.         [change startChange];
  435.         [self deselectAll:self];
  436.         while (i--) {
  437.             graphic = [pblist objectAt:i];
  438.             [slist insertObject:graphic at:0];
  439.             [glist insertObject:graphic at:0];
  440.             if ([graphic mightBeLinked]) {
  441.             [self readLinkForGraphic:graphic
  442.                   fromPasteboard:pboard
  443.                 useNewIdentifier:([pboard changeCount] != lastCutChangeCount)];
  444.             }
  445.             gvFlags.groupInSlist = gvFlags.groupInSlist || [graphic isKindOf:[Group class]];
  446.         }
  447.         [change endChange];
  448.     } else {
  449.         [pblist free];
  450.         pblist = nil;
  451.     }
  452.     NXCloseTypedStream(ts);
  453.     NXCloseMemory(stream, NX_FREEBUFFER);
  454.     } else {
  455.     [self pasteForeignDataFromPasteboard:pboard andLink:doLink at:center];
  456.     }
  457.  
  458.     return pblist;
  459. }
  460. RVt * Pastes from the normal pasteboard.
  461.  * This paste implements "smart paste" which goes like this: if the user
  462.  * pastes in a single item (a Group is considered a single item), then
  463.  * pastes that item again and moves that second item somewhere, then
  464.  * subsequent pastes will be positioned at the same offset between the
  465.  * first and second pastes (this is also known as "transform again").
  466.  */
  467.  
  468. - paste:sender andLink:(LinkType)doLink
  469. {
  470.     List *pblist;
  471.     NXPoint offset;
  472.     Graphic *graphic;
  473.     Pasteboard *pboard;
  474.     NXRect originalBounds, secondBounds;
  475.     static Graphic *secondPaste;
  476.     static NXPoint pasteOffset;
  477.  
  478.     pboard = [Pasteboard new];
  479.     pblist = [self pasteFromPasteboard:pboard andLink:doLink at:NULL];
  480.  
  481.     if (pblist && IncludesType([pboard types], DrawPboardType)) {
  482.     graphic = ([pblist count] == 1) ? [pblist objectAt:0] : nil;
  483.     if (lastPastedChangeCount != [pboard changeCount]) {
  484.         consecutivePastes = 0;
  485.         lastPastedChangeCount = [pboard changeCount];
  486.         originalPaste = graphic;
  487.     } else {
  488.         if (consecutivePastes == 1) {    /* smart paste */
  489.         pasteOffset.x = 10.0;
  490.         pasteOffset.y = -10.0;
  491.         secondPaste = graphic;
  492.         } else if ((consecutivePastes == 2) && graphic) {
  493.         [originalPaste getBounds:&originalBounds];
  494.         [secondPaste getBounds:&secondBounds];
  495.         pasteOffset.x = secondBounds.origin.x - originalBounds.origin.x;
  496.         pasteOffset.y = secondBounds.origin.y - originalBounds.origin.y;
  497.         }
  498.         offset.x = pasteOffset.x * consecutivePastes;
  499.         offset.y = pasteOffset.y * consecutivePastes;
  500.         [slist makeObjectsPerform:@selector(moveBy:) with:(id)&offset];
  501.     }
  502.     consecutivePastes++;
  503.     [self recacheSelection];
  504.     }
  505.  
  506.     [pblist free];
  507.  
  508.     return self;
  509. }
  510.  
  511. @end
  512.