This is PlotView.m in view mode; [Download] [Up]
/* * Copyright (C) 1993 Robert Davis * * This program is free software; you can redistribute it and/or * modify it under the terms of Version 2, or any later version, of * the GNU General Public License as published by the Free Software * Foundation. */ static char RCSId[]="$Id: PlotView.m,v 1.15 1993/05/30 10:01:55 davis Exp $"; #import <appkit/Application.h> #import <appkit/ClipView.h> #import <appkit/Font.h> #import <appkit/FontManager.h> #import <appkit/Matrix.h> #import <appkit/MenuCell.h> #import <appkit/NXImage.h> #import <appkit/Pasteboard.h> #import <appkit/SavePanel.h> #import <appkit/Text.h> #import <defaults/defaults.h> /* NXGetTempFilename() */ #import <dpsclient/psops.h> #import <libc.h> /* unlink(), MAXPATHLEN */ #import "GnuplotPlot.h" #import "PlotView.h" @interface PlotView (Private) - (const char *) _writeEPSToTempFile; - _unlinkTempFile; @end /* Does the list of NXAtoms "types" include the NXAtom "type"? */ static BOOL includesType(NXAtom *types, NXAtom type) { while (types && *types) { if (type == *types) return YES; types++; } return NO; } @implementation PlotView - initFrame:(const NXRect *)frameRect; { const char *const sendTypes[] = {NXPostScriptPboardType, NXTIFFPboardType, NULL}; [super initFrame:frameRect]; [self setClipping: NO]; /* Because we're in a ClipView already */ [self drawInSuperview]; /* Use our ClipView's coordinate system */ fullPath = NULL; epsStream = NULL; tempFileNeedsUpdate = YES; /* Setup services */ [NXApp registerServicesMenuSendTypes: sendTypes andReturnTypes: NULL]; return self; } /* * This method, in addition to freeing the image, unlinks the * temporary EPS file if it exists. */ - free { [self _unlinkTempFile]; if (image) [image free]; return [super free]; } - (BOOL) acceptsFirstResponder { return YES; /* So the font can be changed with fontpanel */ } - (BOOL)acceptsFirstMouse { return YES; /* So we can drag the plot to other apps */ } /* * Redisplays the contents of the view by compositing the image onto * the view. */ - drawSelf:(NXRect *)r :(int) count { PSsetgray(NX_WHITE); /* For color machines, we must clear */ NXRectFill(&(r[0])); /* the window to white. */ if (image) [image composite:NX_SOVER fromRect:&(r[0]) toPoint:&(r[0].origin)]; return self; } - mouseDown:(NXEvent *)theEvent { if (image) { int oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; BOOL loop = YES; NXEvent mouseDownEvent = *theEvent; NXPoint downLoc = mouseDownEvent.location; while (loop) { NXEvent *nextEvent; NXPoint p; nextEvent = [NXApp getNextEvent:(NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)]; p = nextEvent->location; switch (nextEvent->type) { case NX_MOUSEDRAGGED: if ((fabs(downLoc.x - p.x) > 5) || /* Hysteresis */ (fabs(downLoc.y - p.y) > 5)) { NXPoint zero = {0.0, 0.0}; const char *const types[] = {NXPostScriptPboardType, NXTIFFPboardType, NXFilenamePboardType, NULL}; id pboard = [Pasteboard newName:NXDragPboard]; [self copyToPasteboard:pboard types:(NXAtom *)&types]; /* * Here's a neat trick: if the user presses the * alternate key during mouse down, the file will * be dragged out of the view instead of the * image and all its EPS, TIFF, etc. info. */ if ((&mouseDownEvent)->flags & NX_ALTERNATEMASK) { NXRect fileRect = {{(&mouseDownEvent)->location.x-24.0, (&mouseDownEvent)->location.y-24.0}, {48.0, 48.0}}; [[[window contentView] superview] convertRect:&fileRect toView:self]; [self dragFile:fullPath fromRect:&fileRect slideBack:YES event:&mouseDownEvent]; } else { [self dragImage:image at:&zero offset:&zero event:&mouseDownEvent pasteboard:pboard source:self slideBack:YES]; } loop = NO; } break; case NX_MOUSEUP: loop = NO; } } [window setEventMask:oldMask]; return self; } else /* no image to drag */ return nil; } - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag { return (NX_DragOperationCopy | NX_DragOperationGeneric); } - (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent { return YES; } /* * This method takes only one scale factor because we never want the * view to have different proportions than the image. Scale factor * of zero means size to window (so that the entire plot is visible * in the window). Returns the actual factor that the view was * scaled. */ - (float)scale:(float)scaleFactor { float factor; NXSize imageSize; NXRect ourRect; if (image) { if (scaleFactor) { factor = scaleFactor; } else { [superview getDocVisibleRect: &ourRect]; factor = ourRect.size.width / imageOrigWidth; if ((imageOrigHeight * factor) > ourRect.size.height) factor = ourRect.size.height / imageOrigHeight; } imageSize.width = imageOrigWidth * factor; imageSize.height = imageOrigHeight * factor; [image setSize:&imageSize]; /* Show the newly sized image. */ NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width; NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height; [superview descendantFrameChanged:self]; [self display]; return factor; } else return 0.0; } - (BOOL)validateCommand:menuCell { SEL action = [menuCell action]; if (action == @selector(copy:)) return image? YES :NO; else if (action == @selector(copyFont:)) { [[FontManager new] setEnabled:image? YES : NO]; return image? YES :NO; } else if (action == @selector(pasteFont:)) return image? YES :NO; return YES; } - changeFont:sender { id newfont; id doc; newfont = [sender convertFont: [(doc = [window delegate]) currentFont]]; [doc setCurrentFont:newfont]; return self; } - copyFont:sender { /* * This method is invoked when a user presses the "Copy Font" * menu button. We're supposed to put the current font on the * font pasteboard, and the easiest way to do that is to take * advantage of the Text class's ability to do that. Every step * here is necessary: we create a Text object, set it to RTF * (non-monofont), make a selection (even though there is no text * to select, there must be a selection), set the font to the * doc's current font, tell the text object to copy the font to * the font pasteboard, and then free the text object. */ [[[[[[[Text allocFromZone: [self zone]] init] setMonoFont: NO] setSel: 0:1] setFont: [[window delegate] currentFont]] copyFont: sender] free]; return self; } - pasteFont:sender { Text *text = [[Text allocFromZone: [self zone]] init]; /* * We must specify that the text is not monofont (i.e. is RTF) * and there must be a selection in order to paste. There * doesn't need to be any text. */ [[text setMonoFont:NO] setSel:0 :1]; if ([text pasteFont:sender]) /* Returns nil if there's trouble */ [[window delegate] setCurrentFont:[text font]]; [text free]; return self; } - validRequestorForSendType:(NXAtom)sendType andReturnType:(NXAtom)returnType /* * Services menu support. * We are a valid requestor if the send type is EPS and there is no * return data from the request. */ { if (image && ((sendType == NXPostScriptPboardType) || (sendType == NXTIFFPboardType)) && (!returnType || !*returnType)) return self; return [superview validRequestorForSendType:sendType andReturnType:returnType]; } - (BOOL) writeSelectionToPasteboard:pboard types:(NXAtom *)types /* * Services menu support. * Here we are asked by the Services menu mechanism to supply * the image data (which we said we were a valid requestor for * in the above method). */ { while (types && *types) { if ((*types == NXPostScriptPboardType) || (*types == NXTIFFPboardType)) break; types++; } if ([self copyToPasteboard:pboard types:types]) { return YES; } else { return NO; } } - copy:sender { return [self copyToPasteboard:[Pasteboard new]]; } /* * A generic copyToPasteboard: copies our contents in all possible * representations. */ - copyToPasteboard:pboard { NXAtom types[] = {NXPostScriptPboardType, NXTIFFPboardType, NXFilenamePboardType, NULL}; return [self copyToPasteboard:pboard types:types]; } - copyToPasteboard:pboard types:(NXAtom *)typesList { if (image && typesList && *typesList) { NXStream *stream; const char *types[4]; int count; count = 0; if (includesType (typesList, NXPostScriptPboardType)) types[count++] = NXPostScriptPboardType; if (includesType (typesList, NXTIFFPboardType)) types[count++] = NXTIFFPboardType; if (includesType (typesList, NXFilenamePboardType)) types[count++] = NXFilenamePboardType; types[count] = NULL; if (count) [pboard declareTypes:types num:count owner:[self class]]; else { types[0] = NXPostScriptPboardType; types[1] = NULL; [pboard declareTypes:types num:1 owner:[self class]]; } if (includesType(types, NXPostScriptPboardType)) { stream = NXOpenMemory(NULL, 0, NX_WRITEONLY); [self copyPSToStream:stream]; [pboard writeType:NXPostScriptPboardType fromStream:stream]; NXCloseMemory(stream, NX_FREEBUFFER); } if (includesType(types, NXTIFFPboardType)) { stream = NXOpenMemory(NULL, 0, NX_READWRITE); [self copyTIFFToStream:stream]; [pboard writeType:NXTIFFPboardType fromStream:stream]; NXCloseMemory(stream, NX_FREEBUFFER); } if (includesType(types, NXFilenamePboardType)) { const char *path = [self _writeEPSToTempFile]; if (path) [pboard writeType:NXFilenamePboardType data:path length:strlen(path)]; } return self; } else return nil; } - copyPSToStream:(NXStream *)stream { if (epsStream) { char *streambuf; int len, maxlen; NXGetMemoryBuffer(epsStream, &streambuf, &len, &maxlen); NXPrintf (stream, "%s", streambuf); NXFlush (stream); return self; } return nil; } - copyTIFFToStream:(NXStream *)stream { if (image && stream) { [image writeTIFF:stream]; return self; } return nil; } - newStream:(NXStream *) aStream { NXSize imageSize; if (image) [image free]; if (aStream) { NXSeek(aStream, 0, NX_FROMSTART); image = [[[[NXImage allocFromZone: [self zone]] initFromStream: aStream] setScalable: YES] setDataRetained: YES]; [image getSize:&imageSize]; imageOrigWidth = imageSize.width; imageOrigHeight = imageSize.height; NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width; NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height; epsStream = aStream; } else { NX_WIDTH(&bounds) = NX_HEIGHT(&bounds) = NX_WIDTH(&frame) = NX_HEIGHT(&frame) = 0; imageOrigWidth = imageOrigHeight = 0; image = nil; epsStream = NULL; } tempFileNeedsUpdate = YES; return self; } - (BOOL)saveEPSToFile:(const char *)filename { /* * Saves the epsStream to a file as Encapsulated PostScript. * Assumes filename is a validly formed, unique pathname. (It * will overwrite the file if filename is not unique.) */ return !NXSaveToFile (epsStream, filename); } - (BOOL)saveTIFFToFile:(const char *)filename { /* * Saves the epsStream to a file as TIFF. * Assumes filename is a validly formed, unique pathname. (It * will overwrite the file if filename is not unique.) */ NXStream *newStream; if (newStream = NXOpenMemory (NULL, 0, NX_WRITEONLY)) { BOOL returnval = YES; [self copyTIFFToStream:newStream]; if (NXSaveToFile (newStream, filename)) returnval = NO; NXCloseMemory (newStream, NX_FREEBUFFER); return returnval; } return NO; } // Shuts up the compiler about unused RCSId - (const char *) rcsid { return RCSId; } @end @implementation PlotView (Private) - _unlinkTempFile { if (fullPath) { if (*fullPath) unlink (fullPath); NXZoneFree ([self zone], fullPath); fullPath = NULL; } return self; } - (const char *) _writeEPSToTempFile { const char *realName; char *cur; int pos; /* * If epsStream has been written to a temp file before, and the * epsStream hasn't changed since then, we don't need to write * the temp file again. This checks to see if the stream has * changed. It also should (but doesn't) check to see if the * temp file still exists and is accessable (readable). */ if (!tempFileNeedsUpdate) return fullPath; else if (!epsStream) { [self _unlinkTempFile]; return NULL; } [self _unlinkTempFile]; /* Get rid of previous temp file, if any */ fullPath = (char *) NXZoneMalloc ([self zone], MAXPATHLEN * sizeof (char)); *fullPath = '\0'; /* * We need to come up with a unique name for the temp file, which * will reside in /tmp. We use the name of the plot and change * it with NXGetTempFilename. */ realName = [[window delegate] name]; if (cur = rindex(realName, '/')) { /* The plot has been titled */ if (++cur) sprintf (fullPath, "/tmp/%s", cur); else return NULL; /* ...ERROR: path ends in slash */ } else /* The plot is untitled */ sprintf (fullPath, "/tmp/%s", realName); pos = strlen (fullPath); if (cur = rindex (fullPath, '.')) { /* The name has an extension */ pos -= strlen (cur); /* Replace it with .eps */ strcpy (cur, "xxxxxx.eps"); } else /* The name has no extension */ strcat (fullPath, "xxxxxx.eps"); NXGetTempFilename(fullPath, pos); /* Make sure filename is unique */ [self saveEPSToFile:fullPath]; /* Save the stream to the file */ tempFileNeedsUpdate = NO; return fullPath; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.