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.