Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / GUI / osx / scide_scapp / SCEnvelopeEdit.sc
blobac2291bd55d1601183ffd286a8f3dfbf07934536
1 // by Lance Putnam
2 // lance@uwalumni.com
3 // allows now to insert breakpoint on double-click by jan trutzschler
5 SCEnvelopeEdit : SCEnvelopeView {
6         var <env, <pointsPerSegment, viewPoints;
7         var <minLevel, <maxLevel, <minTime, <maxTime, totalDurRec, absTimes, numPoints;
9         *viewClass { ^SCEnvelopeView }
11         *new { arg parent, bounds, env, pointsPerSegment=10;
12                 ^super.new(parent, bounds).initSCEnvelopeEdit(env, pointsPerSegment)
13         }
15         initSCEnvelopeEdit { arg argEnv, argPPS, setMinMax=true;
16                 env = argEnv;
17                 pointsPerSegment = argPPS.asInteger;
18                 if(setMinMax){
19                         minLevel = argEnv.levels.minItem;
20                         maxLevel = argEnv.levels.maxItem;
21                         if(minLevel == maxLevel){ minLevel = 0.0 };
22                 };
23                 minTime = 0;
24                 maxTime = env.times.sum;
25                 totalDurRec = 1/(maxTime - minTime);
27                 absTimes = Array.newClear(env.times.size + 1);
28                 absTimes[0] = 0;
29                 for(1, env.times.size, { arg i;
30                         absTimes[i] = absTimes[i-1] + env.times[i-1];
31                 });
33                 numPoints = (pointsPerSegment * env.times.size) + 1;  // add 1 for the last point
34                 viewPoints = Array.with(Array.newClear(numPoints), Array.newClear(numPoints));
36                 this
37                         .selectionColor_(Color.clear)
38                         .drawLines_(true)       // resize broken when no lines drawn
39                         .drawRects_(true)
40                 ;
41                 this.mouseDownAction_{|view, x, y, modifiers, buttonNumber, clickCount|
42                         this.defaultMauseDownAction(x, y, modifiers, buttonNumber, clickCount);
43                 };
44                 this.action = { arg view;
45                         var bp, bpm1, bpp1, bpLevel, timePos;
47                         // if it's a breakpoint
48                         if((view.index % pointsPerSegment) == 0, {
49                                 bp = view.index.div(pointsPerSegment);
50                                 bpm1 = bp - 1;
51                                 bpp1 = bp + 1;
53                                 bpLevel = view.currentvalue.linlin(0.0, 1.0, minLevel, maxLevel);
54                                 env.levels[bp] = bpLevel;
56                                 timePos = view.value[0][view.index].linlin(0.0, 1.0, minTime, maxTime);
58                                 // first breakpoint
59                                 if(bp == 0, {
60                                         if( timePos <= absTimes[bpp1], {
61                                                 env.times[bp] = absTimes[bpp1] - timePos;
62                                                 absTimes[bp] = timePos;
63                                         },{ // going past right break point
64                                                 env.times[bp] = 0;
65                                                 absTimes[bp] = absTimes[bpp1];
66                                         });
67                                         this.updateSegment(bp);
68                                 // end breakpoint
69                                 },{ if(bp == env.times.size, {
70                                         if( timePos >= absTimes[bpm1], {
71                                                 env.times[bpm1] = timePos - absTimes[bpm1];
72                                                 absTimes[bp] = timePos;
73                                         },{     // going past left break point
74                                                 env.times[bpm1] = 0;
75                                                 absTimes[bp] = absTimes[bpm1];
76                                         });
77                                         this.updateSegment(bpm1);
78                                 // a middle break point
79                                 },{
80                                         if(timePos > absTimes[bpp1], {  // past right break point
81                                                 env.times[bpm1] = absTimes[bp] - absTimes[bpm1];
82                                                 env.times[bp] = 0;
83                                                 absTimes[bp] = absTimes[bpp1];
84                                         },{ if(timePos < absTimes[bpm1], { // past left break point
85                                                 env.times[bpm1] = 0;
86                                                 env.times[bp] = absTimes[bpp1] - absTimes[bp];
87                                                 absTimes[bp] = absTimes[bpm1];
88                                         },{
89                                                 // set left segment dur
90                                                 env.times[bpm1] = timePos - absTimes[bpm1];
92                                                 // set right segment dur
93                                                 env.times[bp] = absTimes[bpp1] - timePos;
95                                                 absTimes[bp] = timePos;
96                                         }); });
97                                         this.updateSegment(bpm1);
98                                         this.updateSegment(bp);
99                                         if((timePos <= absTimes[bpp1]) && (timePos >= absTimes[bpm1]), {
102                                         });
103                                 }); });
105                                 this.redraw;
106                         });
107                 };
109                 this.updateAll;
110                 this.redraw;
112                 numPoints.do({ arg i;
113                         // make a breakpoint
114                         if((i%pointsPerSegment) == 0, {
115                                 this.setThumbSize(i, 6);
117                                 // color code breakpoints
118                                 if(i.div(pointsPerSegment) == env.releaseNode, {
119                                         this.setFillColor(i, Color.red(0.7));
120                                 },{
121                                         if(i.div(pointsPerSegment) == env.loopNode, {
122                                                 this.setFillColor(i, Color.green(0.7));
123                                         },{
124                                                 this.setFillColor(i, Color.blue(0.7));
125                                         });
126                                 });
128                         // Other points should be hidden.
129                         },{ this.setThumbSize(i, 0) });
130                 });
132         }
134         redraw {
135                 this.value = viewPoints;
136         }
138         refresh {
139                 this.updateAll;
140                 this.value = viewPoints;
141         }
143         updateAll {
144                 env.times.size.do({ arg i;
145                         this.updateSegment(i);
146                 });
147         }
149         // updates segment values in viewPoints array
150         updateSegment { arg segNum;
151                 var time, slope, index1, index2, timeOffset;
153                 // update envelope cache
154 //              this.debug(env.levels === env.levels);
155                 env.times = env.times;
156 //              env.levels = env.levels;
157 //              env.curves = env.curves;
159                 segNum = segNum.asInteger;
161                 time = absTimes[segNum];
162                 timeOffset = absTimes[0];
164                 slope = env.times[segNum] / pointsPerSegment;
166                 index1 = pointsPerSegment * segNum;
167                 index2 = index1 + pointsPerSegment - 1;
169                 for(index1, index2, { arg i;
170                         viewPoints[0][i] = time.linlin(minTime, maxTime, 0.0, 1.0);
171                         viewPoints[1][i] = env[time - timeOffset].linlin(minLevel, maxLevel, 0.0, 1.0);
172                         time = time + slope;
173                 });
175                 // draw break point at right level
176                 if(slope == 0, {
177                         viewPoints[1][index1] = env.levels[segNum].linlin(minLevel, maxLevel, 0.0, 1.0);
178                 });
180                 // the last segment has an extra point at the end
181                 if(segNum == (env.times.size-1), {
182                         index2 = index2 + 1;
183                         viewPoints[0][index2] = time.linlin(minTime, maxTime, 0.0, 1.0);
184                         viewPoints[1][index2] = env.levels.last.linlin(minLevel, maxLevel, 0.0, 1.0);
185                 });
186         }
188         minLevel_ { arg level;
189                 minLevel = level;
190                 this.refresh;
191         }
193         maxLevel_ { arg level;
194                 maxLevel = level;
195                 this.refresh;
196         }
198         minTime_ { arg sec;
199                 minTime = sec;
200                 this.refresh;
201         }
203         maxTime_ { arg sec;
204                 maxTime = sec;
205                 this.refresh;
206         }
208         //added by jt ..
210         defaultMauseDownAction{|x, y, modifiers, buttonNumber, clickCount|
211                 var level, time, tbounds;
212                 tbounds = this.bounds;
213                 level = y.linlin(tbounds.top, tbounds.top+tbounds.height, maxLevel, minLevel);
214                 time = x.linlin(tbounds.left, tbounds.left+tbounds.width, minTime, maxTime);
215 //              this.debug([time, level]);
216                 if(clickCount > 1){
217                         //add value
218                         this.insertAtTime(time, level);
219                 }
220         }
222         env_{|e|
223                 this.initSCEnvelopeEdit(e, pointsPerSegment);
224         }
226         addBreakPoint{|level|
227                 var tenv;
228                 tenv = env.addBreakPoint(level, 0);
229                 this.initSCEnvelopeEdit(tenv, pointsPerSegment, false);
230         }
231         insertAtTime{|time, level|
232                 var tenv;
233                 tenv = env.insertAtTime(time, level);
234                 this.initSCEnvelopeEdit(tenv, pointsPerSegment, false);
235         }
236         clear{
237                 env = nil; pointsPerSegment=10; viewPoints=0;
238                 minLevel = maxLevel = minTime = maxTime = totalDurRec = absTimes = numPoints = 0;
239                 viewPoints = [[],[]];
240                 this.action_{};
241         }
243         *paletteExample { arg parent, bounds;
244                 var v;
245                 v = this.new(parent, bounds, Env.perc);
246                 ^v
247         }