home *** CD-ROM | disk | FTP | other *** search
/ OpenStep 4.2J (Developer) / os42jdev.iso / NextDeveloper / Examples / AppKit / Draw / gvPasteboard.m < prev    next >
Text File  |  1996-04-18  |  16KB  |  497 lines

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