home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / SoundApps / aa_Intel_Only / EnvelopeEd1.04b / Source / EnvelopeView.m < prev    next >
Encoding:
Text File  |  1994-09-24  |  52.9 KB  |  1,670 lines

  1. //////////////////////////////////////////////////////////////
  2. //
  3. // EnvelopeView.m -- Implementation for the EnvelopeView class
  4. // Copyright 1991-94 Fernando Lopez Lezcano All Rights Reserved
  5. //
  6. //////////////////////////////////////////////////////////////
  7.  
  8. // Revision History:
  9.  
  10. // 8/25/92 Add parsing of exponential format numbers
  11. // 8/21/92 Memory allocation bug on allocateTemp and allocateDraw
  12. // 8/20/92 Added drawSegments option.
  13.  
  14. #import "EnvelopeView.h"
  15. #import "Controller.h"
  16.  
  17. #import <stdlib.h>
  18. #import <ctype.h>
  19. #import <objc/zone.h>
  20. #import <defaults/defaults.h>
  21. #import <appkit/Application.h>
  22. #import <musickit/musickit.h>            // for envelope class stuff
  23. #import <dpsclient/wraps.h>              // for PSxxx stuff
  24. #import <appkit/Form.h>
  25. #import <appkit/Button.h>
  26. #import <appkit/Panel.h>
  27. #import <appkit/NXCursor.h>
  28. #import <appkit/NXImage.h>
  29.  
  30. #import <appkit/Pasteboard.h>
  31.  
  32. #define    STYLE_MK       '0'
  33. #define    STYLE_pX_Yp    '1'
  34. #define    STYLE_pXcYp    '2'
  35. #define    STYLE_X_Y      '3'
  36. #define    STYLE_XcY      '4'
  37. #define    STYLE_X_Y_Zc   '5'
  38. #define    STYLE_XcYcZ_   '6'
  39. #define    STYLE_ppX_Yp    '7'
  40.  
  41. #define TEST 1
  42.  
  43. #define KNOBSIZE 8
  44. #define MAXNUMSEGS 8
  45.  
  46. #define WIDTH bounds.size.width
  47. #define HEIGHT bounds.size.height
  48.  
  49. #define WHITE 1.0
  50. #define BLACK 0.0
  51. #define DKGRAY (1.0/3.0)
  52. #define LTGRAY (2.0/3.0)
  53. #define TRANSP 2.0
  54.  
  55. #define DRAG_MASK (NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK)
  56.  
  57. //===================================================================
  58. // Envelope auxiliar routines and macros
  59. //===================================================================
  60.  
  61. //-------------------------------------------------------------------
  62. // Draw a knob in the display
  63.  
  64. #define DRAWKNOB(x,y) PScompositerect(x-KNOBSIZE/2,y-KNOBSIZE/2,KNOBSIZE, KNOBSIZE,NX_SOVER)
  65.  
  66. //-------------------------------------------------------------------
  67. // Translate from x/y envelope values to display pixel coordinates
  68.  
  69. #define xToPix(x) (((x-xMin)/(xMax-xMin))*WIDTH)
  70. #define yToPix(y) (((y-yMin)/(yMax-yMin))*HEIGHT)
  71.  
  72. //-------------------------------------------------------------------
  73. // Translate from display pixel coordinates to x/y envelope values
  74.  
  75. #define pixToX(xPix) (xPix/WIDTH*(xMax-xMin)+xMin)
  76. #define pixToY(yPix) (yPix/HEIGHT*(yMax-yMin)+yMin)
  77.  
  78. //-------------------------------------------------------------------
  79. // Shorthand to access envelope object instance variables
  80.  
  81. #define pointCount [theEnvelope pointCount]
  82. #define stickyPoint [theEnvelope stickPoint]
  83. #define xValues [theEnvelope xArray]
  84. #define yValues [theEnvelope yArray]
  85. #define sValues [theEnvelope smoothingArray]
  86.  
  87. //-------------------------------------------------------------------
  88. // Temporary copy arrays for envelope copy operations
  89.  
  90. typedef struct _env {
  91.     double *x;                    // x array for adding or removing points
  92.     double *y;                    // y array
  93.     double *s;                    // smoothing array
  94.     int max;                      // currently allocated length of arrays
  95. } Env;
  96.  
  97. static Env * temp=NULL;           // only one copy for each envelope object
  98.  
  99. // Allocate or reallocate arrays to "size" elements
  100.  
  101. void allocateTemp(int size)
  102. {
  103.     int newsize=size;
  104.     if (newsize<64) newsize=64;
  105.     if (temp==NULL)    {                   // initial creation of arrays
  106.         temp=malloc(sizeof(Env));
  107.         temp->x=malloc(sizeof(double)*newsize);
  108.         temp->y=malloc(sizeof(double)*newsize);
  109.         temp->s=malloc(sizeof(double)*newsize);
  110.         temp->max=newsize;
  111.     }
  112.     else if (size>temp->max) {            // grow to double size each time
  113.         if (size>temp->max*2) 
  114.             temp->max=size;
  115.     else 
  116.         temp->max*=2;
  117.         temp->x=realloc(temp->x,sizeof(double)*temp->max);
  118.         temp->y=realloc(temp->y,sizeof(double)*temp->max);
  119.         temp->s=realloc(temp->s,sizeof(double)*temp->max);
  120.     }
  121. }
  122.  
  123. // Free allocated memory
  124.  
  125. void freeTemp()
  126. {
  127.     if (temp!=NULL) {
  128.         free(temp->x);
  129.         free(temp->y);
  130.         free(temp->s);
  131.         free(temp);
  132.         temp=NULL;
  133.     }
  134. }
  135.  
  136. //-------------------------------------------------------------------
  137. // Temporary arrays for drawing operations
  138.  
  139. typedef struct _draw {
  140.     int num;                    // number of points in draw arrays (x,y,p)
  141.     int *p;                     // point number array
  142.     double *x;                  // x array of drawing coordinates
  143.     double *y;                  // y array
  144.     double *yr;                 // real y coordinate of each point 
  145.     int max;                    // currently allocated length
  146. } Draw;
  147.  
  148. // Each time the envelope is drawn in the screen the calculated positions
  149. // of all intermediate points are stored in the x and y arrays. The point
  150. // number is also stored in the p array. This arrays are then used to erase
  151. // the lines by drawing them in white.
  152.  
  153. static Draw * draw=NULL;
  154.  
  155. // Allocate or reallocate arrays to "size" elements
  156.  
  157. void allocateDraw(int size)
  158. {
  159.     int newsize=size;
  160.     if (newsize<64) newsize=64;
  161.     if (draw==NULL)    {
  162.         draw=malloc(sizeof(Draw));
  163.         draw->x=malloc(sizeof(double)*newsize*MAXNUMSEGS);
  164.         draw->y=malloc(sizeof(double)*newsize*MAXNUMSEGS);
  165.         draw->p=malloc(sizeof(int)*newsize*MAXNUMSEGS);
  166.         draw->yr=malloc(sizeof(double)*newsize);
  167.         draw->max=newsize;
  168.         draw->num=0;
  169.     }
  170.     else if (size>draw->max) {
  171.         if (size>draw->max*2) 
  172.             draw->max=size;
  173.     else 
  174.         draw->max*=2;
  175.         draw->x=realloc(draw->x,sizeof(double)*draw->max*MAXNUMSEGS);
  176.         draw->y=realloc(draw->y,sizeof(double)*draw->max*MAXNUMSEGS);
  177.         draw->p=realloc(draw->p,sizeof(int)*draw->max*MAXNUMSEGS);
  178.         draw->yr=realloc(draw->yr,sizeof(double)*draw->max);
  179.     }
  180. }
  181.  
  182. // Free allocated memory
  183.  
  184. void freeDraw()
  185. {
  186.     if (draw!=NULL) {
  187.         free(draw->x);
  188.         free(draw->y);
  189.         free(draw->p);
  190.         free(draw->yr);
  191.         free(draw);
  192.         draw=NULL;
  193.     }
  194. }
  195.  
  196. //===================================================================
  197. @implementation EnvelopeView
  198. //===================================================================
  199.  
  200. //-------------------------------------------------------------------
  201. // - resetCursorRects sets a new cursor of the view. The message first
  202. // initializes the two cursor shapes if needed and the proceeds to
  203. // set the default cursor for the view to a cross.
  204.  
  205. - resetCursorRects
  206. {
  207.     NXRect visible;    
  208.     NXPoint spot;
  209.     
  210.     if (!theCross) {
  211.         theCross=[NXCursor newFromImage:[NXImage newFromSection:"cross.tiff"]];
  212.         spot.x = 7.0; 
  213.         spot.y = 7.0;
  214.         [theCross setHotSpot:&spot];
  215.     }
  216.     if (!theFilledCross) {
  217.         theFilledCross=[NXCursor newFromImage:[NXImage newFromSection:"crossfill.tiff"]];
  218.         spot.x = 7.0; 
  219.         spot.y = 7.0;
  220.         [theFilledCross setHotSpot:&spot];
  221.     }
  222.     if ([self getVisibleRect:&visible]) {
  223.         [self addCursorRect:&visible cursor: theCross];
  224.     }
  225.     return self;
  226. }
  227.  
  228. //-------------------------------------------------------------------
  229. // - selectPoint:n
  230. // Selects point number n in the envelope and broadcasts the change
  231. // to the controller object.
  232.  
  233. - selectPoint:(int)n
  234. {
  235.     selected=n;
  236.     [self display];
  237.     [theController update: self];
  238.     return self;
  239. }
  240.  
  241. //-------------------------------------------------------------------
  242. // - initFrame:  -- initialize a new Envelope View object
  243.  
  244. NXDefaultsVector envelopeDefaults={
  245.         {"defaultSmoothing", "1.0" },
  246.         {"showSmoothing", "YES" },
  247.         {"drawSegments", "YES" },
  248.     };
  249.  
  250. - initFrame:(const NXRect *)frameRect
  251. {
  252.     static double xs[]={0.0,1.0};
  253.     static double ys[]={0.0,0.0};
  254.     static double ss[]={1.0,1.0};
  255.  
  256.     malloc_debug(0xFFFF);
  257.     
  258.     defaultSmooth=atof(NXGetDefaultValue("EnvelopeEd","defaultSmoothing"));
  259.     ss[0]=ss[1]=defaultSmooth;
  260.     self = [super initFrame: frameRect];
  261.     
  262.     theEnvelope=[[[Envelope alloc] init]
  263.         setPointCount: 2 
  264.         xArray: xs
  265.         orSamplingPeriod: 1.0
  266.         yArray: ys
  267.         smoothingArray: ss
  268.         orDefaultSmoothing: defaultSmooth];
  269.     [theEnvelope setStickPoint: MAXINT];    // init new envelope to default values
  270.     
  271.     allocateDraw(64);                        // arrays for drawing and erasing
  272.     allocateTemp(64);                        // temporary arrays for move operations
  273.     userPath = newUserPath();                // allocate a user path for drawing
  274.     
  275.     xMin=yMin=0.0;                            // max and min limits of display
  276.     xMax=yMax=1.0;
  277.     xSnap=ySnap=0.001;                        // initial Snap off in both directions
  278.     
  279.     envColour=BLACK;
  280.     if (strcasecmp(NXGetDefaultValue("EnvelopeEd","showSmoothing"),"YES")==0) 
  281.         showSmooth=-1;
  282.     if (strcasecmp(NXGetDefaultValue("EnvelopeEd","drawSegments"),"YES")==0) 
  283.         drawSegments=-1;
  284.     else showSmooth=0;
  285.     [self selectPoint: 0];                    // select first point and display
  286.     return self;
  287. }
  288.  
  289. //-------------------------------------------------------------------
  290. // - free  -- Release the allocated memory when object is freed
  291.  
  292. -free
  293. {
  294.     [theEnvelope free];
  295.     freeUserPath(userPath);
  296.     freeTemp();
  297.     freeDraw();
  298.     return [super free]; 
  299. }
  300.  
  301. //-------------------------------------------------------------------
  302. // - controllerIs:  Receive the object id of the controller of the view
  303.  
  304. - controllerIs:sender
  305. {
  306.     theController=sender;
  307.     return self;
  308. }
  309.  
  310. //-------------------------------------------------------------------
  311. // - insertPointAt:p
  312. // Finds the place in the envelope where the point p should be inserted.
  313. // Returns the point number at the insertion or -1 if an error is
  314. // detected. 
  315.  
  316. - (int) insertPointAt:(NXPoint)p
  317. {
  318.     int point;
  319.  
  320.     point=0;
  321.     while (point<=pointCount-1) {
  322.         if (xValues[point]>p.x)
  323.             return point;
  324.         point++;
  325.     }
  326.     return pointCount;
  327. }
  328.  
  329. //-------------------------------------------------------------------
  330. // - (int) hitKnobAt: p border: delta 
  331. // Test if the user hits a knob when clicking the mouse at point p. 
  332. // The boundaries of the zone are delta pixels wide. Returns the
  333. // number of the hit point or -1 if no hit.
  334.  
  335. - (int) hitKnobAt:(NXPoint)p border:(float)delta;
  336. {
  337.     int point;
  338.     float kx, ky, dx, dy;
  339.     
  340.     dx=delta/WIDTH*(xMax-xMin);
  341.     dy=delta/HEIGHT*(yMax-yMin);
  342.     for (point=0; point<pointCount; point++) {
  343.         kx=xValues[point];
  344.         ky=yValues[point];
  345.         if (p.x<kx-dx)
  346.             break;
  347.         if (p.x<=kx+dx && p.x>=kx-dx &&
  348.             p.y<=ky+dy && p.y>=ky-dy)
  349.             return point;
  350.     }
  351.     return -1;
  352. }
  353.  
  354. //-------------------------------------------------------------------
  355. // drawKnobs(from,to,lcolour,hcolour) draws a set of knobs starting at 
  356. // point from and ending at point to. Also highlights the selected
  357. // knob if necessary. The second colour argument specifies the colour
  358. // of the highlighted knob.
  359.  
  360. - drawKnobsFrom:(int)from to:(int)to in:(float)lcolour :(float)hcolour
  361. {
  362.     int point;
  363.     
  364.     PSsetgray(lcolour);
  365.     for (point=from; point<=to; point++) {
  366.         if (point!=selected)
  367.             DRAWKNOB(xToPix(xValues[point]),yToPix(yValues[point]));
  368.     }
  369.     if (selected>=from && selected<=to && hcolour<=WHITE) {
  370.         PSsetgray(hcolour);
  371.         DRAWKNOB(xToPix(xValues[selected]),yToPix(yValues[selected]));
  372.     }
  373.     return self;
  374. }
  375.  
  376. //-------------------------------------------------------------------
  377. // drawSegments(from,to,colour) draws line segments starting at point
  378. // from and ending at point to. It also takes care of the sticky point
  379. // marker. Due to problems with rounding each time a path is drawn the
  380. // moves are recorded in a set of arrays. This path is used to "erase"
  381. // the segment when a point is moved.
  382.  
  383. - recordMovex:(double)x y:(double)y p:(int)n draw:(int)state
  384. {
  385.     double xp, yp;
  386.     
  387.     draw->p[draw->num]=n;
  388.     draw->x[draw->num]=xp=xToPix(x);
  389.     draw->y[draw->num++]=yp=yToPix(y);
  390.     if (state==0)
  391.         UPmoveto(userPath,xp,yp);
  392.     else
  393.         UPlineto(userPath,xp,yp);
  394.     return self;
  395. }
  396.  
  397. - drawSegmentsFrom:(int)from to:(int)to in:(float)colour
  398. {
  399.     int point, seg;
  400.     double 
  401.         incx, incy,
  402.         deltax, deltay, 
  403.         xi, xf, x, 
  404.         yi, yf, y,
  405.         smooth, numsegs;
  406.         
  407.     // If drawing colour is white then use erase arrays to remove 
  408.     // the previously drawn segments
  409.     
  410.     if (colour==WHITE) {
  411.         PSsetgray(colour);
  412.         beginUserPath(userPath, NO);
  413.         for (point=0; 
  414.              draw->p[point]<=from && point<draw->num; 
  415.              point++);
  416.         if (point-1>=0) point--;
  417.         UPmoveto(userPath,draw->x[point],draw->y[point]);
  418.         for (; 
  419.              draw->p[point]<=to && point<draw->num; 
  420.              point++)
  421.             UPlineto(userPath,draw->x[point],draw->y[point]);
  422.         endUserPath(userPath, dps_ustroke);
  423.         sendUserPath(userPath);
  424.         return self;
  425.     }
  426.  
  427.     // Else construct path for next erase and draw segments
  428.     // first draw x and y axis if necessary
  429.     
  430.     draw->num=0;
  431.     if (xMin!=0 || xMax!=0) {
  432.         PSsetgray(LTGRAY);
  433.         beginUserPath(userPath, NO);
  434.         if (xMin!=0) {
  435.             [self recordMovex: 0 y: yMin p: from draw: 0];
  436.             [self recordMovex: 0 y: yMax p: from draw: 1];    
  437.         }
  438.         if (yMin!=0) {
  439.             [self recordMovex: xMin y: 0 p: from draw: 0];
  440.             [self recordMovex: xMax y: 0 p: from draw: 1];
  441.         }
  442.         endUserPath(userPath, dps_ustroke);
  443.         sendUserPath(userPath);
  444.     }
  445.     
  446.     // and then draw the segments that interconnect the breakpoints.
  447.     
  448.     if (drawSegments==0) return self;
  449.     
  450.     draw->num=0;
  451.     PSsetgray(colour);
  452.     beginUserPath(userPath, NO);
  453.     [self recordMovex: xValues[from] y: yValues[from] p: from draw: 0];
  454.     yi=draw->yr[from]=yValues[from];
  455.     for (point=from+1; point<=to; point++) {
  456.         
  457.         xi=xValues[point-1];
  458.         xf=x=xValues[point];
  459.         yf=y=yValues[point];
  460.         deltax=xf-xi;
  461.         deltay=yf-yi;
  462.         numsegs=rint((deltax*WIDTH/(xMax-xMin))/3);        // at least 3 pixels per segment
  463.         if (numsegs>MAXNUMSEGS) numsegs=MAXNUMSEGS;        // but no more than MAXNUMSEGS...
  464.         if (numsegs<2) numsegs=2;                          // or less than 2
  465.         if (showSmooth!=0 && deltax!=0 && deltay!=0) {
  466.             smooth=sValues[point];
  467.             incx=deltax/numsegs;                           // effective deltax is always
  468.             if (smooth<0.01) incx*=0.01;                   // determined by the time
  469.             else if (smooth<1.0) incx*=smooth;             // constant of the segment
  470.             incy=deltay/numsegs;
  471.             for (seg=0, x=xi+incx; seg<numsegs-1; seg++, x+=incx) {
  472.                 y=yi+(deltay*(1-exp(-5.5262*(x-xi)/(deltax*smooth))));
  473.                 [self recordMovex: x y: y p: point draw: 1];
  474.             }
  475.             y=yi+(deltay*(1-exp(-5.5262/smooth)));         // last segment to xf directly
  476.             [self recordMovex: xf y: y p: point draw: 1];
  477.         }
  478.         else [self recordMovex: x y: y p: point draw: 1];
  479.         if (point==stickyPoint) {
  480.             [self recordMovex: xValues[point] y: 0 p: point draw: 1];
  481.             [self recordMovex: xValues[point] y: y p: point draw: 0];
  482.         }
  483.         yi=draw->yr[point]=y;
  484.     }
  485.     endUserPath(userPath, dps_ustroke);
  486.     sendUserPath(userPath);
  487.     return self;
  488. }
  489.  
  490. //-------------------------------------------------------------------
  491. // - drawSelf:: - Draw the envelope object in the view
  492.  
  493. - drawSelf:(NXRect *)rects :(int)rectCount
  494. {
  495.     NXEraseRect(&bounds);                        // clear the view
  496.     PSsetlinewidth(0.0);
  497.     if (theEnvelope!=NULL) {
  498.         [self drawKnobsFrom:0 to:pointCount-1 in:LTGRAY:envColour];
  499.         [self drawSegmentsFrom: 0 to:pointCount-1 in: envColour];
  500.     }
  501.     return self;
  502. }
  503.     
  504. //-------------------------------------------------------------------
  505. // - eraseSelectedKnob Draw the envelope again without drawing the 
  506. // selected knob. This is used just before the modal loop for draging
  507. // a point is entered. In the loop the cursor itself acts as the knob.
  508.  
  509. - eraseSelectedKnob
  510. {
  511.     NXEraseRect(&bounds);                        // clear the view
  512.     PSsetlinewidth(0.0);
  513.     if (theEnvelope!=NULL) {
  514.         [self drawKnobsFrom:0 to:pointCount-1 in:LTGRAY:TRANSP];
  515.         [self drawSegmentsFrom: 0 to:pointCount-1 in: envColour];
  516.     }
  517.     return self;
  518. }
  519.     
  520. //-------------------------------------------------------------------
  521. // movePoint:to: Move point n to a new location.
  522. // This is called from within the drag modal loop so it tries to be
  523. // quick. Each time it erases the old knobs and segments (up to two)
  524. // and then draws the new knobs and segments. This last step can draw
  525. // any number of segments depending on how close the other points are
  526. // to the selected point (the problem is that the knob can erase 
  527. // small portions of the segments that are lying outside of the area
  528. // bounded by the selected point and its two enclosing points).
  529.  
  530. - (int) movePoint:(int)n to: (NXPoint)p;
  531. {
  532.     int left,right,                                // limits for x movement of point
  533.         drawFrom,drawTo;                           // start and end points for drawing lines
  534.     
  535.     if (n==0) {                                    // determine limits for erase and draw
  536.         drawFrom=0; 
  537.         drawTo=1;
  538.     } 
  539.     else
  540.     if (n==pointCount-1) {
  541.         drawFrom=n-1; 
  542.         drawTo=n;
  543.     } 
  544.     else {
  545.         drawFrom=n-1; 
  546.         drawTo=n+1;
  547.     }
  548.     left=drawFrom;
  549.     right=drawTo;
  550.     
  551.     if (showSmooth!=0) {                          // if showing smoothing draw points...
  552.         while(drawFrom-1>=0 &&                    // ...that have smoothing greater that 1
  553.             sValues[drawFrom]>1.0)
  554.             drawFrom--;
  555.         while(drawTo+1<pointCount &&
  556.             sValues[drawTo]>1.0)
  557.             drawTo++;
  558.     }
  559.     if (n!=0 && p.x<xValues[left])                // force selected point to be within
  560.         p.x=xValues[left];                        // neighbouring points
  561.     if (n!=pointCount-1 && p.x>xValues[right]) 
  562.         p.x=xValues[right];
  563.     if (xSnap!=0 && 
  564.         xValues[right]-xValues[left]>2*xSnap)
  565.         p.x=rint(p.x/xSnap)*xSnap;                // snap into x grid
  566.     if (p.y>yMax) p.y=yMax;                       // clip y values to max and min
  567.     if (p.y<yMin) p.y=yMin;
  568.     if (ySnap!=0) 
  569.         p.y=rint(p.y/ySnap)*ySnap;                // snap into y grid
  570.     
  571.     if (n>1)                                      // avoid 3 points with the same x value
  572.         if (p.x==xValues[n-1] && 
  573.             p.x==xValues[n-2])
  574.             p.x=xValues[n-1]+(1/WIDTH*(xMax-xMin));
  575.     if (n<(pointCount-2))
  576.         if (p.x==xValues[n+1] && 
  577.             p.x==xValues[n+2])
  578.             p.x=xValues[n+1]-(1/WIDTH*(xMax-xMin));
  579.  
  580.     [self drawKnobsFrom:drawFrom                  // erase old knobs and segments
  581.         to:drawTo in:WHITE:TRANSP];
  582.     [self drawSegmentsFrom:drawFrom 
  583.         to:drawTo in:WHITE];
  584.  
  585.     xValues[n]=p.x;
  586.     yValues[n]=p.y;                               // update coordinates in arrays
  587.     
  588.     [self drawKnobsFrom:drawFrom                  // draw new knobs and segments
  589.         to:drawTo in:LTGRAY:TRANSP];
  590.     [self drawSegmentsFrom:drawFrom 
  591.         to:drawTo in:envColour];
  592.  
  593.     [[self window] flushWindow];
  594.     return n;
  595. }
  596.  
  597. //-------------------------------------------------------------------
  598. // addPoint:  -- Adds a point to the envelope
  599. // Returns number of new point, -1 on error
  600.  
  601. - (int) addPoint:(NXPoint)p
  602. {
  603.     int point, newp, oldp, newpc, newSticky;
  604.  
  605.     if (p.x<xMin || p.x>=xMax ||                 // check point is within bounds
  606.         p.y<yMin || p.y>yMax)
  607.         return -1;
  608.     if (ySnap!=0) p.y=rint(p.y/ySnap)*ySnap;     // snap into y grid
  609.  
  610.     if ((point=[self insertPointAt:p])<0)        // and that we get a valid point number
  611.         return -1;
  612.  
  613.     newpc=pointCount+1;
  614.     allocateTemp(newpc);
  615.     allocateDraw(newpc);
  616.  
  617.     draw->num=0;                                 // clear erase path
  618.     newSticky=[theEnvelope stickPoint];
  619.     if (newSticky!=MAXINT && stickyPoint>point) 
  620.         newSticky++;                             // adjust value of stick point
  621.     
  622.     for (newp=oldp=0; newp<newpc; newp++) {
  623.         if (newp==point) {
  624.             temp->x[newp]=p.x;                   // set values of new point
  625.             temp->y[newp]=p.y;
  626.             draw->yr[newp]=p.y;
  627.             temp->s[newp]=defaultSmooth;
  628.         }
  629.         else {
  630.             temp->x[newp]=xValues[oldp];         // copy old values into new arrays
  631.             temp->y[newp]=yValues[oldp];
  632.             draw->yr[newp]=draw->yr[oldp];
  633.             if (sValues!=NULL)
  634.                 temp->s[newp]=sValues[oldp];
  635.             else
  636.                 temp->s[newp]=defaultSmooth;
  637.             oldp++;
  638.         }
  639.     }
  640.     [theEnvelope                                 // redefine the old envelope object
  641.         setPointCount: newpc
  642.         xArray: temp->x
  643.         orSamplingPeriod: 1.0
  644.         yArray: temp->y
  645.         smoothingArray: temp->s
  646.         orDefaultSmoothing: defaultSmooth];
  647.     [theEnvelope setStickPoint: newSticky];
  648.  
  649.     return point;
  650. }
  651.  
  652. //-------------------------------------------------------------------
  653. // removePoint: -- Remove a point from the envelope
  654. // Returns the new number of points or -1 on error
  655.  
  656. - (int) removePoint:(int)n
  657. {
  658.     int oldp, newp, newpc, newSticky;
  659.     
  660.     if (pointCount<3) return -1;                // leave always at least two breaks
  661.     
  662.     newpc=pointCount-1;
  663.     allocateTemp(newpc);
  664.     allocateDraw(newpc);
  665.     
  666.     draw->num=0;
  667.     for (oldp=newp=0; newp<newpc; newp++,oldp++) {
  668.         if (oldp==n) oldp++;                    // copy arrays skipping deleted element
  669.         temp->x[newp]=xValues[oldp];
  670.         temp->y[newp]=yValues[oldp];
  671.         draw->yr[newp]=draw->yr[oldp];
  672.         if (sValues!=NULL)
  673.             temp->s[newp]=sValues[oldp];
  674.     }
  675.     newSticky=[theEnvelope stickPoint];
  676.     if (newSticky!=MAXINT) {
  677.         if (newSticky==n)
  678.             newSticky=MAXINT;                   // remove stick point with point
  679.         else if (newSticky>n)
  680.             newSticky--;                        // or adjust value if higher
  681.     }
  682.     [theEnvelope
  683.         setPointCount: newpc
  684.         xArray: temp->x
  685.         orSamplingPeriod: 1.0
  686.         yArray: temp->y
  687.         smoothingArray: temp->s
  688.         orDefaultSmoothing: defaultSmooth];
  689.     [theEnvelope setStickPoint: newSticky];     // update envelope object
  690.  
  691.     [self selectPoint: n-1];                    // and select previous point
  692.     return pointCount;
  693. }
  694.  
  695. //-------------------------------------------------------------------
  696. // mouseDown:  -- Responds to a mousedown event
  697. // The following is the behaviour of the mouse:
  698. // hit a knob:            --> drag the point
  699. // hit with shift:        --> delete envelope breakpoint
  700. // hit with alternate:    --> toggle sticky point at breakpoint
  701. // no hit with shift:    --> create a new envelope breakpoint
  702.  
  703. - mouseDown:(NXEvent *)event
  704. {
  705.     NXPoint ep, p;
  706.     int hitpt;                // Point to move/remove
  707.     int oldMask;
  708.  
  709.      ep=event->location;
  710.     [self convertPoint:&ep fromView:nil];
  711.     p.x=pixToX(ep.x);
  712.     p.y=pixToY(ep.y);                                    // convert from pixels to x/y
  713.     hitpt=[self hitKnobAt: p border: KNOBSIZE/2];        // see if it is a breakpoint    
  714.     if (event->flags & NX_SHIFTMASK) {                   // with shift key down...
  715.         if (hitpt>=0) {
  716.             if ([self removePoint: hitpt] > 0)           // hit --> remove point
  717.                 hitpt=-1;
  718.         }
  719.         else hitpt=[self addPoint: p];                   // no hit --> add point
  720.     }
  721.     if ((event->flags&NX_ALTERNATEMASK)&&(hitpt>=0)) {   // hit plus alternate...
  722.         if (stickyPoint==hitpt)
  723.             [theEnvelope setStickPoint: MAXINT];                
  724.         else
  725.             [theEnvelope setStickPoint: hitpt];
  726.         [self selectPoint: hitpt];
  727.         hitpt=-1;
  728.     }
  729.     
  730.     if (hitpt >= 0) {                                    // Move hitpt as mouse drags
  731.         [self selectPoint: hitpt];                       // select and redraw image
  732.         oldMask = [[self window] addToEventMask:DRAG_MASK];
  733.         
  734.         [self lockFocus];
  735.         [theFilledCross push];                           // use cursor = cross+knob
  736.         [self eraseSelectedKnob];
  737.         while (event->type != NX_MOUSEUP) {
  738.              ep=event->location;
  739.             [self convertPoint:&ep fromView:nil];
  740.             if ([NXApp peekNextEvent: DRAG_MASK into: event]==NULL) {
  741.                 p.x=pixToX(ep.x);
  742.                 p.y=pixToY(ep.y);
  743.                 [self movePoint:hitpt to:p];
  744.                 [theController updateCoords: self at: hitpt];
  745.                 NXPing();
  746.             }
  747.             event = [NXApp getNextEvent:DRAG_MASK];
  748.         }
  749.         [NXCursor pop];                                   // return to crosshair cursor
  750.         [self unlockFocus];
  751.     }
  752.     [self display];
  753.     return self;
  754. }
  755.  
  756. //===================================================================
  757. // Messages received from window to change first responder status
  758. //===================================================================
  759.  
  760. //-------------------------------------------------------------------
  761. // highlight
  762.  
  763. - highlight
  764. {
  765.     envColour=BLACK;
  766.     [self display];
  767.     return self;
  768. }
  769.  
  770. //-------------------------------------------------------------------
  771. // dim
  772.  
  773. - dim
  774. {
  775.     envColour=DKGRAY;
  776.     [self display];
  777.     return self;
  778. }
  779.  
  780. //-------------------------------------------------------------------
  781. // acceptsFirstResponder: 
  782.  
  783. - (BOOL) acceptsFirstResponder
  784. {
  785.     return YES;
  786. }
  787.  
  788. //-------------------------------------------------------------------
  789. // becomeFirstResponder: 
  790.  
  791. - becomeFirstResponder
  792. {
  793.     if (theController!=NULL)
  794.         [theController update: self];
  795.     envColour=BLACK;
  796.     [self display];
  797.     return self;
  798. }
  799.  
  800. //-------------------------------------------------------------------
  801. // resignFirstResponder: 
  802.  
  803. - resignFirstResponder
  804. {
  805.     return self;
  806. }
  807.  
  808. //===================================================================
  809. // Pasteboard interface methods
  810. //===================================================================
  811.  
  812. //-------------------------------------------------------------------
  813. // copy: -- Copy the current envelope to the pasteboard
  814.  
  815. - copy:sender
  816. {
  817.     int point;                  // current point
  818.      char select;               // number representing the style
  819.     const char *style;          // string representing the style
  820.     const char *types[1];
  821.     int num=0;
  822.     char *s;                    // output string
  823.     int max;                    // max number of chars in the output string
  824.     char tmp[128];              // temporary buffer for conversions
  825.  
  826.     max=1024;
  827.     s=malloc(max*sizeof(char));                  // allocate array for output string
  828.     
  829.     if (theController==NULL)                     // get current selected style
  830.         style="MusicKit";
  831.     else {
  832.         style=[theController getStyle];
  833.         if (style==NULL) style="MusicKit";
  834.     }
  835.     select=STYLE_MK;
  836.     if (strcmp(style,"MusicKit")==0) select=STYLE_MK;
  837.     if (strcmp(style,"(x y ...)")==0) select=STYLE_pX_Yp;
  838.     if (strcmp(style,"(x,y ...)")==0) select=STYLE_pXcYp;
  839.     if (strcmp(style,"x y ...")==0) select=STYLE_X_Y;
  840.     if (strcmp(style,"x,y ...")==0) select=STYLE_XcY;
  841.     if (strcmp(style,"x,y,z ...")==0) select=STYLE_XcYcZ_;
  842.     if (strcmp(style,"x y z,...")==0) select=STYLE_X_Y_Zc;
  843.     if (strcmp(style,"((x y)...)")==0) select=STYLE_ppX_Yp;
  844.     switch(select)
  845.         {
  846.         case STYLE_MK:
  847.             strcpy(s,"[");
  848.             for (point=0; point<pointCount-1; point++) {
  849.                 sprintf(tmp,"(%5.3f,%5.3f",xValues[point],yValues[point]);
  850.                 strcat(s,tmp);
  851.                 if (sValues[point]!=defaultSmooth) {
  852.                     sprintf(tmp,",%5.3f",sValues[point]);
  853.                     strcat(s,tmp);
  854.                 }
  855.                 strcat(s,")");
  856.                 if (stickyPoint==point) strcat(s,"|");
  857.                 if (strlen(s)>max-128) {
  858.                     max+=1024;
  859.                     s=realloc(s,max*sizeof(char));
  860.                 }
  861.             }
  862.             sprintf(tmp,"(%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1]);
  863.             strcat(s,tmp);
  864.             if (sValues[point]!=defaultSmooth) {
  865.                 sprintf(tmp,",%5.3f",sValues[point]);
  866.                 strcat(s,tmp);
  867.             }
  868.             strcat(s,")");
  869.             if (stickyPoint==point) strcat(s,"|");
  870.             strcat(s,"]");
  871.             break;
  872.  
  873.         case STYLE_pX_Yp:
  874.             strcpy(s,"(");
  875.             for (point=0; point<pointCount-1; point++) {
  876.                 sprintf(tmp,"%5.3f %5.3f ",xValues[point],yValues[point]);
  877.                 strcat(s,tmp);
  878.                 if (strlen(s)>max-128) {
  879.                     max+=1024;
  880.                     s=realloc(s,max*sizeof(char));
  881.                 }
  882.             }
  883.             sprintf(tmp,"%5.3f %5.3f)",xValues[pointCount-1],yValues[pointCount-1]); 
  884.             strcat(s,tmp);
  885.             break;
  886.             
  887.         case STYLE_pXcYp:
  888.             strcpy(s,"(");
  889.             for (point=0; point<pointCount-1; point++) {
  890.                 sprintf(tmp,"%5.3f,%5.3f,",xValues[point],yValues[point]);
  891.                 strcat(s,tmp);
  892.                 if (strlen(s)>max-128) {
  893.                     max+=1024;
  894.                     s=realloc(s,max*sizeof(char));
  895.                 }
  896.             }
  897.             sprintf(tmp,"%5.3f,%5.3f)",xValues[pointCount-1],yValues[pointCount-1]); 
  898.             strcat(s,tmp);
  899.             break;
  900.  
  901.         case STYLE_X_Y:
  902.             strcpy(s,"");
  903.             for (point=0; point<pointCount-1; point++) {
  904.                 sprintf(tmp,"%5.3f %5.3f ",xValues[point],yValues[point]);
  905.                 strcat(s,tmp);
  906.                 if (strlen(s)>max-128) {
  907.                     max+=1024;
  908.                     s=realloc(s,max*sizeof(char));
  909.                 }
  910.             }
  911.             sprintf(tmp,"%5.3f %5.3f",xValues[pointCount-1],yValues[pointCount-1]); 
  912.             strcat(s,tmp);
  913.             break;
  914.             
  915.         case STYLE_XcY:
  916.             strcpy(s,"");
  917.             for (point=0; point<pointCount-1; point++) {
  918.                 sprintf(tmp,"%5.3f,%5.3f,",xValues[point],yValues[point]);
  919.                 strcat(s,tmp);
  920.                 if (strlen(s)>max-128) {
  921.                     max+=1024;
  922.                     s=realloc(s,max*sizeof(char));
  923.                 }
  924.             }
  925.             sprintf(tmp,"%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1]); 
  926.             strcat(s,tmp);
  927.             break;
  928.  
  929.         case STYLE_XcYcZ_:
  930.             strcpy(s,"");
  931.             for (point=0; point<pointCount-1; point++) {
  932.                 sprintf(tmp,"%5.3f,%5.3f,%5.3f ",xValues[point],yValues[point],sValues[point]);
  933.                 strcat(s,tmp);
  934.                 if (strlen(s)>max-128) {
  935.                     max+=1024;
  936.                     s=realloc(s,max*sizeof(char));
  937.                 }
  938.             }
  939.             sprintf(tmp,"%5.3f,%5.3f,%5.3f",xValues[pointCount-1],yValues[pointCount-1],sValues[pointCount-1]);
  940.             strcat(s,tmp);
  941.             break;
  942.  
  943.         case STYLE_X_Y_Zc:
  944.             strcpy(s,"");
  945.             for (point=0; point<pointCount-1; point++) {
  946.                 sprintf(tmp,"%5.3f %5.3f %5.3f,",xValues[point],yValues[point],sValues[point]);
  947.                 strcat(s,tmp);
  948.                 if (strlen(s)>max-128) {
  949.                     max+=1024;
  950.                     s=realloc(s,max*sizeof(char));
  951.                 }
  952.             }
  953.             sprintf(tmp,"%5.3f %5.3f %5.3f",xValues[pointCount-1],yValues[pointCount-1],sValues[pointCount-1]);
  954.             strcat(s,tmp);
  955.             break;
  956.  
  957.         case STYLE_ppX_Yp:
  958.             strcpy(s,"(");
  959.             for (point=0; point<pointCount-1; point++) {
  960.                 sprintf(tmp,"(%5.3f %5.3f)",xValues[point],yValues[point]);
  961.                 strcat(s,tmp);
  962.                 if (strlen(s)>max-128) {
  963.                     max+=1024;
  964.                     s=realloc(s,max*sizeof(char));
  965.                 }
  966.             }
  967.             sprintf(tmp,"(%5.3f %5.3f))",xValues[pointCount-1],yValues[pointCount-1]); 
  968.             strcat(s,tmp);
  969.             break;
  970.  
  971.         }
  972.     types[num++] = NXAsciiPboardType;
  973.     [[Pasteboard new] declareTypes: types num: num owner: [self class]];
  974.     [[Pasteboard new] writeType: NXAsciiPboardType data:s length:strlen(s)];
  975.     free(s);
  976.     return self;
  977. }
  978.  
  979. //-------------------------------------------------------------------
  980. // paste: -- Paste the current pasteboard contents into the view. The
  981. // method parses the text representation of the envelope automatically
  982. // deciding on the type of envelope received. The internal representation
  983. // is a standard MusicKit envelope object.
  984.  
  985. // Get next token from a string (symbol or number)
  986.  
  987. #define NUMBER 0
  988.  
  989. char *data;
  990.  
  991. // Parse the data stream in tokens representing symbols and numbers
  992.  
  993. int token(char *t)
  994. {
  995.     char c;
  996.     int i;
  997.     
  998.     while (((t[0]=c=*data++) == ' ')||(c == '\t')||(c == '\n'));
  999.     t[1]='\0';
  1000.     if (c=='\0') {
  1001.         data--;
  1002.         return EOF;
  1003.     }
  1004.     if (!isdigit(c) && c!= '.' && c!= '-')
  1005.         return c;        
  1006.     i=0;                            // collect a number stream
  1007.     if (c=='-')
  1008.         t[++i]=c=*data++;
  1009.     if (isdigit(c))
  1010.         while (isdigit(t[++i]=c=*data++));
  1011.     if (c == '.')
  1012.         while (isdigit(t[++i]=c=*data++));
  1013.     
  1014.     if ((c == 'e')||(c == 'E')) {
  1015.         t[++i]=c=*data++;
  1016.         if (c=='-')
  1017.             t[++i]=c=*data++;
  1018.         if (isdigit(c))
  1019.             while (isdigit(t[++i]=c=*data++));
  1020.     }
  1021.     
  1022.     t[i]='\0';
  1023.     if (c!='\0')
  1024.         data--;
  1025.     return NUMBER;
  1026. }
  1027.  
  1028. - paste:sender
  1029. {
  1030.     char *prs, *orig, tk[1024];
  1031.     int   symb, length;
  1032.     const NXAtom *pastetypes;
  1033.     int sticky, point;
  1034.  
  1035.     pastetypes = [[Pasteboard new] types];    
  1036.     if ([[Pasteboard new] 
  1037.         readType: NXAsciiPboardType 
  1038.         data:&prs 
  1039.         length:&length]) {                            // if ASCII in pasteboard...
  1040.     
  1041.         data=orig=calloc(length+16,sizeof(char));     // copy data to local buffer
  1042.         strncpy(data,prs,length);
  1043.         
  1044.         point=0;                                      // start by converting point 0
  1045.         sticky=MAXINT;                                // no sticky point by default
  1046.         allocateTemp(64);                             // 64 points long by default
  1047.         
  1048.         if ((symb=token(tk))=='(') {
  1049.         if (symb=token(tk)=='(') {
  1050.         
  1051.     // parse a list of lists type envelope
  1052.     // accepted syntax: "((x0 y0)...(xn yn))" or "((x0,y0)...(xn,yn))"
  1053.  
  1054.             while((symb!=')')&&(symb!=-1)) {
  1055.             symb=token(tk);                           // should be "x" component
  1056.                 if (symb!=NUMBER) {                   // must be x component
  1057.                     NXRunAlertPanel("Error",
  1058.                     "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
  1059.                     NULL,NULL,"Continue",data-1,orig);
  1060.                     break;
  1061.                 }
  1062.                 else {
  1063.                     temp->x[point]=atof(tk);           // convert "x" component!
  1064.                     temp->y[point]=0.0;                // and set defaults
  1065.                     temp->s[point]=defaultSmooth;
  1066.                 }
  1067.         symb=token(tk);
  1068.                 if (symb!=NUMBER) {
  1069.                     NXRunAlertPanel("Error",
  1070.                     "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
  1071.                     NULL,NULL,"Continue",data-1,orig);
  1072.                     break;
  1073.                 }
  1074.                 else {
  1075.                     temp->y[point]=atof(tk);          // convert "y" component
  1076.                     point++;                          // count a complete envelope node
  1077.                     allocateTemp(point+1);
  1078.                 }
  1079.                 if (symb=token(tk)!=')') {
  1080.                     NXRunAlertPanel("Error",
  1081.                     "Expected closing parenthesis at:\n\"%s\"\nin:\n\"%s\"",
  1082.                     NULL,NULL,"Continue",data-1,orig);
  1083.                     break;
  1084.                 }
  1085.                 else symb=token(tk);
  1086.             }
  1087.             showSmooth=0;                             // only MK shows smoothing by default
  1088.         }    
  1089.     else {
  1090.     
  1091.     // parse a CLM type envelope
  1092.     // accepted syntax: "(x0 y0 ... xn yn)" or "(x0,y0, ... xn,yn)"
  1093.             
  1094.             while((symb!=')')&&(symb!=-1)) {
  1095.                 if (symb==',') symb=token(tk);        // ignore commas between xy pairs
  1096.                 if (symb!=NUMBER) {                   // must be x component
  1097.                     NXRunAlertPanel("Error",
  1098.                     "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
  1099.                     NULL,NULL,"Continue",data-1,orig);
  1100.                     break;
  1101.                 }
  1102.                 else {
  1103.                     temp->x[point]=atof(tk);           // convert "x" component!
  1104.                     temp->y[point]=0.0;                // and set defaults
  1105.                     temp->s[point]=defaultSmooth;
  1106.                 }
  1107.                 if ((symb=token(tk))==',')             // ignore commas between values
  1108.                     symb=token(tk);
  1109.                 if (symb!=NUMBER) {
  1110.                     NXRunAlertPanel("Error",
  1111.                     "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
  1112.                     NULL,NULL,"Continue",data-1,orig);
  1113.                     break;
  1114.                 }
  1115.                 else {
  1116.                     temp->y[point]=atof(tk);          // convert "y" component
  1117.                     point++;                          // count a complete envelope node
  1118.                     allocateTemp(point+1);
  1119.                 }
  1120.                 symb=token(tk);
  1121.             }
  1122.             showSmooth=0;                             // only MK shows smoothing by default
  1123.         }
  1124.     }
  1125.         else if (symb=='[')    {    
  1126.         
  1127.     // parse a MusicKit envelope
  1128.     // uses normal MusicKit syntax
  1129.             
  1130.             symb=token(tk);                            // should be starting '(' or '|'
  1131.             while((symb=='(')||(symb=='|')) {
  1132.                 if (symb=='|') {
  1133.                     sticky=point-1;                    // last point was the sticky point
  1134.                     symb=token(tk);                    // should be '(' or the end
  1135.                     continue;
  1136.                 }
  1137.                 if ((symb=token(tk))!=NUMBER) {        // break if not a number
  1138.                     NXRunAlertPanel("Error",
  1139.                     "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
  1140.                     NULL,NULL,"Continue",data-1,orig);
  1141.                     break;
  1142.                 }
  1143.                 else {
  1144.                     temp->x[point]=atof(tk);           // convert "x" component!
  1145.                     temp->y[point]=0.0;
  1146.                     temp->s[point]=defaultSmooth;
  1147.                 }
  1148.                 if ((symb=token(tk))==',')             // skip comma but also accept a space
  1149.                     symb=token(tk);
  1150.                 if (symb!=NUMBER) {                    // break if not a number
  1151.                     NXRunAlertPanel("Error",
  1152.                     "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
  1153.                     NULL,NULL,"Continue",data-1,orig);
  1154.                     break;
  1155.                 }
  1156.                 else {
  1157.                     temp->y[point]=atof(tk);           // convert "y" component
  1158.                 }
  1159.                 if ((symb=token(tk))==',')             // is there a smoothing component?
  1160.                     if ((symb=token(tk))==NUMBER) {    // if number...
  1161.                         temp->s[point]=atof(tk);       // convert smoothing component
  1162.                         symb=token(tk);
  1163.                     }
  1164.                 point++;                               // count a complete envelope node
  1165.                 allocateTemp(point+1);
  1166.  
  1167.                 if (symb!=')') {                       // must be point's closing parenthesis
  1168.                     NXRunAlertPanel("Error",
  1169.                     "Expected a ')' at:\n\"%s\"\nin:\n\"%s\"",
  1170.                     NULL,NULL,"Continue",data-1,orig);
  1171.                     break;
  1172.                 }
  1173.                 symb=token(tk);
  1174.             }
  1175.             showSmooth=-1;
  1176.         }
  1177.         else if (symb==NUMBER)    {
  1178.  
  1179.     // parse a x-y-<z> pair type envelope
  1180.     // accepted syntax: "x0 y0 ... xn yn" or "x0,y0, ... xn,yn" for xy pairs
  1181.     // accepted syntax: "x0,y0,z0 x1,y1,z1 ..." or " x0 y0 z0,x1 y1 z1, ..." for xyz sets
  1182.             
  1183.             int commas=0;
  1184.             double last_value=0.0;
  1185.             int pending=0;
  1186.  
  1187.             while(symb!=-1) {
  1188.                 if (pending==0) {
  1189.                     if (symb!=NUMBER) {                  // not a number!
  1190.                         NXRunAlertPanel("Error",
  1191.                         "Expected x component at:\n\"%s\"\nin:\n\"%s\"",
  1192.                         NULL,NULL,"Continue",data-1,orig);
  1193.                         break;
  1194.                     }
  1195.                     else {
  1196.                         temp->x[point]=atof(tk);        // convert "x" component!
  1197.                         temp->y[point]=0;
  1198.                         temp->s[point]=defaultSmooth;
  1199.                     }
  1200.                     if ((symb=token(tk))==',') {
  1201.                         commas++;
  1202.                         symb=token(tk);
  1203.                     }
  1204.                 }
  1205.                 else {
  1206.                     temp->x[point]=last_value;
  1207.                     temp->y[point]=0.0;
  1208.                     temp->s[point]=defaultSmooth;
  1209.                     pending=0;
  1210.                 }
  1211.                 if (symb!=NUMBER) {
  1212.                     NXRunAlertPanel("Error",
  1213.                     "Expected y component at:\n\"%s\"\nin:\n\"%s\"",
  1214.                     NULL,NULL,"Continue",data-1,orig);
  1215.                     break;
  1216.                 }
  1217.                 else temp->y[point]=atof(tk);        // convert "y" component
  1218.                 if ((symb=token(tk))==',') {
  1219.                     commas++;
  1220.                     symb=token(tk);
  1221.                 }
  1222.                 if (symb==-1) {
  1223.                     point++;
  1224.                     allocateTemp(point+1);
  1225.                     break;
  1226.                 }
  1227.                 else {
  1228.                     if (symb!=NUMBER) {
  1229.                         NXRunAlertPanel("Error",
  1230.                         "Expected x or z component at:\n\"%s\"\nin:\n\"%s\"",
  1231.                         NULL,NULL,"Continue",data-1,orig);
  1232.                         break;
  1233.                     }
  1234.                     else last_value=atof(tk);
  1235.                     if ((((symb=token(tk))!=',')&&(commas==2)) ||
  1236.                          ((symb==',')&&(commas==0))) {
  1237.                         temp->s[point]=last_value;
  1238.                         pending=0;
  1239.                         commas=0;
  1240.                     }
  1241.                     else {
  1242.                         pending=1;
  1243.                         commas=1;
  1244.                     }
  1245.                     point++;
  1246.                     allocateTemp(point+1);
  1247.                 }
  1248.                 if (symb==',') symb=token(tk);
  1249.             }
  1250.             showSmooth=0;                            // only MK shows smoothing by default
  1251.         }
  1252.         else
  1253.             NXRunAlertPanel(
  1254.                 "Error",
  1255.                 "The envelope must start with '[','(' or a number:\n\"%s\"",
  1256.                 NULL,NULL,"Continue",orig);
  1257.         
  1258.         vm_deallocate(task_self(),(vm_address_t)prs, length);
  1259.         
  1260.         if (point<2) {                                // if envelope has less than 2 nodes
  1261.             NXRunAlertPanel(
  1262.                 "Error",
  1263.                 "Less than 2 legal points in envelope:\n\"%s\"",
  1264.                 NULL,NULL,"Continue",orig);
  1265.         }
  1266.         else {
  1267.             [theEnvelope
  1268.                 setPointCount: point
  1269.                 xArray: temp->x
  1270.                 orSamplingPeriod: 1.0
  1271.                 yArray: temp->y
  1272.                 smoothingArray: temp->s
  1273.                 orDefaultSmoothing: defaultSmooth];
  1274.             [theEnvelope setStickPoint: sticky];      // update envelope object
  1275.             allocateDraw(temp->max); //(pointCount);                 // resize drawing arrays
  1276.             [self scaleLimits];                       // define drawing limits
  1277.         }
  1278.         if (selected<pointCount)
  1279.             [self selectPoint: selected];
  1280.         else
  1281.             [self selectPoint: 0];                    // display and update controller
  1282.         free(orig);                                   // free local copy of data
  1283.     }
  1284.     return self;
  1285. }
  1286.  
  1287. //===================================================================
  1288. // Messages received from controller to change parameters
  1289. //===================================================================
  1290.  
  1291. //-------------------------------------------------------------------
  1292. // setPointTo: set current point to n (as a side effect updates values
  1293. // of x and y coordinates on the controller object
  1294.  
  1295. - setPointTo: (int)point
  1296. {
  1297.     if (point>=pointCount) point=pointCount-1;
  1298.     if (point<0) point=0;
  1299.     [self selectPoint: point];
  1300.     [theController update: self];
  1301.     return self;
  1302. }
  1303.  
  1304. //-------------------------------------------------------------------
  1305. // nextPoint go to the next point in the envelope if possible
  1306.  
  1307. - nextPoint
  1308. {
  1309.     int next;
  1310.     
  1311.     next=selected+1;
  1312.     if (next>=pointCount) next=selected;
  1313.     if (next<0) next=0;
  1314.     if (next!=selected) {
  1315.         [self selectPoint:next];
  1316.         [theController update: self];
  1317.     }
  1318.     return self;
  1319. }
  1320.  
  1321. //-------------------------------------------------------------------
  1322. // previousPoint go to the previous point in the envelope if possible
  1323.  
  1324. - previousPoint
  1325. {
  1326.     int previous;
  1327.     
  1328.     previous=selected-1;
  1329.     if (previous>=pointCount) previous=selected;
  1330.     if (previous<0) previous=0;
  1331.     if (previous!=selected) {
  1332.         [self selectPoint:previous];
  1333.         [theController update: self];
  1334.     }
  1335.     return self;
  1336. }
  1337.  
  1338. //-------------------------------------------------------------------
  1339. // setXAt:to: changes value of x coordinate of point n
  1340.  
  1341. - setXAt: (int)n to: (float)coord
  1342. {
  1343.     if (n!=0 && coord<xValues[n-1])             // force selected point to be within
  1344.         coord=xValues[n-1];                     // enclosing points
  1345.     if (n!=pointCount-1 && coord>xValues[n+1]) 
  1346.         coord=xValues[n+1];
  1347.     if (coord!=xValues[n]) {                    // if x changed update display panel
  1348.         xValues[n]=coord;
  1349.         [self display];
  1350.         [theController update: self];
  1351.     }
  1352.     return self;
  1353. }
  1354.  
  1355. //-------------------------------------------------------------------
  1356. // setYAt:to: changes value of y coordinate of point n
  1357.  
  1358. - setYAt: (int)n to: (float)coord
  1359. {
  1360.     if (coord>yMax) coord=yMax;                // clip y values to max and min
  1361.     if (coord<yMin) coord=yMin;
  1362.     
  1363.     if (coord!=yValues[n]) {                   // if y changed update display panel
  1364.         yValues[n]=coord;
  1365.         [self display];
  1366.         [theController update: self];
  1367.     }
  1368.     return self;
  1369. }
  1370.  
  1371. //-------------------------------------------------------------------
  1372. // setYrAt:to: changes value of real y coordinate of point n
  1373.  
  1374. - setYrAt: (int)n to: (float)coord
  1375. {
  1376.     double y;
  1377.     
  1378.     if (coord>yMax) coord=yMax;               // clip y values to max and min
  1379.     if (coord<yMin) coord=yMin;
  1380.     
  1381.     if (n==0) return self;                    // no sense to change this in first point
  1382.     
  1383.     draw->yr[n]=coord;
  1384.     y=(coord-yValues[n-1]*exp(-5.5262/sValues[n]))/(1-exp(-5.5262/sValues[n]));
  1385.     [self setYAt: n to: y];
  1386.     return self;
  1387. }
  1388.  
  1389. //-------------------------------------------------------------------
  1390. // setSmoothAt:to: changes value of smoothing of point n
  1391.  
  1392. - setSmoothAt: (int)n to: (float)value
  1393. {
  1394.     if (value!=sValues[n]) {                  // if smoothing changed update panel
  1395.         sValues[n]=value;
  1396.         [self display];
  1397.         [theController update: self];
  1398.     }
  1399.     return self;
  1400. }
  1401.  
  1402. //-------------------------------------------------------------------
  1403. // setXMinTo: changes minimum value of x component of envelope
  1404.  
  1405. - setXMinTo: (float)coord
  1406. {
  1407.     if (coord<xMax) {
  1408.         xMin=coord;
  1409.         [self display];
  1410.     }
  1411.     else if (theController!=NULL)
  1412.         [theController update: self];
  1413.     return self;
  1414. }
  1415.  
  1416. //-------------------------------------------------------------------
  1417. // setXMaxTo: changes maximun value of x component of envelope
  1418.  
  1419. - setXMaxTo: (float)coord
  1420. {
  1421.     if (coord>xMin) {
  1422.         xMax=coord;
  1423.         [self display];
  1424.     }
  1425.     else if (theController!=NULL)
  1426.         [theController update: self];
  1427.     return self;
  1428. }
  1429.  
  1430. //-------------------------------------------------------------------
  1431. // setXLimitsTo:: changes max and min values of x component
  1432.  
  1433. - setXLimitsTo: (float)min : (float)max
  1434. {
  1435.     xMin=min;
  1436.     xMax=max;
  1437.     [self display];
  1438.     return self;
  1439. }
  1440.  
  1441. //-------------------------------------------------------------------
  1442. // setYMinTo: changes minimum value of y component of envelope
  1443.  
  1444. - setYMinTo: (float)coord
  1445. {
  1446.     if (coord<yMax) {
  1447.         yMin=coord;
  1448.         [self display];
  1449.     }
  1450.     else if (theController!=NULL)
  1451.         [theController update: self];
  1452.     return self;
  1453. }
  1454.  
  1455. //-------------------------------------------------------------------
  1456. // setYMaxTo: changes maximun value of y component of envelope
  1457.  
  1458. - setYMaxTo: (float)coord
  1459. {
  1460.     if (coord>yMax) {
  1461.         yMax=coord;
  1462.         [self display];
  1463.     }
  1464.     else if (theController!=NULL)
  1465.         [theController update: self];
  1466.     return self;
  1467. }
  1468.  
  1469. //-------------------------------------------------------------------
  1470. // setXSnapTo: changes value of x Snap
  1471.  
  1472. - setXSnapTo: (float)coord
  1473. {
  1474.     xSnap=coord;
  1475.     [self display];
  1476.     return self;
  1477. }
  1478.  
  1479. //-------------------------------------------------------------------
  1480. // setYSnapTo: changes value of y Snap
  1481.  
  1482. - setYSnapTo: (float)coord
  1483. {
  1484.     ySnap=coord;
  1485.     [self display];
  1486.     return self;
  1487. }
  1488.  
  1489. //-------------------------------------------------------------------
  1490. // setStickyAt:To: sets point to be the sticky point of envelope
  1491.  
  1492. - setStickyAt:(int) point To: (int)state
  1493. {
  1494.     if (state==0)
  1495.         [theEnvelope setStickPoint: MAXINT];
  1496.     else
  1497.         [theEnvelope setStickPoint: point];
  1498.     [theController update: self];
  1499.     [self display];
  1500.     return self;
  1501. }
  1502.  
  1503. //-------------------------------------------------------------------
  1504. // setShowSmooth: sets type of graphics to be used
  1505.  
  1506. - setShowSmooth: (int)state
  1507. {
  1508.     showSmooth=state;
  1509.     [self display];
  1510.     [theController update: self];
  1511.     return self;
  1512. }
  1513.  
  1514. //-------------------------------------------------------------------
  1515. // setDrawSegments: choose to draw segments or only points
  1516.  
  1517. - setDrawSegments: (int)state
  1518. {
  1519.     drawSegments=state;
  1520.     [self display];
  1521.     [theController update: self];
  1522.     return self;
  1523. }
  1524.  
  1525. //-------------------------------------------------------------------
  1526. // scaleLimits computes the max and min bounds of the view
  1527.  
  1528. - scaleLimits
  1529. {
  1530.     double xmin, xmax, ymin, ymax;
  1531.     int point;
  1532.     
  1533.     xmin=xmax=xValues[0];
  1534.     ymin=ymax=yValues[0];
  1535.     for (point=1; point<pointCount; point++) {
  1536.         if (xValues[point]<xmin) xmin=xValues[point];
  1537.         if (xValues[point]>xmax) xmax=xValues[point];
  1538.         if (yValues[point]<ymin) ymin=yValues[point];
  1539.         if (yValues[point]>ymax) ymax=yValues[point];
  1540.     }
  1541.     xMax=xmax;
  1542.     xMin=xmin;
  1543.     if (ymax>=0.0 && ymax<=10.0)
  1544.         yMax=rint(ymax*10+1.0)/10;
  1545.     else
  1546.         yMax=rint(ymax+1.0);
  1547.     if (ymin<=0.0 && ymin>=-1.0)
  1548.         yMin=rint(ymin*10-1.0)/10;
  1549.     else
  1550.         yMin=rint(ymin-1.0);
  1551.     if (ymin==0) yMin=ymin;
  1552.     
  1553.     [self display];
  1554.     return self;
  1555. }
  1556.  
  1557. //===================================================================
  1558. // Messages received from controller to query for envelope values
  1559. //===================================================================
  1560.  
  1561. //-------------------------------------------------------------------
  1562. // (int)getPoint Return the selected point
  1563.  
  1564. - (int)getPoint
  1565. {
  1566.     return selected;
  1567. }
  1568.  
  1569. //-------------------------------------------------------------------
  1570. // (float)getX:(int)i Return value of x component of point i
  1571.  
  1572. - (float)getX:(int)i
  1573. {
  1574.     if (i>=pointCount) i=pointCount-1;
  1575.     if (i<0) i=0;
  1576.     return xValues[i];
  1577. }
  1578.  
  1579. //-------------------------------------------------------------------
  1580. // (float)getY:(int)i Return value of y component of point i
  1581.  
  1582. - (float)getY:(int)i
  1583. {
  1584.     if (i>=pointCount) i=pointCount-1;
  1585.     if (i<0) i=0;
  1586.     return yValues[i];
  1587. }
  1588.  
  1589. //-------------------------------------------------------------------
  1590. // (float)getYr:(int)i Return value of real y component of point i
  1591.  
  1592. - (float)getYr:(int)i
  1593. {
  1594.     if (i>=pointCount) i=pointCount-1;
  1595.     if (i<0) i=0;
  1596.     return draw->yr[i];
  1597. }
  1598.  
  1599. //-------------------------------------------------------------------
  1600. // (float)getSmoothing:(int)i Return value of smoothing of point i
  1601.  
  1602. - (float)getSmoothing:(int)i
  1603. {
  1604.     if (i>=pointCount) i=pointCount-1;
  1605.     if (i<0) i=0;
  1606.     return sValues[i];
  1607. }
  1608.  
  1609. //-------------------------------------------------------------------
  1610. // (int)getSticky:(int)i Return value of stickyness of point i
  1611.  
  1612. - (int)getSticky:(int)i
  1613. {
  1614.     if (i>=pointCount) i=pointCount-1;
  1615.     if (i<0) i=0;
  1616.     if (stickyPoint==i)
  1617.         return 1;
  1618.     else
  1619.         return 0;
  1620. }
  1621.  
  1622. //-------------------------------------------------------------------
  1623. // (float)getXMax 
  1624.  
  1625. - (float) getXMax { return xMax; }
  1626.  
  1627. //-------------------------------------------------------------------
  1628. // (float)getXMin 
  1629.  
  1630. - (float) getXMin { return xMin; }
  1631.  
  1632. //-------------------------------------------------------------------
  1633. // (float)getYMax 
  1634.  
  1635. - (float) getYMax { return yMax; }
  1636.  
  1637. //-------------------------------------------------------------------
  1638. // (float)getYMin 
  1639.  
  1640. - (float) getYMin { return yMin; }
  1641.  
  1642. //-------------------------------------------------------------------
  1643. // (float)getXSnap 
  1644.  
  1645. - (float) getXSnap { return xSnap; }
  1646.  
  1647. //-------------------------------------------------------------------
  1648. // (float)getYSnap 
  1649.  
  1650. - (float) getYSnap { return ySnap; }
  1651.  
  1652. //-------------------------------------------------------------------
  1653. // (int)getShowSmooth 
  1654.  
  1655. - (int)getShowSmooth
  1656. {
  1657.     return showSmooth;
  1658. }
  1659.  
  1660. //-------------------------------------------------------------------
  1661. // (int)getDrawSegments 
  1662.  
  1663. - (int)getDrawSegments
  1664. {
  1665.     return drawSegments;
  1666. }
  1667.  
  1668. @end
  1669.  
  1670.