home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / aa_Intel_Only / Gnuplot / GnuplotSource / GnuplotPlot.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  19.8 KB  |  990 lines

  1. /*
  2.  *  Copyright (C) 1993  Robert Davis
  3.  *
  4.  *  This program is free software; you can redistribute it and/or
  5.  *  modify it under the terms of Version 2, or any later version, of 
  6.  *  the GNU General Public License as published by the Free Software 
  7.  *  Foundation.
  8.  */
  9.  
  10. static char RCSId[]="$Id: GnuplotPlot.m,v 1.15 1993/05/29 00:10:27 davis Exp $";
  11.  
  12.  
  13. #import <appkit/Application.h>
  14. #import <appkit/FontManager.h>
  15. #import <appkit/Pasteboard.h>
  16. #import <appkit/SavePanel.h>
  17. #import <appkit/Window.h>
  18. #import <appkit/publicWraps.h>    /* NXBeep()        */
  19.  
  20. #import <objc/List.h>
  21. #import <objc/NXStringTable.h>
  22.  
  23. #import <sys/stat.h>        /* stat()        */
  24. #import <sys/types.h>        /* stat()        */
  25.  
  26. #import <streams/streams.h>
  27.  
  28. #import <ctype.h>        /* isspace()        */
  29. #import <libc.h>        /* MAXPATHLEN        */
  30. #import <strings.h>        /* strlen(), strcpy()    */
  31. #import <stdlib.h>
  32.  
  33. #import "FunctionObject.h"
  34. #import "GnuplotPlot.h"
  35. #import "Gnuplot.h"
  36. #import "PlotView.h"
  37. #import "PopUpScrollView.h"
  38. #import "Status.h"
  39.  
  40. @interface GnuplotPlot (Private)
  41.  
  42. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain;
  43. - _write:(const char *)filename;
  44. - _read:(const char *) filename;
  45.  
  46. @end
  47.  
  48.  
  49.  
  50. #define DEFAULT_WIN_WIDTH    380
  51. #define DEFAULT_WIN_HEIGHT    269
  52.  
  53.  
  54. extern int scanner (char expression[]);
  55. extern int almost_equals (int t_num, char *str);
  56. extern char input_line[];
  57.  
  58. extern int nextfe_color;        /* Is this a color machine?    */
  59.  
  60.  
  61. /*** Preferences -- Pseudo Class Variables ***/
  62.  
  63. static int constantUpdate = UPDATE_NOT3D;    /* Replot after any change? */
  64.                         /* Default window size    */
  65. static NXSize winSize = { DEFAULT_WIN_WIDTH, DEFAULT_WIN_HEIGHT };
  66.  
  67.  
  68.  
  69. /*
  70.  *  This function computes a new location for each new window created.  
  71.  *  p is the origin of the windows frame, w is the size of the window.
  72.  */
  73. static void _newLocation (NXPoint *p, NXSize w)
  74. {
  75.   static count = 0;            /* This tracks all our instances */
  76.  
  77.   p->x += (21.0 * count);
  78.   p->y -= (24.0 * count) + (w.height - DEFAULT_WIN_HEIGHT);
  79.   count = (count > 10)? 0 : count+1;
  80. }
  81.  
  82.  
  83.  
  84.  
  85. static char *endOfCommand (char *aString)
  86. {
  87.     char *cur;
  88.     char c;
  89.  
  90.     for (cur = aString ; cur ; cur++)
  91.     switch (*cur) {
  92.     case '\\':
  93.         if (*(cur+1) && (*(cur+1) == '\n'))
  94.         cur++;
  95.         break;
  96.     case '\n':
  97.     case '\0':
  98.         return cur;
  99.     case ';':
  100.         return cur;
  101.     case '\'':
  102.     case '"':                    /* Skip quote */
  103.  
  104.         /* Note that gnuplot does not allow nested quotes. */
  105.  
  106.         c = *(cur++);
  107.             while (*cur && (*cur != c) && (*cur != '\n'))
  108.                 cur++;
  109.         if (*cur == '\n')
  110.         return cur;
  111.         break;
  112.     case '#':
  113.         while (cur++ && *cur)
  114.         if ((*cur == '\n') && (*(cur-1) != '\\'))
  115.             return cur;
  116.         break;
  117.     }
  118.  
  119.     return NULL;
  120. }
  121.  
  122.  
  123.  
  124.  
  125. static char *commandcpy (char *dest, char *source)
  126. {
  127.     char *cursource, *curdest, *end;
  128.  
  129.     end = endOfCommand (source);
  130.  
  131.     curdest = dest;
  132.     for (cursource = source ; cursource && (cursource != end); cursource++)
  133.     if (*cursource == '\\' && (cursource+1 != end) 
  134.         && (*(cursource+1) == '\n'))
  135.         cursource++;
  136.     else
  137.         *(curdest++) = *cursource;
  138.  
  139.     *(curdest++) = '\n';
  140.     *curdest = '\0';
  141.  
  142.     return end;
  143. }
  144.  
  145.  
  146. /*
  147.  *  stripCommands    Removes the 'set output "output.eps"\n', 'set 
  148.  *            terminal ...\n', and 'pause ...' lines and 
  149.  *            comments from the string.
  150.  */
  151. static char *stripCommands (char **aString)
  152. {
  153.     if (aString && *aString) {
  154.  
  155.     char *cur, *end, *newText;
  156.     BOOL plotAlready = NO;
  157.  
  158.     cur = *aString;
  159.     newText = (char *) malloc (strlen (*aString));
  160.  
  161.     *newText = '\0';
  162.  
  163.     while (cur && *cur) {
  164.         while (isspace(*cur))
  165.         cur++;
  166.  
  167.         end = commandcpy (input_line, cur);
  168.         scanner (input_line);
  169.  
  170.         if (plotAlready)
  171.         break;
  172.  
  173.         else if (!( (almost_equals (0, "se$t")    &&
  174.  
  175.              (almost_equals (1, "o$utput")        ||
  176.               almost_equals (1, "t$erminal")))    ||
  177.  
  178.                almost_equals (0, "pa$use") )) {
  179.  
  180.         strcat (newText, input_line);
  181.  
  182.         }
  183.  
  184.         plotAlready = plotAlready || almost_equals (0, "p$lot")
  185.                       || almost_equals (0, "sp$lot");
  186.         cur = (end ? end + 1 : NULL);
  187.     }
  188.  
  189.     strcpy (*aString, newText);
  190.     return *aString;
  191.     }
  192.  
  193.     return NULL;
  194. }
  195.  
  196.  
  197.  
  198. @implementation GnuplotPlot
  199.  
  200.  
  201. + initialize
  202. {
  203.     /*  
  204.      *  This is probably temporary -- until color is handled better, 
  205.      *  this just checks to see if we're on a color machine and sets 
  206.      *  the color variable of the terminal accordingly.
  207.      */
  208.     nextfe_color = (([NXApp colorScreen]->depth) == NX_TwoBitGrayDepth)? 0 : 1;
  209.     return self;
  210. }
  211.  
  212.  
  213.  
  214. + setConstantUpdate:(int)updateType
  215. {
  216.     constantUpdate = updateType;
  217.     return self;
  218. }
  219.  
  220.  
  221. + setHalvePlot:(BOOL)condition
  222. {
  223.     if (condition) {
  224.     winSize.width = 380;
  225.     winSize.height = 269;
  226.     } else {
  227.     winSize.width = 740;
  228.     winSize.height = 521;
  229.     }
  230.  
  231.     return [Status setHalvePlot:condition];
  232. }
  233.  
  234.  
  235. - init
  236. {
  237.     return [self initFromFile: NULL];
  238. }
  239.  
  240.  
  241. - initFromFile:(const char *) filename
  242. {
  243.     NXRect theFrame;
  244.     const char *validSendTypes[2];
  245.  
  246.     [super init];
  247.     controller = [NXApp delegate];
  248.     zone = [self zone];
  249.  
  250.     [NXApp loadNibSection: "GnuplotPlot.nib"
  251.             owner: self
  252.         withNames: NO
  253.          fromZone: zone];
  254.  
  255.     status = [[Status allocFromZone:zone] init];
  256.     [status setDelegate: self];
  257.  
  258.     [plotScrollView setVertScrollerRequired: YES];
  259.     [plotScrollView setHorizScrollerRequired: YES];
  260.     [plotScrollView setBorderType:NX_BEZEL];
  261.  
  262.  
  263.     plotView = [[PlotView allocFromZone:zone] initFrame:NULL];
  264.     [[plotScrollView setDocView:plotView] free];
  265.  
  266.     stringSet = [controller stringSet];
  267.     fullPath = NULL;
  268.  
  269.     /* Setup services */
  270.     validSendTypes[0] = NXFilenamePboardType;
  271.     validSendTypes[1] = NULL;
  272.     [NXApp registerServicesMenuSendTypes: validSendTypes andReturnTypes: NULL];
  273.  
  274.  
  275.     [window sizeWindow:winSize.width :winSize.height];    /* Default size    */
  276.  
  277.     [window getFrame:&theFrame];        /* Position the window    */
  278.     _newLocation (&theFrame.origin, winSize);
  279.     [window moveTo:NX_X(&theFrame) :NX_Y(&theFrame)];
  280.  
  281.     [window setMiniwindowIcon: "GnuplotDoc.tiff"];
  282.  
  283.     if (filename)  {
  284.     [self setName:filename];
  285.     isTitled = YES;
  286.     [window makeKeyAndOrderFront:self];
  287.     if (![self plotFromFile:filename]) {
  288.         [NXApp delayedFree:self];
  289.         return nil;
  290.     }
  291.     } else {
  292.     [window makeKeyAndOrderFront:self];
  293.     [self setName: NULL];
  294.     isTitled = NO;
  295.     }
  296.  
  297.     [self setDocEdited: NO];
  298.  
  299.     return self;
  300. }
  301.  
  302.  
  303. - free
  304. {
  305.     NXZoneFree (zone, fullPath);
  306.     NXZoneFree (zone, readText);
  307.     [status free];
  308.     [window free];
  309.  
  310.     return [super free];
  311. }
  312.  
  313.  
  314.  
  315. /*** Target/Action ***/
  316.  
  317.  
  318. - save:sender
  319. {
  320.     return [self _saveWithNewName:NO retainNewName:YES];
  321. }
  322.  
  323.  
  324. - saveAs:sender
  325. {
  326.     return [self _saveWithNewName:YES retainNewName:YES];
  327. }
  328.  
  329.  
  330. - saveTo:sender
  331. {
  332.     return [self _saveWithNewName:YES retainNewName:NO];
  333. }
  334.  
  335.  
  336. - revertToSaved:sender
  337. {
  338.     switch (NXRunAlertPanel ([stringSet valueForStringKey: "revertT"],
  339.                  [stringSet valueForStringKey: "revert"],
  340.                  [stringSet valueForStringKey: "revertB"],
  341.                  [stringSet valueForStringKey: "cancelB"],
  342.                  NULL, fullPath)) {
  343.     case NX_ALERTDEFAULT:
  344.     [status resetCurrent];
  345.     [self plotFromFile:fullPath];
  346.     [self setDocEdited:NO];
  347.  
  348.     /* 
  349.      * Make sure any panels that are showing info about this 
  350.      * document know that we just changed.
  351.      */
  352.     [controller updateApp];
  353.         [[FontManager new] setSelFont:[status font] isMultiple:NO];
  354.  
  355.     return self;
  356.     break;
  357.     case NX_ALERTOTHER:
  358.     return nil;
  359.     break;
  360.     }
  361.  
  362.     return self;
  363. }
  364.  
  365.  
  366.  
  367. - close:sender
  368. {
  369.     [window performClose:self];
  370.     return self;
  371. }
  372.  
  373.  
  374. - print:sender;
  375. {
  376.     return [plotView printPSCode:sender];
  377. }
  378.  
  379.  
  380.  
  381. - setName: (const char *) name
  382. {
  383.     char title[255];
  384.     char *oldFullPath = fullPath;
  385.  
  386.     if (name) {
  387.  
  388.     fullPath = NXCopyStringBufferFromZone (name, zone);
  389.     [window setTitleAsFilename:fullPath];
  390.     isTitled = YES;
  391.  
  392.     } else {
  393.  
  394.     sprintf (title, "%s%d", [stringSet valueForStringKey: "untitled"],
  395.                             [controller numberNew]);
  396.     fullPath = NXCopyStringBufferFromZone (title, zone);
  397.     [window setTitleAsFilename:title];
  398.     isTitled = NO;
  399.  
  400.     }
  401.  
  402.     NXZoneFree (zone, oldFullPath);
  403.  
  404.     return self;
  405. }
  406.  
  407.  
  408. - (const char *) name
  409. {
  410.     return fullPath;
  411. }
  412.  
  413.  
  414. - setCurrentFont:aFont
  415. {
  416.     if (aFont)  {
  417.     [status setFont:aFont];
  418.     }
  419.  
  420.     return self;
  421. }
  422.  
  423.  
  424. - currentFont
  425. {
  426.     return [status font];
  427. }
  428.  
  429.  
  430.  
  431. - window
  432. {
  433.     return window;
  434. }
  435.  
  436.  
  437. - (Status *)status
  438. {
  439.     return status;
  440. }
  441.  
  442.  
  443. - plotFromFile:(const char *)aFullPath
  444. {
  445.     id returnVal = self;
  446.  
  447.     if (![self _read:aFullPath])    /* _read into readText        */
  448.     return nil;
  449.  
  450.     stripCommands (&readText);        /* Ignore output, terminal, and    */
  451.                     /* multiple plot commands    */
  452.  
  453.     [status setAppendix: readText];
  454.  
  455.     /* 
  456.      *  We don't check the return status of plot: because even if 
  457.      *  there are errors, we want the file to remain open -- maybe the 
  458.      *  user can correct the errors without having to use vi.
  459.      */
  460.     [self plot:self];
  461.     [status grabCurrent];
  462.  
  463.  
  464.     [status setAppendix: NULL];
  465.     NXZoneFree (zone, readText);
  466.     readText = NULL;
  467.  
  468.     return returnVal;
  469. }
  470.  
  471.  
  472. - plot:sender
  473. {
  474.     id returnVal = self;
  475.  
  476.     /* 
  477.      *  We don't return nil if the settings don't allow us to create a 
  478.      *  valid plot -- we simply don't plot.
  479.      */
  480.     if (![status canPlot]) {
  481.  
  482.     [plotView newStream:NULL];
  483.  
  484.     } else {
  485.  
  486.     char *newTitle;
  487.     char *oldTitle = NXCopyStringBufferFromZone ([window title], zone);
  488.  
  489.     newTitle = NXZoneMalloc (zone, strlen (oldTitle) + 12);
  490.     sprintf (newTitle, [stringSet valueForStringKey:"plotting..."],
  491.          oldTitle);
  492.     [window setTitle:newTitle];
  493.     NXPing();
  494.     NXZoneFree (zone, oldTitle);
  495.     NXZoneFree (zone, newTitle);
  496.  
  497.     /* 
  498.      *  We do, however, report an error if the settings do allow 
  499.      *  us to plot but we were unable to (e.g. if gnuplot bailed 
  500.      *  out).
  501.      */
  502.     if (![status plot]) {
  503.         [self reportScriptError:self];
  504.         returnVal = nil;
  505.     } else {
  506.  
  507.         [plotView newStream:[status stream]];
  508.  
  509.         /* 
  510.          * Make sure the scale of the doc matches the current 
  511.          * popUp setting
  512.          */
  513.         [plotScrollView update];    /* Not done at every window update */
  514.  
  515.         [window update];
  516.     }
  517.  
  518.     [window setTitleAsFilename:fullPath];
  519.     NXPing();
  520.     }
  521.  
  522.     return returnVal;
  523. }
  524.  
  525.  
  526.  
  527.  
  528. - reportScriptError: sender;
  529. {
  530.     NXRunAlertPanel ([stringSet valueForStringKey:"scriptErrorT"],
  531.              [stringSet valueForStringKey:"scriptError"],
  532.              NULL, NULL, NULL, [self name]);
  533.  
  534.     [plotView newStream: NULL];
  535.     [window display];
  536.     return self;
  537. }
  538.  
  539.  
  540. - addDataFile:(const char *)aPath
  541. {
  542.     FunctionObject *functionObject;
  543.  
  544.     if (![FunctionObject isAcceptableDataFile:aPath])
  545.     return nil;
  546.  
  547.     functionObject = [[FunctionObject allocFromZone: [status zone]]
  548.                                      initFromString: aPath
  549.                        isThreeD: [status isThreeD]];
  550. //    [functionObject setDataFile:YES];
  551.     [[status functions] addObject:functionObject];
  552.     [status reportSettingsChange:self];
  553.  
  554.     return self;
  555. }
  556.  
  557.  
  558.  
  559. - setDocEdited:(BOOL) state
  560. {
  561.     if (((constantUpdate == UPDATE_ALWAYS) ||
  562.      ((constantUpdate == UPDATE_NOT3D) && ![status isThreeD])) && state)
  563.     [self plot:self];
  564.  
  565.     [window setDocEdited:state];
  566.  
  567.     return self;
  568. }
  569.  
  570. - (BOOL) isDocEdited
  571. {
  572.     return [window isDocEdited];
  573. }
  574.  
  575.  
  576.  
  577. - hideDocument:sender
  578. {
  579.   [window orderOut:sender];
  580.   return self;
  581. }
  582.  
  583.  
  584.  
  585.  
  586. /*** Services ***/
  587.  
  588.  
  589. - validRequestorForSendType:(NXAtom)sendType andReturnType:(NXAtom)returnType
  590. /*
  591.  *  Services menu support.
  592.  *  We are a valid requestor if the send type is filename and there is 
  593.  *  no return data from the request.
  594.  */
  595. {
  596.     return (isTitled && (sendType == NXFilenamePboardType) &&
  597.         (!returnType || !*returnType)) ? self : nil;
  598. }
  599.  
  600. - (BOOL)writeSelectionToPasteboard:pboard types:(NXAtom *)types
  601. /*
  602.  *  Services menu support.
  603.  *  Here we are asked by the Services menu mechanism to supply the 
  604.  *  filename (which we said we were a valid requestor for in the above 
  605.  *  method).
  606.  */
  607. {
  608.     int save;
  609.  
  610.     if (isTitled) {
  611.         while (types && *types)
  612.         if (*types == NXFilenamePboardType)
  613.         break;
  614.         else 
  615.         types++;
  616.  
  617.         if (types && *types) {
  618.             if ([self isDocEdited]) {
  619.         save = NXRunAlertPanel (
  620.             [stringSet valueForStringKey:"servicesT"],
  621.             [stringSet valueForStringKey:"serviceSave"],
  622.             [stringSet valueForStringKey:"save"],
  623.             [stringSet valueForStringKey:"dont save"], NULL);
  624.  
  625.                 if (save == NX_ALERTDEFAULT)
  626.             [self save:self];
  627.             }
  628.             [pboard declareTypes:&NXFilenamePboardType num:1 owner:self];
  629.             [pboard writeType:NXFilenamePboardType
  630.                      data:fullPath length:strlen (fullPath)+1];
  631.             return YES;
  632.         }
  633.     }
  634.  
  635.     return NO;
  636. }
  637.  
  638.  
  639.  
  640. /*** Autoupdate Methods ***/
  641.  
  642. - (BOOL)validateCommand:menuCell
  643. {
  644.     SEL action = [menuCell action];
  645.     BOOL edited = [self isDocEdited];
  646.     BOOL canPlot = [status canPlot];
  647.  
  648.     /* 
  649.      *  Note that we only allow a document to be saved when the 
  650.      *  settings validly produce a plot.  Also, if we need to see if 
  651.      *  the plot has a file with which it is associated.
  652.      */
  653.     
  654.     if (action == @selector(save:))
  655.         return (edited || !isTitled) && canPlot;
  656.     else if (action == @selector(saveAs:))
  657.         return canPlot;
  658.     else if (action == @selector(saveTo:))
  659.         return canPlot;
  660.     else if (action == @selector(saveAll:))    /* See Gnuplot class */
  661.         return (edited || !isTitled) && canPlot;
  662.     else if (action == @selector(revertToSaved:))
  663.     return (edited && isTitled);
  664.     else if (action == @selector(print:))
  665.     return canPlot;
  666.     else if (action == @selector(plot:))
  667.     return canPlot;
  668.  
  669.     return YES;
  670. }
  671.     
  672.  
  673.  
  674.  
  675. /*** Window Delegate Methods ***/
  676.  
  677. - windowDidBecomeMain:sender
  678. {
  679.     /* Update font panel */
  680.     if (plotView)  {
  681.     [window makeFirstResponder: plotView];
  682. //    [[FontManager new] setSelFont:[status font] isMultiple:NO];
  683.     }
  684.  
  685.     [controller setCurrentDoc:self];
  686.  
  687.     return self;
  688. }
  689.  
  690.  
  691.  
  692. - windowDidMiniaturize:sender
  693. {
  694.     [controller setCurrentDoc:nil];
  695.     return self;
  696. }
  697.  
  698.  
  699.  
  700. - windowDidResignMain:sender
  701. {
  702. //    [[FontManager new] setEnabled:NO];
  703.     return self;
  704. }
  705.  
  706.  
  707.  
  708. - windowDidResize:sender
  709. {
  710.     int aTag;
  711.  
  712.     /*
  713.      *  Make sure the scale of the doc matches the current popUp 
  714.      *  setting.  (If it's set to "Always Fit," which is 0, then resizing 
  715.      *  window should cause it to rescale.)
  716.      */
  717.     aTag = [plotScrollView currentTag];
  718.     if (!aTag)
  719.     [plotScrollView setCurrentTag: aTag];
  720.  
  721.     return self;
  722. }
  723.  
  724.  
  725. - windowDidUpdate:sender
  726. {
  727.     [[FontManager new] setEnabled:([status canPlot] && [window isMainWindow])];
  728.     return self;
  729. }
  730.  
  731.  
  732.  
  733. - windowWillClose:sender
  734. {
  735.     id returnVal = self;
  736.     BOOL done = NO;
  737.  
  738.     if ([self isDocEdited] && [status canPlot])  {
  739.  
  740.     while (!done)  {    /* Keep asking until user tells what to do */
  741.  
  742.         switch (NXRunAlertPanel([stringSet valueForStringKey:"willCloseT"],
  743.                     [stringSet valueForStringKey:"willClose"],
  744.                     [stringSet valueForStringKey:"save"],
  745.                     [stringSet valueForStringKey:"dont save"],
  746.                     [stringSet valueForStringKey:"cancelB"],
  747.                     [self name]))  {
  748.         case NX_ALERTDEFAULT:        /* Save        */
  749.         if (returnVal = [self save:self])
  750.             done = YES;
  751.         break;
  752.         case NX_ALERTALTERNATE:
  753.         returnVal = self;        /* Don't Save    */
  754.         done = YES;
  755.         break;
  756.         case NX_ALERTOTHER:            /* Cancel    */
  757.         returnVal = nil;
  758.         done = YES;
  759.         break;
  760.  
  761.         }
  762.  
  763.     }
  764.  
  765.     }
  766.  
  767.     if (returnVal)  {
  768.     plotScrollView = nil;        /* Don't want stale instance vars */
  769.     plotView = nil;
  770.  
  771.     [window orderOut:self];
  772.     [controller docDidClose:self];
  773.     [NXApp delayedFree: self];
  774.     }
  775.  
  776.     return returnVal;
  777. }
  778.  
  779.  
  780. - windowWillResize:sender toSize:(NXSize *) frameSize
  781. {
  782.     NXRect rect;
  783.     float x, y, w, h;
  784.  
  785.     [plotView getFrame:&rect];
  786.  
  787.     /*  
  788.      *  If the frame rectangle of the view has both a length and a 
  789.      *  width, i.e. if there's something to see, we control the sizing 
  790.      *  so it can never be bigger than the rectangle (except to allow 
  791.      *  room for border and sliders).
  792.      */
  793.  
  794.     if ((w = NX_WIDTH(&rect)) && (h = NX_HEIGHT(&rect)))  {
  795.  
  796.     x = w + 22;            /* Make room for borders & sliders */
  797.     y = h + 49;
  798.  
  799.             /* If the current setting isn't "Always Fit"    */
  800.     if ([plotScrollView currentTag])  {
  801.  
  802.         if (frameSize->width > x) frameSize->width = x;
  803.         if (frameSize->height > y) frameSize->height = y;
  804.  
  805.     }
  806.  
  807.     }
  808.  
  809. /*
  810.  *  Don't allow the window to get small enough to lose the scroll 
  811.  *  buttons and the pop-up list.
  812.  */
  813.  
  814.     if (frameSize->width < 147) frameSize->width = 147;
  815.     if (frameSize->height < 65) frameSize->height = 65;
  816.  
  817.     return self;
  818. }
  819.  
  820.  
  821.  
  822. /*** Status Delegate Methods ***/
  823.  
  824.  
  825. - settingsDidChange:sender
  826. {
  827.     [self setDocEdited: YES];
  828.     return self;
  829. }
  830.  
  831.  
  832.  
  833. // Shuts up the compiler about unused RCSId
  834. - (const char *) rcsid
  835. {
  836.     return RCSId;
  837. }
  838.  
  839.  
  840. @end
  841.  
  842.  
  843.  
  844. @implementation GnuplotPlot (Private)
  845.  
  846.  
  847. /*
  848.  *  This method is based on a similar method from the NeXTSTEP 
  849.  *  Developer Example WhatsUpDoc.  All varieties of save go through 
  850.  *  this routine.  It covers all the cases of running the Save Panel 
  851.  *  and retaining the name chosen.
  852.  */
  853. - _saveWithNewName:(BOOL)doNewName retainNewName:(BOOL)doRetain
  854. {
  855.     const char    *saveName;        /* filename to save into */
  856.     id        savePanel = [SavePanel new];
  857.  
  858.     if (doNewName || !isTitled) {    /* saveAs or saveTo */
  859.  
  860.     if ([savePanel runModalForDirectory:fullPath
  861.                file: (isTitled ? rindex (fullPath, '/') + 1 
  862.                           : [self name])]) {
  863.         saveName = [savePanel filename];
  864.     } else
  865.         return nil;            /* cancelled from SavePanel */
  866.  
  867.  
  868.     } else {                /* ordinary Save */
  869.  
  870.     /* 
  871.      *  In order to save a document, it must be able to produce a 
  872.      *  valid plot and it must either be edited or be untitled.
  873.      */
  874.     if ((![self isDocEdited] && isTitled) || ![status canPlot])
  875.         return nil;
  876.  
  877.     saveName = fullPath;
  878.     }
  879.  
  880.     switch ([controller saveType]) {
  881.     case SAVE_GNUPLOT:
  882.  
  883.     if (![self _write:saveName])  {
  884.         NXRunAlertPanel ([stringSet valueForStringKey: "Save"],
  885.                  [stringSet valueForStringKey: "cantSave"],
  886.                  [stringSet valueForStringKey: "OKB"],
  887.                  NULL, NULL, saveName);
  888.         return nil;
  889.     }
  890.                     /* Update the document name    */
  891.     if (doRetain) {            /* if requested            */
  892.         [self setName:saveName];
  893.         [self setDocEdited:NO];
  894.     }
  895.     break;
  896.  
  897.     case SAVE_EPS:
  898.  
  899.     if (![plotView saveEPSToFile:[savePanel filename]]) {
  900.         NXRunAlertPanel ([stringSet valueForStringKey: "Save"],
  901.                  [stringSet valueForStringKey: "cantSave"],
  902.                  [stringSet valueForStringKey: "OKB"],
  903.                  NULL, NULL, saveName);
  904.         return nil;
  905.     }
  906.  
  907.     break;
  908.  
  909.     case SAVE_TIFF:
  910.  
  911.     if (![plotView saveTIFFToFile:[savePanel filename]]) {
  912.         NXRunAlertPanel ([stringSet valueForStringKey: "Save"],
  913.                  [stringSet valueForStringKey: "cantSave"],
  914.                  [stringSet valueForStringKey: "OKB"],
  915.                  NULL, NULL, saveName);
  916.         return nil;
  917.     }
  918.  
  919.     break;
  920.     }
  921.  
  922.     return self;
  923. }
  924.  
  925.  
  926.  
  927. /*
  928.  *  This method does all the writing so any application-specific stuff 
  929.  *  should go here.
  930.  */
  931. - _write:(const char *)filename
  932. {
  933.     NXModalSession *session;
  934.     id modalPanel = NXGetAlertPanel ("Save", "Replotting %s.",
  935.                      NULL,NULL,NULL, [self name]);
  936.  
  937.     if ([Status lastplot] != status)  {
  938.     session = [NXApp beginModalSession:NULL for:modalPanel];
  939.     [self plot:self];
  940.     [NXApp endModalSession:session];
  941.     NXFreeAlertPanel(modalPanel);
  942.     }
  943.  
  944.     return [status saveToFile:filename];
  945. }
  946.  
  947.  
  948.  
  949.  
  950. - _read:(const char *) filename
  951. {
  952.     NXStream *stream;
  953.     struct stat fileinfo;
  954.  
  955.     if (stat (filename, &fileinfo)) {
  956.     NXRunAlertPanel ([stringSet valueForStringKey: "openT"],
  957.              [stringSet valueForStringKey: "cantOpen"],
  958.              [stringSet valueForStringKey: "OKB"],
  959.              NULL, NULL, filename);
  960.     return nil;
  961.     }
  962.  
  963.     if (stream = NXMapFile (filename, NX_READONLY)) {
  964.     if (readText)
  965.         NXZoneFree (zone, readText);
  966.     readText = NXZoneMalloc (zone, fileinfo.st_size + 1);
  967.     NXRead (stream, readText, fileinfo.st_size);
  968.     readText[fileinfo.st_size] = '\0';
  969.     NXClose (stream);
  970.  
  971.     /* Check to see if the file we read is writable -- foreshadowing */
  972.         if (!(fileinfo.st_mode & S_IWRITE))
  973.         NXRunAlertPanel ([stringSet valueForStringKey: "warningT"],
  974.                  [stringSet valueForStringKey: "unwritable"],
  975.                  [stringSet valueForStringKey: "OKB"],
  976.                  NULL, NULL, filename);
  977.     } else {
  978.     NXRunAlertPanel ([stringSet valueForStringKey: "openT"],
  979.              [stringSet valueForStringKey: "cantOpen"],
  980.              [stringSet valueForStringKey: "OKB"],
  981.              NULL, NULL, filename);
  982.     return nil;
  983.     }
  984.  
  985.     return self;
  986. }
  987.  
  988.  
  989. @end
  990.