home *** CD-ROM | disk | FTP | other *** search
- #import "draw.h"
-
- @implementation GraphicView(Pasteboard)
-
- /* Methods to search through Pasteboard types lists. */
-
- BOOL IncludesType(const NXAtom *types, NXAtom type)
- {
- if (types) while (*types) if (*types++ == type) return YES;
- return NO;
- }
-
- NXAtom MatchTypes(const NXAtom *typesToMatch, const NXAtom *orderedTypes)
- {
- while (orderedTypes && *orderedTypes) {
- if (IncludesType(typesToMatch, *orderedTypes)) return *orderedTypes;
- orderedTypes++;
- }
- return NULL;
- }
-
- NXAtom TextPasteType(const NXAtom *types)
- /*
- * Returns the pasteboard type in the passed list of types which is preferred
- * by the Draw program for pasting. The Draw program prefers PostScript over TIFF.
- */
- {
- if (IncludesType(types, NXRTFPboardType)) return NXRTFPboardType;
- if (IncludesType(types, NXAsciiPboardType)) return NXAsciiPboardType;
- return NULL;
- }
-
- NXAtom ForeignPasteType(const NXAtom *types)
- /*
- * Returns the pasteboard type in the passed list of types which is preferred
- * by the Draw program for pasting. The Draw program prefers PostScript over TIFF.
- */
- {
- NXAtom retval = TextPasteType(types);
- return retval ? retval : MatchTypes(types, [NXImage imagePasteboardTypes]);
- }
-
- NXAtom DrawPasteType(const NXAtom *types)
- /*
- * Returns the pasteboard type in the passed list of types which is preferred
- * by the Draw program for pasting. The Draw program prefers its own type
- * of course, then it prefers Text, then something NXImage can handle.
- */
- {
- if (IncludesType(types, DrawPboardType)) return DrawPboardType;
- return ForeignPasteType(types);
- }
-
- NXAtom *TypesDrawExports(void)
- {
- staticRVitom *exportList = NULL;
- if (!exportList) {
- NX_MALLOC(exportList, NXAtom, NUM_TYPES_DRAW_EXPORTS);
- exportList[0] = DrawPboardType;
- exportList[1] = NXPostScriptPboardType;
- exportList[2] = NXTIFFPboardType;
- }
- return exportList;
- }
-
- /* Lazy Pasteboard evaluation handler */
-
- /*
- * IMPORTANT: The pasteboard:provideData: method is a factory method since the
- * factory object is persistent and there is no guarantee that the INSTANCE of
- * GraphicView that put the Draw format into the Pasteboard will be around
- * to lazily put PostScript or TIFF in there, so we keep one around (actually
- * we only create it when we need it) to do the conversion (scrapper).
- *
- * If you find this part of the code confusing, then you need not even
- * use the provideData: mechanism--simply put the data for all the different
- * types your program knows how to put in the Pasteboard in at the time
- * that you declareTypes:.
- */
-
- /*
- * Converts the data in the Pasteboard from Draw internal format to
- * either PostScript or TIFF using the writeTIFFToStream: and writePSToStream:
- * methods. It sends these messages to the scrapper (a GraphicView cached
- * to perform this very function). Note that the scrapper view is put in
- * a window, but that window is off-screen, has no backing store, and no
- * title (and is thus very cheap).
- */
-
- + convert:(NXTypedStream *)ts to:(const char *)type using:(SEL)writer toPasteboard:(Pasteboard *)pb
- {
- Window *w;
- List *list;
- NXZone *zone;
- NXStream *stream;
- GraphicView *scrapper;
- NXRect scrapperFrame = {{0.0, 0.0}, {11.0*72.0, 14.0*72.0}};
-
- if (!ts) return self;
-
- zone = NXCreateZone(vm_page_size, vm_page_size, NO);
- NXNameZone(zone, "Scrapper");
- scrapper = [[GraphicView allocFromZone:zone] initFrame:&scrapperFrame];
- NXSetTypedStreamZone(ts, zone);
- list = NXReadObject(ts);
- [scrapper getBBox:&scrapperFrame of:list];
- scrapperFrame.size.width += scrapperFrame.origin.x;
- scrapperFrame.size.height += scrapperFrame.origin.y;
- scrapperFrame.origin.x = scrapperFrame.origin.y = 0.0;
- [scrapper sizeTo:scrapperFrame.size.width :scrapperFrame.size.height];
- w = [[Window allocFromZone:zone] initContent:&scrapperFrame
- style:NX_PLAINSTYLE
- backing:NX_NONRETAINED
- buttonMask:0
- defer:NO];
- [w reenableDisplay];
- RVpsetContentView:scrapper];
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- [scrapper perform:writer with:(id)stream with:list];
- [pb writeType:type fromStream:stream];
- NXCloseMemory(stream, NX_FREEBUFFER);
- [list freeObjects];
- [list free];
- [w free];
- NXDestroyZone(zone);
-
- return self;
- }
-
-
- /*
- * Called by the Pasteboard whenever PostScript or TIFF data is requested
- * from the Pasteboard by some other application. The current contents of
- * the Pasteboard (which is in the Draw internal format) is taken out and loaded
- * into a stream, then convert:to:using:toPasteboard: is called. This
- * returns self if successful, nil otherwise.
- */
-
- + pasteboard:(Pasteboard *)sender provideData:(const char *)type
- {
- id retval = nil;
- NXStream *stream;
- NXTypedStream *ts;
-
- if ((type == NXPostScriptPboardType) || (type == NXTIFFPboardType)) {
- if (stream = [sender readTypeToStream:DrawPboardType]) {
- if (ts = NXOpenTypedStream(stream, NX_READONLY)) {
- retval = self;
- if (type == NXPostScriptPboardType) {
- [self convert:ts to:type using:@selector(writePSToStream:usingList:) toPasteboard:sender];
- } else if (type == NXTIFFPboardType) {
- [self convert:ts to:type using:@selector(writeTIFFToStream:usingList:) toPasteboard:sender];
- } else {
- retval = nil;
- }
- NXCloseTypedStream(ts);
- }
- NXCloseMemory(stream, NX_FREEBUFFER);
- }
- }
-
- return retval;
- }
-
- /* Writing data in different forms (other than the internal Draw format) */
-
- /*
- * Writes out the PostScript generated by drawing all the objects in the
- * glist. The bounding box of the generated encapsulated PostScript will
- * be equal to the bounding box of the objects in the glist (NOT the
- * bounds of the view).
- */
-
- - writePSToStream:(NXStream *)stream
- {
- NXRect bbox;
-
- if (stream) {
- if (([glist count] == 1) && [[glist objectAt:0] canEmitEPS]) {
- [[glist objectAt:0] writeEPSToStream:stream];
- } else {
- [self getBBox:&bbox of:glist];
- [self copyPSCodeInside:&bbox to:stream];
- }
- }
-
- return self;
- }
-
- /*
- * This is the same as writePSToStream:, but it lets you specify the list
- * of Graphics you want to generate PostScript for (does its job by swapping
- * the glist for the list you provide temporarily).
- */
-
- - writePSToStream:(NXStream *)stream usingList:list
- {
- List *saRVqlist;
-
- savedglist = glist;
- glist = list;
- [self writePSToStream:stream];
- glist = savedglist;
-
- return self;
- }
-
- /*
- * Images all of the objects in the glist and writes out the result in
- * the Tagged Image File Format (TIFF). The image will not have alpha in it.
- */
-
- - writeTIFFToStream:(NXStream *)stream
- {
- NXRect sbounds;
- Window *tiffCache;
- NXBitmapImageRep *bm;
-
- if (!stream) return self;
-
- if (([glist count] == 1) && [[glist objectAt:0] canEmitTIFF]) {
- [[glist objectAt:0] writeTIFFToStream:stream];
- } else {
- tiffCache = [self createCacheWindow:nil];
- [tiffCache setDepthLimit:NX_TwentyFourBitRGBDepth];
- [self cacheList:glist into:tiffCache withTransparentBackground:NO];
- [self getBBox:&sbounds of:glist];
- [[tiffCache contentView] lockFocus];
- sbounds.origin.x = sbounds.origin.y = 0.0;
- bm = [[NXBitmapImageRep alloc] initData:NULL fromRect:&sbounds];
- [[tiffCache contentView] unlockFocus];
- [bm writeTIFF:stream usingCompression:NX_TIFF_COMPRESSION_LZW];
- [bm free];
- [tiffCache free];
- }
-
- return self;
- }
-
- /*
- * This is the same as writeTIFFToStream:, but it lets you specify the list
- * of Graphics you want to generate TIFF for (does its job by swapping
- * the glist for the list you provide temporarily).
- */
-
- - writeTIFFToStream:(NXStream *)stream usingList:list
- {
- List *savedglist;
-
- savedglist = glist;
- glist = list;
- [self writeTIFFToStream:stream];
- glist = savedglist;
-
- return self;
- }
-
- /* Writing the selection to a stream */
-
- - copySelectionAsPSToStream:(NXStream *)stream
- {
- return (stream && [slist count]) ? [self writePSToStream:stream usingList:slist] : nil;
- }
-
- - copySelectionAsTIFFToStream:(NXStream *)stream
- {
- return (stream && [slist count]) ? [self writeTIFFToStream:stream usingList:slist] : nil;
- }
-
- - copySelectionToStream:(NXStream *)stream
- {
- NXTypedStream *ts;
-
- if ([slist count]) {
- ts = NXOpenTypedStream(stream, NX_WRITEONLY);
- NXWriteRootObject(ts, slist);
- NXCloseTypedStream(ts);
- } else {
- return nil;
- }
-
- return self;
- }
-
- /* Pasteboard-related target/action methods */
-
- - cut:sender
- /*
- * Calls copy: then delete:.
- */
- {
- id change;
-
- if ([slist count] > 0) {
- change = [[CutGraphicsChange alloc] initGraphicView:self];
- [change startChange];
- [self copy:sender];
- lastCutChangeCount = lastCopieRVrngeCount;
- [self delete:sender];
- consecutivePastes = 0;
- [change endChange];
- return self;
- } else {
- return nil;
- }
- }
-
- - copy:sender
- {
- if ([slist count]) {
- [self copyToPasteboard:[Pasteboard new]];
- lastPastedChangeCount = [[Pasteboard new] changeCount];
- lastCopiedChangeCount = [[Pasteboard new] changeCount];
- consecutivePastes = 1;
- originalPaste = [slist objectAt:0];
- }
- return self;
- }
-
- - paste:sender
- {
- return [self paste:sender andLink:DontLink];
- }
-
- - pasteAndLink:sender
- {
- return [self paste:sender andLink:Link];
- }
-
- - link:sender
- {
- return [self paste:sender andLink:LinkOnly];
- }
-
- /* Methods to write to/read from the pasteboard */
-
- /*
- * Puts all the objects in the slist into the Pasteboard by archiving
- * the slist itself. Also registers the PostScript and TIFF types since
- * the GraphicView knows how to convert its internal type to PostScript
- * or TIFF via the write{PS,TIFF}ToStream: methods.
- */
-
- - copyToPasteboard:(Pasteboard *)pboard types:(NXAtom *)typesList
- {
- char *data;
- NXStream *stream;
- const char *types[4];
- int i = 0, length, maxlen;
-
- if ([slist count]) {
- types[i++] = DrawPboardType;
- if (!typesList || IncludesType(typesList, NXPostScriptPboardType)) {
- types[i++] = NXPostScriptPboardType;
- }
- if (!typesList || IncludesType(typesList, NXTIFFPboardType)) {
- types[i++] = NXTIFFPboardType;
- }
- [pboard declareTypes:types num:i owner:[self class]];
- stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
- [self copySelectionToStream:stream];
- NXGetMemoryBuffer(stream, &data, &length, &maxlen);
- [pboard writeType:DrawPboardType data:data length:length];
- NXCloseMemory(stream, NX_FREEBUFFER);
- [self writeLinkToPasteboard:pboard types:typesList];
- return self;
- } else {
- return nil;
- }
- }
-
- - copyToPasteboard:(Pasteboard *)pboard
- {
- return [self copyToPasteboard:pboard types:NULL];
- }
-
- /*
- * Pastes any data that comes from another application.
- * Basically this is the "else" in pasteFromPasteboard: below if there
- * is no Draw internal format in the Pasteboard. This is also called
- * from the drag stuff (see gvDrag.m).
- */
-
- - (BOOL)pasteForeignDataFromPasteboard:(Pasteboard *)pboard andLink:(LinkType)doLink at:(const NXPoint *)center
- {
- NXDataLink *link = nil;
- Graphic *graphic = nil;
-
- if (!linkManager) doLink = DontLinkRVs if (doLink) link = [[NXDataLink alloc] initFromPasteboard:pboard];
- if (link && (doLink == LinkOnly)) {
- graphic = [[Image allocFromZone:[self zone]] initWithLinkButton];
- } else {
- graphic = [[TextGraphic allocFromZone:[self zone]] initFromPasteboard:pboard];
- if (!graphic) graphic = [[Image allocFromZone:[self zone]] initFromPasteboard:pboard];
- }
- [self deselectAll:self];
- if (doLink && link) {
- if ([self addLink:link toGraphic:graphic at:center update:UPDATE_NORMALLY]) return YES;
- } else if (graphic) {
- if ([self placeGraphic:graphic at:center]) return YES;
- }
-
- return NO;
- }
-
- /*
- * Pastes any type available from the specified Pasteboard into the GraphicView.
- * If the type in the Pasteboard is the internal type, then the objects
- * are simply added to the slist and glist. If it is PostScript or TIFF,
- * then an Image object is created using the contents of
- * the Pasteboard. Returns a list of the pasted objects (which should be freed
- * by the caller).
- */
-
- - pasteFromPasteboard:(Pasteboard *)pboard andLink:(LinkType)doLink at:(const NXPoint *)center
- {
- int i;
- id change;
- NXStream *stream;
- NXTypedStream *ts;
- List *pblist = nil;
- Graphic *graphic = nil;
- BOOL pasteDrawType = NO;
-
- if (!linkManager) doLink = DontLink;
- if (!doLink) pasteDrawType = IncludesType([pboard types], DrawPboardType);
-
- if (pasteDrawType) {
- stream = [pboard readTypeToStream:DrawPboardType];
- ts = NXOpenTypedStream(stream, NX_READONLY);
- pblist = NXReadObject(ts);
- if (i = [pblist count]) {
- change = [[PasteGraphicsChange alloc] initGraphicView:self graphics:pblist];
- [change startChange];
- [self deselectAll:self];
- while (i--) {
- graphic = [pblist objectAt:i];
- [slist insertObject:graphic at:0];
- [glist insertObject:graphic at:0];
- if ([graphic mightBeLinked]) {
- [self readLinkForGraphic:graphic
- fromPasteboard:pboard
- useNewIdentifier:([pboard changeCount] != lastCutChangeCount)];
- }
- gvFlags.groupInSlist = gvFlags.groupInSlist || [graphic isKindOf:[Group class]];
- }
- [change endChange];
- } else {
- [pblist free];
- pblist = nil;
- }
- NXCloseTypedStream(ts);
- NXCloseMemory(stream, NX_FREEBUFFER);
- } else {
- [self pasteForeignDataFromPasteboard:pboard andLink:doLink at:center];
- }
-
- return pblist;
- }
- RVt * Pastes from the normal pasteboard.
- * This paste implements "smart paste" which goes like this: if the user
- * pastes in a single item (a Group is considered a single item), then
- * pastes that item again and moves that second item somewhere, then
- * subsequent pastes will be positioned at the same offset between the
- * first and second pastes (this is also known as "transform again").
- */
-
- - paste:sender andLink:(LinkType)doLink
- {
- List *pblist;
- NXPoint offset;
- Graphic *graphic;
- Pasteboard *pboard;
- NXRect originalBounds, secondBounds;
- static Graphic *secondPaste;
- static NXPoint pasteOffset;
-
- pboard = [Pasteboard new];
- pblist = [self pasteFromPasteboard:pboard andLink:doLink at:NULL];
-
- if (pblist && IncludesType([pboard types], DrawPboardType)) {
- graphic = ([pblist count] == 1) ? [pblist objectAt:0] : nil;
- if (lastPastedChangeCount != [pboard changeCount]) {
- consecutivePastes = 0;
- lastPastedChangeCount = [pboard changeCount];
- originalPaste = graphic;
- } else {
- if (consecutivePastes == 1) { /* smart paste */
- pasteOffset.x = 10.0;
- pasteOffset.y = -10.0;
- secondPaste = graphic;
- } else if ((consecutivePastes == 2) && graphic) {
- [originalPaste getBounds:&originalBounds];
- [secondPaste getBounds:&secondBounds];
- pasteOffset.x = secondBounds.origin.x - originalBounds.origin.x;
- pasteOffset.y = secondBounds.origin.y - originalBounds.origin.y;
- }
- offset.x = pasteOffset.x * consecutivePastes;
- offset.y = pasteOffset.y * consecutivePastes;
- [slist makeObjectsPerform:@selector(moveBy:) with:(id)&offset];
- }
- consecutivePastes++;
- [self recacheSelection];
- }
-
- [pblist free];
-
- return self;
- }
-
- @end
-