home *** CD-ROM | disk | FTP | other *** search
-
- static char RCSId[]="$Id: GnuplotPlot.m,v 1.1.1.1 1993/03/18 03:33:38 davis Exp $";
-
-
- #import <appkit/Application.h>
- #import <appkit/FontManager.h>
- #import <appkit/Pasteboard.h>
- #import <appkit/SavePanel.h>
- #import <appkit/Window.h>
- #import <appkit/publicWraps.h> /* NXBeep() */
-
- #import <objc/List.h>
- #import <objc/NXStringTable.h>
-
- #import <sys/stat.h> /* stat() */
- #import <sys/types.h> /* stat() */
-
- #import <streams/streams.h>
-
- #import <libc.h> /* MAXPATHLEN */
- #import <strings.h> /* strlen(), strcpy() */
- #import <stdlib.h>
-
- #import "FunctionObject.h"
- #import "GnuplotPlot.h"
- #import "Gnuplot.h"
- #import "PlotView.h"
- #import "PopUpScrollView.h"
- #import "Status.h"
-
-
- @interface GnuplotPlot (Private)
-
- - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain;
- - _write:(const char *)filename;
- - _read:(const char *) filename;
-
- @end
-
-
-
- #define DEFAULT_WIN_WIDTH 380
- #define DEFAULT_WIN_HEIGHT 269
-
-
- extern int scanner (char expression[]);
- extern int almost_equals (int t_num, char *str);
- extern char input_line[];
-
- extern int nextfe_color; /* Is this a color machine? */
-
-
- /*** Preferences -- Pseudo Class Variables ***/
-
- static int constantUpdate = UPDATE_NOT3D; /* Replot after any change? */
- /* Default window size */
- static NXSize winSize = { DEFAULT_WIN_WIDTH, DEFAULT_WIN_HEIGHT };
-
-
-
- /*
- * This function computes a new location for each new window created.
- * p is the origin of the windows frame, w is the size of the window.
- */
- static void _newLocation (NXPoint *p, NXSize w)
- {
- static count = 0; /* This tracks all our instances */
-
- p->x += (21.0 * count);
- p->y -= (24.0 * count) + (w.height - DEFAULT_WIN_HEIGHT);
- count = (count > 10)? 0 : count+1;
- }
-
-
-
-
- static char *endOfCommand (char *aString)
- {
- char *cur;
- char quote[200];
- int numquotes = 0;
-
- for (cur = aString ; cur ; cur++)
- switch (*cur) {
- case '\n':
- case '\0':
- return cur;
- case ';':
- if (!numquotes)
- return cur;
- case '\'':
- case '"':
- if (numquotes && (quote[numquotes - 1] == *cur)) /* Close */
- --numquotes;
- else
- quote [numquotes++] = *cur; /* Open */
- }
-
- return NULL;
- }
-
-
-
-
- static BOOL isBadCommand (char *aString)
- {
- char *end, was;
- BOOL returnval = NO;
-
- if (aString && *aString) {
-
- /* Locate the end of the command */
-
- end = endOfCommand (aString);
- was = *end;
- *end = '\0';
-
- strcpy (input_line, aString);
- scanner (aString);
-
- returnval = (almost_equals (0, "se$t") &&
-
- (almost_equals (1, "o$utput") ||
- almost_equals (1, "t$erminal"))) ||
-
- almost_equals (0, "pa$use");
-
- *end = was;
-
- }
-
- return returnval;
- }
-
-
- static void commandcat (char *dest, char *source)
- {
- char *cursource, *curdest, *end;
-
- end = endOfCommand (source);
-
- curdest = dest + strlen (dest);
- for (cursource = source ; cursource && (cursource != end); cursource++)
- *(curdest++) = *cursource;
-
- *(curdest++) = '\n';
- *curdest = '\0';
- }
-
-
- /*
- * stripCommands Removes the 'set output "output.eps"\n', 'set
- * terminal ...\n', and 'pause ...' lines and
- * comments from the string.
- */
- static char *stripCommands (char **aString)
- {
- char *cur, *newText;
-
- if (aString && *aString) {
- cur = *aString;
- newText = (char *) malloc (strlen (*aString));
-
- *newText = '\0';
-
- while (cur && *cur) {
- if (!isBadCommand (cur))
- commandcat (newText, cur);
-
- cur = endOfCommand (cur);
- cur = (cur ? cur + 1 : NULL);
- }
-
- strcpy (*aString, newText);
- return *aString;
- }
-
- return NULL;
- }
-
-
-
- @implementation GnuplotPlot
-
-
- + initialize
- {
- /*
- * This is probably temporary -- until color is handled better,
- * this just checks to see if we're on a color machine and sets
- * the color variable of the terminal accordingly.
- */
- nextfe_color = (([NXApp colorScreen]->depth) == NX_TwoBitGrayDepth)? 0 : 1;
- return self;
- }
-
-
-
- + setConstantUpdate:(int)updateType
- {
- constantUpdate = updateType;
- return self;
- }
-
-
- + setHalvePlot:(BOOL)condition
- {
- if (condition) {
- winSize.width = 380;
- winSize.height = 269;
- } else {
- winSize.width = 740;
- winSize.height = 521;
- }
-
- return [Status setHalvePlot:condition];
- }
-
-
- - init
- {
- return [self initFromFile: NULL];
- }
-
-
- - initFromFile:(const char *) filename
- {
- NXRect theFrame;
- const char *validSendTypes[2];
-
- [super init];
- controller = [NXApp delegate];
- zone = [self zone];
-
- [NXApp loadNibSection: "GnuplotPlot.nib"
- owner: self
- withNames: NO
- fromZone: zone];
-
- status = [[[Status allocFromZone:zone] init] setDelegate: self];
-
- [plotScrollView setVertScrollerRequired: YES];
- [plotScrollView setHorizScrollerRequired: YES];
- [plotScrollView setBorderType:NX_BEZEL];
-
-
- plotView = [[PlotView allocFromZone:zone] initFrame:NULL];
- [[plotScrollView setDocView:plotView] free];
-
- stringSet = [controller stringSet];
- fullPath = NULL;
-
- /* Setup services */
- validSendTypes[0] = NXFilenamePboardType;
- validSendTypes[1] = NULL;
- [NXApp registerServicesMenuSendTypes: validSendTypes andReturnTypes: NULL];
-
- [window sizeWindow:winSize.width :winSize.height]; /* Default size */
-
- [window getFrame:&theFrame]; /* Position the window */
- _newLocation (&theFrame.origin, winSize);
- [window moveTo:NX_X(&theFrame) :NX_Y(&theFrame)];
-
- [window setMiniwindowIcon: "GnuplotDoc.tiff"];
-
- if (filename) {
- [self setName:filename];
- isTitled = YES;
- [window makeKeyAndOrderFront:self];
- if (![self plotFromFile:filename]) {
- [NXApp delayedFree:self];
- return nil;
- }
- } else {
- [window makeKeyAndOrderFront:self];
- [self setName: NULL];
- isTitled = NO;
- }
-
- [self setDocEdited: NO];
-
- return self;
- }
-
-
- - free
- {
- NXZoneFree (zone, fullPath);
- NXZoneFree (zone, readText);
-
- [status free];
-
- if (window)
- [window free];
-
- return [super free];
- }
-
-
-
- /*** Target/Action ***/
-
-
- - save:sender
- {
- return [self _saveWithNewName:NO retainNewName:YES];
- }
-
-
- - saveAs:sender
- {
- return [self _saveWithNewName:YES retainNewName:YES];
- }
-
-
- - saveTo:sender
- {
- return [self _saveWithNewName:YES retainNewName:NO];
- }
-
-
- - revertToSaved:sender
- {
- switch (NXRunAlertPanel ([stringSet valueForStringKey: "revertT"],
- [stringSet valueForStringKey: "revert"],
- [stringSet valueForStringKey: "revertB"],
- [stringSet valueForStringKey: "cancelB"],
- NULL, fullPath)) {
- case NX_ALERTDEFAULT:
- [status resetCurrent];
- [self plotFromFile:fullPath];
- [self setDocEdited:NO];
-
- /*
- * Make sure any panels that are showing info about this
- * document know that we just changed.
- */
- [controller updateApp];
- [[FontManager new] setSelFont:[status font] isMultiple:NO];
-
- return self;
- break;
- case NX_ALERTOTHER:
- return nil;
- break;
- }
-
- return self;
- }
-
-
-
- - close:sender
- {
- [window performClose:self];
- return self;
- }
-
-
- - print:sender;
- {
- return [plotView printPSCode:sender];
- }
-
-
-
- - setName: (const char *) name
- {
- char title[255];
- char *oldFullPath = fullPath;
-
- if (name) {
-
- fullPath = NXCopyStringBufferFromZone (name, zone);
- [window setTitleAsFilename:fullPath];
- isTitled = YES;
-
- } else {
-
- sprintf (title, "%s%d", [stringSet valueForStringKey: "untitled"],
- [controller numberNew]);
- fullPath = NXCopyStringBufferFromZone (title, zone);
- [window setTitle: title];
- isTitled = NO;
-
- }
-
- NXZoneFree (zone, oldFullPath);
-
- return self;
- }
-
-
- - (const char *) name
- {
- return fullPath;
- }
-
-
- - setCurrentFont:aFont
- {
- if (aFont) {
- [status setFont:aFont];
- }
-
- return self;
- }
-
-
- - currentFont
- {
- return [status font];
- }
-
-
-
- - window
- {
- return window;
- }
-
-
- - status
- {
- return status;
- }
-
-
- - plotFromFile:(const char *)aFullPath
- {
- id returnVal = self;
-
- if (![self _read:aFullPath]) /* _read into readText */
- return nil;
-
- stripCommands (&readText); /* Ignore output, terminal, and */
- /* multiple plot commands */
-
- [status setAppendix: readText];
-
- /*
- * We don't check the return status of plot: because even if
- * there are errors, we want the file to remain open -- maybe the
- * user can correct the errors without having to use vi.
- */
- [self plot:self];
- [status grabCurrent];
-
-
- [status setAppendix: NULL];
- NXZoneFree (zone, readText);
- readText = NULL;
-
- return returnVal;
- }
-
-
- - plot:sender
- {
- id returnVal = self;
-
- /*
- * We don't return nil if the settings don't allow us to create a
- * valid plot -- we simply don't plot.
- */
- if (![status canPlot]) {
-
- [plotView newStream:NULL];
-
- } else {
-
- char *newTitle;
- char *oldTitle = NXCopyStringBufferFromZone ([window title], zone);
-
- newTitle = NXZoneMalloc (zone, strlen (oldTitle) + 12);
- sprintf (newTitle, [stringSet valueForStringKey:"plotting..."],
- oldTitle);
- [window setTitle:newTitle];
- NXPing();
-
- /*
- * We do, however, report an error if the settings do allow
- * us to plot but we were unable to (e.g. if gnuplot bailed
- * out).
- */
- if (![status plot]) {
- [self reportScriptError:self];
- returnVal = nil;
- } else {
-
- [plotView newStream:[status stream]];
-
- /*
- * Make sure the scale of the doc matches the current
- * popUp setting
- */
- [plotScrollView update]; /* Not done at every window update */
-
- [window update];
- }
-
- [window setTitle:oldTitle];
- NXPing();
- NXZoneFree (zone, oldTitle);
- NXZoneFree (zone, newTitle);
- }
-
- return returnVal;
- }
-
-
-
-
- - reportScriptError: sender;
- {
- NXRunAlertPanel ([stringSet valueForStringKey:"scriptErrorT"],
- [stringSet valueForStringKey:"scriptError"],
- NULL, NULL, NULL, [self name]);
-
- [plotView newStream: NULL];
- [window display];
- return self;
- }
-
-
- - addDataFile:(const char *)aPath
- {
- FunctionObject *functionObject;
-
- if (![FunctionObject isAcceptableDataFile:aPath])
- return nil;
-
- functionObject = [[FunctionObject allocFromZone: [status zone]]
- initFromString: aPath];
- [functionObject setDataFile:YES];
- [[status functions] addObject:functionObject];
- [status reportSettingsChange:self];
-
- return self;
- }
-
-
-
- - setDocEdited:(BOOL) state
- {
- if (((constantUpdate == UPDATE_ALWAYS) ||
- ((constantUpdate == UPDATE_NOT3D) && ![status isThreeD])) && state)
- [self plot:self];
-
- [window setDocEdited:state];
-
- return self;
- }
-
- - (BOOL) isDocEdited
- {
- return [window isDocEdited];
- }
-
-
-
- - hideDocument:sender
- {
- [window orderOut:sender];
- return self;
- }
-
-
-
-
- /*** Services ***/
-
-
- - validRequestorForSendType:(NXAtom)sendType andReturnType:(NXAtom)returnType
- /*
- * Services menu support.
- * We are a valid requestor if the send type is filename and there is
- * no return data from the request.
- */
- {
- return (isTitled && (sendType == NXFilenamePboardType) &&
- (!returnType || !*returnType)) ? self : nil;
- }
-
- - (BOOL)writeSelectionToPasteboard:pboard types:(NXAtom *)types
- /*
- * Services menu support.
- * Here we are asked by the Services menu mechanism to supply the
- * filename (which we said we were a valid requestor for in the above
- * method).
- */
- {
- int save;
-
- if (isTitled) {
- while (types && *types)
- if (*types == NXFilenamePboardType)
- break;
- else
- types++;
-
- if (types && *types) {
- if ([self isDocEdited]) {
- save = NXRunAlertPanel (
- [stringSet valueForStringKey:"servicesT"],
- [stringSet valueForStringKey:"serviceSave"],
- [stringSet valueForStringKey:"save"],
- [stringSet valueForStringKey:"dont save"], NULL);
-
- if (save == NX_ALERTDEFAULT)
- [self save:self];
- }
- [pboard declareTypes:&NXFilenamePboardType num:1 owner:self];
- [pboard writeType:NXFilenamePboardType
- data:fullPath length:strlen (fullPath)+1];
- return YES;
- }
- }
-
- return NO;
- }
-
-
-
- /*** Autoupdate Methods ***/
-
- - (BOOL)validateCommand:menuCell
- {
- SEL action = [menuCell action];
- BOOL edited = [self isDocEdited];
- BOOL canPlot = [status canPlot];
-
- /*
- * Note that we only allow a document to be saved when the
- * settings validly produce a plot. Also, if we need to see if
- * the plot has a file with which it is associated.
- */
-
- if (action == @selector(save:))
- return (edited || !isTitled) && canPlot;
- else if (action == @selector(saveAs:))
- return canPlot;
- else if (action == @selector(saveTo:))
- return canPlot;
- else if (action == @selector(saveAll:)) /* See Gnuplot class */
- return (edited || !isTitled) && canPlot;
- else if (action == @selector(revertToSaved:))
- return (edited && isTitled);
- else if (action == @selector(print:))
- return canPlot;
- else if (action == @selector(plot:))
- return canPlot;
-
- return YES;
- }
-
-
-
-
- /*** Window Delegate Methods ***/
-
- - windowDidBecomeMain:sender
- {
- /* Update font panel */
- if (plotView) {
- [window makeFirstResponder: plotView];
- // [[FontManager new] setSelFont:[status font] isMultiple:NO];
- }
-
- [controller setCurrentDoc:self];
-
- return self;
- }
-
-
-
- - windowDidMiniaturize:sender
- {
- [controller setCurrentDoc:nil];
- return self;
- }
-
-
-
- - windowDidResignMain:sender
- {
- // [[FontManager new] setEnabled:NO];
- return self;
- }
-
-
-
- - windowDidResize:sender
- {
- int aTag;
-
- /*
- * Make sure the scale of the doc matches the current popUp
- * setting. (If it's set to "Always Fit," which is 0, then resizing
- * window should cause it to rescale.)
- */
- aTag = [plotScrollView currentTag];
- if (!aTag)
- [plotScrollView setCurrentTag: aTag];
-
- return self;
- }
-
-
- - windowDidUpdate:sender
- {
- [[FontManager new] setEnabled:([status canPlot] && [window isMainWindow])];
- return self;
- }
-
-
-
- - windowWillClose:sender
- {
- id returnVal = self;
- BOOL done = NO;
-
- if ([self isDocEdited]) {
-
- while (!done) { /* Keep asking until user tells what to do */
-
- switch (NXRunAlertPanel([stringSet valueForStringKey:"willCloseT"],
- [stringSet valueForStringKey:"willClose"],
- [stringSet valueForStringKey:"save"],
- [stringSet valueForStringKey:"dont save"],
- [stringSet valueForStringKey:"cancelB"],
- [self name])) {
- case NX_ALERTDEFAULT: /* Save */
- if (returnVal = [self save:self])
- done = YES;
- break;
- case NX_ALERTALTERNATE:
- returnVal = self; /* Don't Save */
- done = YES;
- break;
- case NX_ALERTOTHER: /* Cancel */
- returnVal = nil;
- done = YES;
- break;
-
- }
-
- }
-
- }
-
- if (returnVal) {
- plotScrollView = nil; /* Don't want stale instance vars */
- plotView = nil;
-
- [window orderOut:self];
- [controller docDidClose:self];
- [NXApp delayedFree: self];
- }
-
- return returnVal;
- }
-
-
- - windowWillResize:sender toSize:(NXSize *) frameSize
- {
- NXRect rect;
- float x, y, w, h;
-
- [plotView getFrame:&rect];
-
- /*
- * If the frame rectangle of the view has both a length and a
- * width, i.e. if there's something to see, we control the sizing
- * so it can never be bigger than the rectangle (except to allow
- * room for border and sliders).
- */
-
- if ((w = NX_WIDTH(&rect)) && (h = NX_HEIGHT(&rect))) {
-
- x = w + 22; /* Make room for borders & sliders */
- y = h + 49;
-
- /* If the current setting isn't "Always Fit" */
- if ([plotScrollView currentTag]) {
-
- if (frameSize->width > x) frameSize->width = x;
- if (frameSize->height > y) frameSize->height = y;
-
- }
-
- }
-
- /*
- * Don't allow the window to get small enough to lose the scroll
- * buttons and the pop-up list.
- */
-
- if (frameSize->width < 147) frameSize->width = 147;
- if (frameSize->height < 65) frameSize->height = 65;
-
- return self;
- }
-
-
-
- /*** Status Delegate Methods ***/
-
-
- - settingsDidChange:sender
- {
- [self setDocEdited: YES];
- return self;
- }
-
-
-
- // Shuts up the compiler about unused RCSId
- - (const char *) rcsid
- {
- return RCSId;
- }
-
-
- @end
-
-
-
- @implementation GnuplotPlot (Private)
-
-
- /*
- * This method was taken from the NeXTSTEP Developer Example
- * WhatsUpDoc. All varieties of save go through this routine. It
- * covers all the cases of running the Save Panel and retaining the
- * name chosen.
- */
- - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain
- {
- id savePanel;
- const char *saveName; /* filename to save into */
-
- if (doNewName || !isTitled) { /* saveAs or saveTo */
-
- savePanel = [SavePanel new];
- [savePanel setRequiredFileType:DOCUMENT_TYPE];
- if ([savePanel runModalForDirectory:fullPath
- file: (isTitled ? rindex (fullPath, '/') + 1
- : [self name])]) {
- saveName = [savePanel filename];
- } else
- return self; /* aborted out? */
-
-
- } else { /* ordinary Save */
-
- /*
- * In order to save a document, it must be able to produce a
- * valid plot and it must either be edited or be untitled.
- */
- if ((![self isDocEdited] && isTitled) || ![status canPlot])
- return nil;
-
- saveName = fullPath;
- }
-
- if (![self _write:saveName]) {
- NXRunAlertPanel ([stringSet valueForStringKey: "Save"],
- [stringSet valueForStringKey: "cantSave"],
- [stringSet valueForStringKey: "OKB"],
- NULL, NULL, saveName);
- return nil;
- }
- /* Update the document name */
- if (doRetain) { /* if requested */
- [self setName:saveName];
- [self setDocEdited:NO];
- }
-
- return self;
- }
-
-
-
- /*
- * This method does all the writing so any application-specific stuff
- * should go here.
- */
- - _write:(const char *)filename
- {
- NXModalSession *session;
- id modalPanel = NXGetAlertPanel ("Save", "Replotting %s.",
- NULL,NULL,NULL, [self name]);
-
- if ([Status lastplot] != status) {
- session = [NXApp beginModalSession:NULL for:modalPanel];
- [self plot:self];
- [NXApp endModalSession:session];
- NXFreeAlertPanel(modalPanel);
- }
-
- return [status saveToFile:filename];
- }
-
-
-
-
- - _read:(const char *) filename
- {
- NXStream *stream;
- struct stat fileinfo;
-
- if (stat (filename, &fileinfo)) {
- NXRunAlertPanel ([stringSet valueForStringKey: "openT"],
- [stringSet valueForStringKey: "cantOpen"],
- [stringSet valueForStringKey: "OKB"],
- NULL, NULL, filename);
- return nil;
- }
-
- if (stream = NXMapFile (filename, NX_READONLY)) {
- if (readText)
- NXZoneFree (zone, readText);
- readText = NXZoneMalloc (zone, fileinfo.st_size + 1);
- NXScanf (stream, "%[^\0]", readText);
- NXClose (stream);
-
- /* Check to see if the file we read is writable -- foreshadowing */
- if (!(fileinfo.st_mode & S_IWRITE))
- NXRunAlertPanel ([stringSet valueForStringKey: "warningT"],
- [stringSet valueForStringKey: "unwritable"],
- [stringSet valueForStringKey: "OKB"],
- NULL, NULL, filename);
- } else {
- NXRunAlertPanel ([stringSet valueForStringKey: "openT"],
- [stringSet valueForStringKey: "cantOpen"],
- [stringSet valueForStringKey: "OKB"],
- NULL, NULL, filename);
- return nil;
- }
-
- return self;
- }
-
-
- @end
-