This is GnuplotPlot.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: GnuplotPlot.m,v 1.15 1993/05/29 00:10:27 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 <ctype.h> /* isspace() */ #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 c; for (cur = aString ; cur ; cur++) switch (*cur) { case '\\': if (*(cur+1) && (*(cur+1) == '\n')) cur++; break; case '\n': case '\0': return cur; case ';': return cur; case '\'': case '"': /* Skip quote */ /* Note that gnuplot does not allow nested quotes. */ c = *(cur++); while (*cur && (*cur != c) && (*cur != '\n')) cur++; if (*cur == '\n') return cur; break; case '#': while (cur++ && *cur) if ((*cur == '\n') && (*(cur-1) != '\\')) return cur; break; } return NULL; } static char *commandcpy (char *dest, char *source) { char *cursource, *curdest, *end; end = endOfCommand (source); curdest = dest; for (cursource = source ; cursource && (cursource != end); cursource++) if (*cursource == '\\' && (cursource+1 != end) && (*(cursource+1) == '\n')) cursource++; else *(curdest++) = *cursource; *(curdest++) = '\n'; *curdest = '\0'; return end; } /* * stripCommands Removes the 'set output "output.eps"\n', 'set * terminal ...\n', and 'pause ...' lines and * comments from the string. */ static char *stripCommands (char **aString) { if (aString && *aString) { char *cur, *end, *newText; BOOL plotAlready = NO; cur = *aString; newText = (char *) malloc (strlen (*aString)); *newText = '\0'; while (cur && *cur) { while (isspace(*cur)) cur++; end = commandcpy (input_line, cur); scanner (input_line); if (plotAlready) break; else if (!( (almost_equals (0, "se$t") && (almost_equals (1, "o$utput") || almost_equals (1, "t$erminal"))) || almost_equals (0, "pa$use") )) { strcat (newText, input_line); } plotAlready = plotAlready || almost_equals (0, "p$lot") || almost_equals (0, "sp$lot"); cur = (end ? end + 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]; [status 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]; [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 setTitleAsFilename: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 *)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(); NXZoneFree (zone, oldTitle); NXZoneFree (zone, newTitle); /* * 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 setTitleAsFilename:fullPath]; NXPing(); } 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 isThreeD: [status isThreeD]]; // [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] && [status canPlot]) { 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 is based on a similar method 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 { const char *saveName; /* filename to save into */ id savePanel = [SavePanel new]; if (doNewName || !isTitled) { /* saveAs or saveTo */ if ([savePanel runModalForDirectory:fullPath file: (isTitled ? rindex (fullPath, '/') + 1 : [self name])]) { saveName = [savePanel filename]; } else return nil; /* cancelled from SavePanel */ } 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; } switch ([controller saveType]) { case SAVE_GNUPLOT: 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]; } break; case SAVE_EPS: if (![plotView saveEPSToFile:[savePanel filename]]) { NXRunAlertPanel ([stringSet valueForStringKey: "Save"], [stringSet valueForStringKey: "cantSave"], [stringSet valueForStringKey: "OKB"], NULL, NULL, saveName); return nil; } break; case SAVE_TIFF: if (![plotView saveTIFFToFile:[savePanel filename]]) { NXRunAlertPanel ([stringSet valueForStringKey: "Save"], [stringSet valueForStringKey: "cantSave"], [stringSet valueForStringKey: "OKB"], NULL, NULL, saveName); return nil; } break; } 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); NXRead (stream, readText, fileinfo.st_size); readText[fileinfo.st_size] = '\0'; 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
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.