restore compilability amidst automation state mgmt changes
[ardour.git] / libs / ardour / automation_event.cc
1 /*
2     Copyright (C) 2002 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <set>
22 #include <climits>
23 #include <float.h>
24 #include <cmath>
25 #include <sstream>
26 #include <algorithm>
27 #include <sigc++/bind.h>
28 #include <ardour/automation_event.h>
29
30 #include "i18n.h"
31
32 using namespace std;
33 using namespace ARDOUR;
34 using namespace sigc;
35 using namespace PBD;
36
37 sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
38
39 #if 0
40 static void dumpit (const AutomationList& al, string prefix = "")
41 {
42         cerr << prefix << &al << endl;
43         for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
44                 cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
45         }
46         cerr << "\n";
47 }
48 #endif
49
50 AutomationList::AutomationList (double defval, bool with_state)
51 {
52         _frozen = false;
53         changed_when_thawed = false;
54         _state = Off;
55         _style = Absolute;
56         _touching = false;
57         no_state = with_state;
58         min_yval = FLT_MIN;
59         max_yval = FLT_MAX;
60         max_xval = 0; // means "no limit" 
61         default_value = defval;
62         _dirty = false;
63         rt_insertion_point = events.end();
64         lookup_cache.left = -1;
65         lookup_cache.range.first = events.end();
66
67         AutomationListCreated(this);
68 }
69
70 AutomationList::AutomationList (const AutomationList& other)
71 {
72         _frozen = false;
73         changed_when_thawed = false;
74         _style = other._style;
75         min_yval = other.min_yval;
76         max_yval = other.max_yval;
77         max_xval = other.max_xval;
78         default_value = other.default_value;
79         _state = other._state;
80         _touching = other._touching;
81         _dirty = false;
82         rt_insertion_point = events.end();
83         no_state = other.no_state;
84         lookup_cache.left = -1;
85         lookup_cache.range.first = events.end();
86
87         for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
88                 /* we have to use other point_factory() because
89                    its virtual and we're in a constructor.
90                 */
91                 events.push_back (other.point_factory (**i));
92         }
93
94         mark_dirty ();
95         AutomationListCreated(this);
96 }
97
98 AutomationList::AutomationList (const AutomationList& other, double start, double end)
99 {
100         _frozen = false;
101         changed_when_thawed = false;
102         _style = other._style;
103         min_yval = other.min_yval;
104         max_yval = other.max_yval;
105         max_xval = other.max_xval;
106         default_value = other.default_value;
107         _state = other._state;
108         _touching = other._touching;
109         _dirty = false;
110         rt_insertion_point = events.end();
111         no_state = other.no_state;
112         lookup_cache.left = -1;
113         lookup_cache.range.first = events.end();
114
115         /* now grab the relevant points, and shift them back if necessary */
116
117         AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
118
119         if (!section->empty()) {
120                 for (AutomationList::iterator i = section->begin(); i != section->end(); ++i) {
121                         events.push_back (other.point_factory ((*i)->when, (*i)->value));
122                 }
123         }
124
125         delete section;
126
127         mark_dirty ();
128         AutomationListCreated(this);
129 }
130
131 AutomationList::~AutomationList()
132 {
133         GoingAway ();
134
135         for (AutomationEventList::iterator x = events.begin(); x != events.end(); ++x) {
136                 delete (*x);
137         }
138 }
139
140 bool
141 AutomationList::operator== (const AutomationList& other)
142 {
143         return events == other.events;
144 }
145
146 AutomationList&
147 AutomationList::operator= (const AutomationList& other)
148 {
149         if (this != &other) {
150                 
151                 events.clear ();
152                 
153                 for (const_iterator i = other.events.begin(); i != other.events.end(); ++i) {
154                         events.push_back (point_factory (**i));
155                 }
156                 
157                 min_yval = other.min_yval;
158                 max_yval = other.max_yval;
159                 max_xval = other.max_xval;
160                 default_value = other.default_value;
161                 
162                 mark_dirty ();
163                 maybe_signal_changed ();
164         }
165
166         return *this;
167 }
168
169 void
170 AutomationList::maybe_signal_changed ()
171 {
172         mark_dirty ();
173
174         if (_frozen) {
175                 changed_when_thawed = true;
176         } else {
177                 StateChanged (Change (0));
178         }
179 }
180
181 void
182 AutomationList::set_automation_state (AutoState s)
183 {
184         if (s != _state) {
185                 _state = s;
186                 automation_state_changed (); /* EMIT SIGNAL */
187         }
188 }
189
190 void
191 AutomationList::set_automation_style (AutoStyle s)
192 {
193         if (s != _style) {
194                 _style = s;
195                 automation_style_changed (); /* EMIT SIGNAL */
196         }
197 }
198
199 void
200 AutomationList::start_touch ()
201 {
202         _touching = true;
203         _new_touch = true;
204 }
205
206 void
207 AutomationList::stop_touch ()
208 {
209         _touching = false;
210         _new_touch = false;
211 }
212
213 void
214 AutomationList::clear ()
215 {
216         {
217                 Glib::Mutex::Lock lm (lock);
218                 events.clear ();
219                 mark_dirty ();
220         }
221
222         maybe_signal_changed ();
223 }
224
225 void
226 AutomationList::x_scale (double factor)
227 {
228         Glib::Mutex::Lock lm (lock);
229         _x_scale (factor);
230 }
231
232 bool
233 AutomationList::extend_to (double when)
234 {
235         Glib::Mutex::Lock lm (lock);
236         if (events.empty() || events.back()->when == when) {
237                 return false;
238         }
239         double factor = when / events.back()->when;
240         _x_scale (factor);
241         return true;
242 }
243
244 void AutomationList::_x_scale (double factor)
245 {
246         for (AutomationList::iterator i = events.begin(); i != events.end(); ++i) {
247                 (*i)->when = floor ((*i)->when * factor);
248         }
249
250         mark_dirty ();
251 }
252
253 void
254 AutomationList::reposition_for_rt_add (double when)
255 {
256         rt_insertion_point = events.end();
257 }
258
259 #define last_rt_insertion_point rt_insertion_point
260
261 void
262 AutomationList::rt_add (double when, double value)
263 {
264         /* this is for automation recording */
265
266         if ((_state & Touch) && !_touching) {
267                 return;
268         }
269
270         // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
271
272         {
273                 Glib::Mutex::Lock lm (lock);
274
275                 iterator where;
276                 TimeComparator cmp;
277                 ControlEvent cp (when, 0.0);
278                 bool done = false;
279
280                 if ((last_rt_insertion_point != events.end()) && ((*last_rt_insertion_point)->when < when) ) {
281
282                         /* we have a previous insertion point, so we should delete
283                            everything between it and the position where we are going
284                            to insert this point.
285                         */
286
287                         iterator after = last_rt_insertion_point;
288
289                         if (++after != events.end()) {
290                                 iterator far = after;
291
292                                 while (far != events.end()) {
293                                         if ((*far)->when > when) {
294                                                 break;
295                                         }
296                                         ++far;
297                                 }
298
299                                 if(_new_touch) {
300                                         where = far;
301                                         last_rt_insertion_point = where;
302                                                                                              
303                                         if((*where)->when == when) {
304                                                 (*where)->value = value;
305                                                 done = true;
306                                         }
307                                 } else {
308                                         where = events.erase (after, far);
309                                 }
310
311                         } else {
312
313                                 where = after;
314
315                         }
316                         
317                         iterator previous = last_rt_insertion_point;
318                         --previous;
319                         
320                         if (last_rt_insertion_point != events.begin() && (*last_rt_insertion_point)->value == value && (*previous)->value == value) {
321                                 (*last_rt_insertion_point)->when = when;
322                                 done = true;
323                                 
324                         }
325                         
326                 } else {
327
328                         where = lower_bound (events.begin(), events.end(), &cp, cmp);
329
330                         if (where != events.end()) {
331                                 if ((*where)->when == when) {
332                                         (*where)->value = value;
333                                         done = true;
334                                 }
335                         }
336                 }
337                 
338                 if (!done) {
339                         last_rt_insertion_point = events.insert (where, point_factory (when, value));
340                 }
341                 
342                 _new_touch = false;
343                 mark_dirty ();
344         }
345
346         maybe_signal_changed ();
347 }
348
349 #undef last_rt_insertion_point
350
351 void
352 AutomationList::add (double when, double value)
353 {
354         /* this is for graphical editing and loading data from storage */
355
356         {
357                 Glib::Mutex::Lock lm (lock);
358                 TimeComparator cmp;
359                 ControlEvent cp (when, 0.0f);
360                 bool insert = true;
361                 iterator insertion_point;
362
363                 for (insertion_point = lower_bound (events.begin(), events.end(), &cp, cmp); insertion_point != events.end(); ++insertion_point) {
364
365                         /* only one point allowed per time point */
366
367                         if ((*insertion_point)->when == when) {
368                                 (*insertion_point)->value = value;
369                                 insert = false;
370                                 break;
371                         } 
372
373                         if ((*insertion_point)->when >= when) {
374                                 break;
375                         }
376                 }
377
378                 if (insert) {
379
380                         events.insert (insertion_point, point_factory (when, value));
381                         reposition_for_rt_add (0);
382
383                 } 
384
385                 mark_dirty ();
386         }
387
388         maybe_signal_changed ();
389 }
390
391 void
392 AutomationList::erase (AutomationList::iterator i)
393 {
394         {
395                 Glib::Mutex::Lock lm (lock);
396                 events.erase (i);
397                 reposition_for_rt_add (0);
398                 if (!no_state) {
399 #ifdef STATE_MANAGER
400                         save_state (_("removed event"));
401 #endif
402                 }
403                 mark_dirty ();
404         }
405         maybe_signal_changed ();
406 }
407
408 void
409 AutomationList::erase (AutomationList::iterator start, AutomationList::iterator end)
410 {
411         {
412                 Glib::Mutex::Lock lm (lock);
413                 events.erase (start, end);
414                 reposition_for_rt_add (0);
415                 mark_dirty ();
416         }
417         maybe_signal_changed ();
418 }       
419
420 void
421 AutomationList::reset_range (double start, double endt)
422 {
423         bool reset = false;
424
425         {
426         Glib::Mutex::Lock lm (lock);
427                 TimeComparator cmp;
428                 ControlEvent cp (start, 0.0f);
429                 iterator s;
430                 iterator e;
431                 
432                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
433
434                         cp.when = endt;
435                         e = upper_bound (events.begin(), events.end(), &cp, cmp);
436
437                         for (iterator i = s; i != e; ++i) {
438                                 (*i)->value = default_value;
439                         }
440                         
441                         reset = true;
442
443                         mark_dirty ();
444                 }
445         }
446
447         if (reset) {
448                 maybe_signal_changed ();
449         }
450 }
451
452 void
453 AutomationList::erase_range (double start, double endt)
454 {
455         bool erased = false;
456
457         {
458                 Glib::Mutex::Lock lm (lock);
459                 TimeComparator cmp;
460                 ControlEvent cp (start, 0.0f);
461                 iterator s;
462                 iterator e;
463
464                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) != events.end()) {
465                         cp.when = endt;
466                         e = upper_bound (events.begin(), events.end(), &cp, cmp);
467                         events.erase (s, e);
468                         reposition_for_rt_add (0);
469                         erased = true;
470                         mark_dirty ();
471                 }
472                 
473         }
474
475         if (erased) {
476                 maybe_signal_changed ();
477         }
478 }
479
480 void
481 AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
482 {
483         /* note: we assume higher level logic is in place to avoid this
484            reordering the time-order of control events in the list. ie. all
485            points after end are later than (end)->when.
486         */
487
488         {
489                 Glib::Mutex::Lock lm (lock);
490
491                 while (start != end) {
492                         (*start)->when += xdelta;
493                         (*start)->value += ydelta;
494                         ++start;
495                 }
496
497                 mark_dirty ();
498         }
499
500         maybe_signal_changed ();
501 }
502
503 void
504 AutomationList::modify (iterator iter, double when, double val)
505 {
506         /* note: we assume higher level logic is in place to avoid this
507            reordering the time-order of control events in the list. ie. all
508            points after *iter are later than when.
509         */
510
511         {
512                 Glib::Mutex::Lock lm (lock);
513                 (*iter)->when = when;
514                 (*iter)->value = val;
515                 mark_dirty ();
516         }
517         
518         maybe_signal_changed ();
519 }
520
521 std::pair<AutomationList::iterator,AutomationList::iterator>
522 AutomationList::control_points_adjacent (double xval)
523 {
524         Glib::Mutex::Lock lm (lock);
525         iterator i;
526         TimeComparator cmp;
527         ControlEvent cp (xval, 0.0f);
528         std::pair<iterator,iterator> ret;
529
530         ret.first = events.end();
531         ret.second = events.end();
532
533         for (i = lower_bound (events.begin(), events.end(), &cp, cmp); i != events.end(); ++i) {
534                 
535                 if (ret.first == events.end()) {
536                         if ((*i)->when >= xval) {
537                                 if (i != events.begin()) {
538                                         ret.first = i;
539                                         --ret.first;
540                                 } else {
541                                         return ret;
542                                 }
543                         }
544                 } 
545                 
546                 if ((*i)->when > xval) {
547                         ret.second = i;
548                         break;
549                 }
550         }
551
552         return ret;
553 }
554
555 void
556 AutomationList::freeze ()
557 {
558         _frozen = true;
559 }
560
561 void
562 AutomationList::thaw ()
563 {
564         _frozen = false;
565         if (changed_when_thawed) {
566                  StateChanged(Change(0)); /* EMIT SIGNAL */
567         }
568 }
569
570 void
571 AutomationList::set_max_xval (double x)
572 {
573         max_xval = x;
574 }
575
576 void 
577 AutomationList::mark_dirty ()
578 {
579         lookup_cache.left = -1;
580         _dirty = true;
581 }
582
583 void
584 AutomationList::truncate_end (double last_coordinate)
585 {
586         {
587                 Glib::Mutex::Lock lm (lock);
588                 ControlEvent cp (last_coordinate, 0);
589                 list<ControlEvent*>::reverse_iterator i;
590                 double last_val;
591
592                 if (events.empty()) {
593                         fatal << _("programming error:")
594                               << "AutomationList::truncate_end() called on an empty list"
595                               << endmsg;
596                         /*NOTREACHED*/
597                         return;
598                 }
599
600                 if (last_coordinate == events.back()->when) {
601                         return;
602                 }
603
604                 if (last_coordinate > events.back()->when) {
605                         
606                         /* extending end:
607                         */
608
609                         iterator foo = events.begin();
610                         bool lessthantwo;
611
612                         if (foo == events.end()) {
613                                 lessthantwo = true;
614                         } else if (++foo == events.end()) {
615                                 lessthantwo = true;
616                         } else {
617                                 lessthantwo = false;
618                         }
619
620                         if (lessthantwo) {
621                                 /* less than 2 points: add a new point */
622                                 events.push_back (point_factory (last_coordinate, events.back()->value));
623                         } else {
624
625                                 /* more than 2 points: check to see if the last 2 values
626                                    are equal. if so, just move the position of the
627                                    last point. otherwise, add a new point.
628                                 */
629
630                                 iterator penultimate = events.end();
631                                 --penultimate; /* points at last point */
632                                 --penultimate; /* points at the penultimate point */
633                                 
634                                 if (events.back()->value == (*penultimate)->value) {
635                                         events.back()->when = last_coordinate;
636                                 } else {
637                                         events.push_back (point_factory (last_coordinate, events.back()->value));
638                                 }
639                         }
640
641                 } else {
642
643                         /* shortening end */
644
645                         last_val = unlocked_eval (last_coordinate);
646                         last_val = max ((double) min_yval, last_val);
647                         last_val = min ((double) max_yval, last_val);
648                         
649                         i = events.rbegin();
650                         
651                         /* make i point to the last control point */
652                         
653                         ++i;
654                         
655                         /* now go backwards, removing control points that are
656                            beyond the new last coordinate.
657                         */
658
659                         uint32_t sz = events.size();
660                         
661                         while (i != events.rend() && sz > 2) {
662                                 list<ControlEvent*>::reverse_iterator tmp;
663                                 
664                                 tmp = i;
665                                 ++tmp;
666                                 
667                                 if ((*i)->when < last_coordinate) {
668                                         break;
669                                 }
670                                 
671                                 events.erase (i.base());
672                                 --sz;
673
674                                 i = tmp;
675                         }
676                         
677                         events.back()->when = last_coordinate;
678                         events.back()->value = last_val;
679                 }
680
681                 reposition_for_rt_add (0);
682                 mark_dirty();
683         }
684
685         maybe_signal_changed ();
686 }
687
688 void
689 AutomationList::truncate_start (double overall_length)
690 {
691         {
692                 Glib::Mutex::Lock lm (lock);
693                 AutomationList::iterator i;
694                 double first_legal_value;
695                 double first_legal_coordinate;
696
697                 if (events.empty()) {
698                         fatal << _("programming error:")
699                               << "AutomationList::truncate_start() called on an empty list"
700                               << endmsg;
701                         /*NOTREACHED*/
702                         return;
703                 }
704                 
705                 if (overall_length == events.back()->when) {
706                         /* no change in overall length */
707                         return;
708                 }
709                 
710                 if (overall_length > events.back()->when) {
711                         
712                         /* growing at front: duplicate first point. shift all others */
713
714                         double shift = overall_length - events.back()->when;
715                         uint32_t np;
716
717                         for (np = 0, i = events.begin(); i != events.end(); ++i, ++np) {
718                                 (*i)->when += shift;
719                         }
720
721                         if (np < 2) {
722
723                                 /* less than 2 points: add a new point */
724                                 events.push_front (point_factory (0, events.front()->value));
725
726                         } else {
727
728                                 /* more than 2 points: check to see if the first 2 values
729                                    are equal. if so, just move the position of the
730                                    first point. otherwise, add a new point.
731                                 */
732
733                                 iterator second = events.begin();
734                                 ++second; /* points at the second point */
735                                 
736                                 if (events.front()->value == (*second)->value) {
737                                         /* first segment is flat, just move start point back to zero */
738                                         events.front()->when = 0;
739                                 } else {
740                                         /* leave non-flat segment in place, add a new leading point. */
741                                         events.push_front (point_factory (0, events.front()->value));
742                                 }
743                         }
744
745                 } else {
746
747                         /* shrinking at front */
748                         
749                         first_legal_coordinate = events.back()->when - overall_length;
750                         first_legal_value = unlocked_eval (first_legal_coordinate);
751                         first_legal_value = max (min_yval, first_legal_value);
752                         first_legal_value = min (max_yval, first_legal_value);
753
754                         /* remove all events earlier than the new "front" */
755
756                         i = events.begin();
757                         
758                         while (i != events.end() && !events.empty()) {
759                                 list<ControlEvent*>::iterator tmp;
760                                 
761                                 tmp = i;
762                                 ++tmp;
763                                 
764                                 if ((*i)->when > first_legal_coordinate) {
765                                         break;
766                                 }
767                                 
768                                 events.erase (i);
769                                 
770                                 i = tmp;
771                         }
772                         
773
774                         /* shift all remaining points left to keep their same
775                            relative position
776                         */
777                         
778                         for (i = events.begin(); i != events.end(); ++i) {
779                                 (*i)->when -= first_legal_coordinate;
780                         }
781
782                         /* add a new point for the interpolated new value */
783                         
784                         events.push_front (point_factory (0, first_legal_value));
785                 }           
786
787                 reposition_for_rt_add (0);
788
789                 mark_dirty();
790         }
791
792         maybe_signal_changed ();
793 }
794
795 double
796 AutomationList::unlocked_eval (double x)
797 {
798         return shared_eval (x);
799 }
800
801 double
802 AutomationList::shared_eval (double x) 
803 {
804         pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
805         int32_t npoints;
806         double lpos, upos;
807         double lval, uval;
808         double fraction;
809
810         npoints = events.size();
811
812         switch (npoints) {
813         case 0:
814                 return default_value;
815
816         case 1:
817                 if (x >= events.front()->when) {
818                         return events.front()->value;
819                 } else {
820                         // return default_value;
821                         return events.front()->value;
822                 } 
823                 
824         case 2:
825                 if (x >= events.back()->when) {
826                         return events.back()->value;
827                 } else if (x == events.front()->when) {
828                         return events.front()->value;
829                 } else if (x < events.front()->when) {
830                         // return default_value;
831                         return events.front()->value;
832                 }
833
834                 lpos = events.front()->when;
835                 lval = events.front()->value;
836                 upos = events.back()->when;
837                 uval = events.back()->value;
838                 
839                 /* linear interpolation betweeen the two points
840                 */
841
842                 fraction = (double) (x - lpos) / (double) (upos - lpos);
843                 return lval + (fraction * (uval - lval));
844
845         default:
846
847                 if (x >= events.back()->when) {
848                         return events.back()->value;
849                 } else if (x == events.front()->when) {
850                         return events.front()->value;
851                 } else if (x < events.front()->when) {
852                         // return default_value;
853                         return events.front()->value;
854                 }
855
856                 return multipoint_eval (x);
857                 break;
858         }
859 }
860
861 double
862 AutomationList::multipoint_eval (double x) 
863 {
864         pair<AutomationList::iterator,AutomationList::iterator> range;
865         double upos, lpos;
866         double uval, lval;
867         double fraction;
868
869         /* only do the range lookup if x is in a different range than last time
870            this was called (or if the lookup cache has been marked "dirty" (left<0)
871         */
872
873         if ((lookup_cache.left < 0) ||
874             ((lookup_cache.left > x) || 
875              (lookup_cache.range.first == events.end()) || 
876              ((*lookup_cache.range.second)->when < x))) {
877
878                 ControlEvent cp (x, 0);
879                 TimeComparator cmp;
880                 
881                 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
882         }
883         
884         range = lookup_cache.range;
885
886         if (range.first == range.second) {
887
888                 /* x does not exist within the list as a control point */
889
890                 lookup_cache.left = x;
891
892                 if (range.first != events.begin()) {
893                         --range.first;
894                         lpos = (*range.first)->when;
895                         lval = (*range.first)->value;
896                 }  else {
897                         /* we're before the first point */
898                         // return default_value;
899                         return events.front()->value;
900                 }
901                 
902                 if (range.second == events.end()) {
903                         /* we're after the last point */
904                         return events.back()->value;
905                 }
906
907                 upos = (*range.second)->when;
908                 uval = (*range.second)->value;
909                 
910                 /* linear interpolation betweeen the two points
911                    on either side of x
912                 */
913
914                 fraction = (double) (x - lpos) / (double) (upos - lpos);
915                 return lval + (fraction * (uval - lval));
916
917         } 
918
919         /* x is a control point in the data */
920         lookup_cache.left = -1;
921         return (*range.first)->value;
922 }
923
924 AutomationList*
925 AutomationList::cut (iterator start, iterator end)
926 {
927         AutomationList* nal = new AutomationList (default_value);
928
929         {
930                 Glib::Mutex::Lock lm (lock);
931
932                 for (iterator x = start; x != end; ) {
933                         iterator tmp;
934                         
935                         tmp = x;
936                         ++tmp;
937                         
938                         nal->events.push_back (point_factory (**x));
939                         events.erase (x);
940                         
941                         reposition_for_rt_add (0);
942
943                         x = tmp;
944                 }
945
946                 mark_dirty ();
947         }
948
949         maybe_signal_changed ();
950
951         return nal;
952 }
953
954 AutomationList*
955 AutomationList::cut_copy_clear (double start, double end, int op)
956 {
957         AutomationList* nal = new AutomationList (default_value);
958         iterator s, e;
959         ControlEvent cp (start, 0.0);
960         TimeComparator cmp;
961         bool changed = false;
962         
963         {
964                 Glib::Mutex::Lock lm (lock);
965
966                 if ((s = lower_bound (events.begin(), events.end(), &cp, cmp)) == events.end()) {
967                         return nal;
968                 }
969
970                 cp.when = end;
971                 e = upper_bound (events.begin(), events.end(), &cp, cmp);
972
973                 if (op != 2 && (*s)->when != start) {
974                         nal->events.push_back (point_factory (0, unlocked_eval (start)));
975                 }
976
977                 for (iterator x = s; x != e; ) {
978                         iterator tmp;
979                         
980                         tmp = x;
981                         ++tmp;
982
983                         changed = true;
984                         
985                         /* adjust new points to be relative to start, which
986                            has been set to zero.
987                         */
988                         
989                         if (op != 2) {
990                                 nal->events.push_back (point_factory ((*x)->when - start, (*x)->value));
991                         }
992
993                         if (op != 1) {
994                                 events.erase (x);
995                         }
996                         
997                         x = tmp;
998                 }
999
1000                 if (op != 2 && nal->events.back()->when != end - start) {
1001                         nal->events.push_back (point_factory (end - start, unlocked_eval (end)));
1002                 }
1003
1004                 if (changed) {
1005                         reposition_for_rt_add (0);
1006                 }
1007
1008                 mark_dirty ();
1009         }
1010
1011         maybe_signal_changed ();
1012
1013         return nal;
1014
1015 }
1016
1017 AutomationList*
1018 AutomationList::copy (iterator start, iterator end)
1019 {
1020         AutomationList* nal = new AutomationList (default_value);
1021
1022         {
1023                 Glib::Mutex::Lock lm (lock);
1024                 
1025                 for (iterator x = start; x != end; ) {
1026                         iterator tmp;
1027                         
1028                         tmp = x;
1029                         ++tmp;
1030                         
1031                         nal->events.push_back (point_factory (**x));
1032                         
1033                         x = tmp;
1034                 }
1035         }
1036
1037         return nal;
1038 }
1039
1040 AutomationList*
1041 AutomationList::cut (double start, double end)
1042 {
1043         return cut_copy_clear (start, end, 0);
1044 }
1045
1046 AutomationList*
1047 AutomationList::copy (double start, double end)
1048 {
1049         return cut_copy_clear (start, end, 1);
1050 }
1051
1052 void
1053 AutomationList::clear (double start, double end)
1054 {
1055         (void) cut_copy_clear (start, end, 2);
1056 }
1057
1058 bool
1059 AutomationList::paste (AutomationList& alist, double pos, float times)
1060 {
1061         if (alist.events.empty()) {
1062                 return false;
1063         }
1064
1065         {
1066                 Glib::Mutex::Lock lm (lock);
1067                 iterator where;
1068                 iterator prev;
1069                 double end = 0;
1070                 ControlEvent cp (pos, 0.0);
1071                 TimeComparator cmp;
1072
1073                 where = upper_bound (events.begin(), events.end(), &cp, cmp);
1074
1075                 for (iterator i = alist.begin();i != alist.end(); ++i) {
1076                         events.insert (where, point_factory( (*i)->when+pos,( *i)->value));
1077                         end = (*i)->when + pos;
1078                 }
1079         
1080         
1081                 /* move all  points after the insertion along the timeline by 
1082                    the correct amount.
1083                 */
1084
1085                 while (where != events.end()) {
1086                         iterator tmp;
1087                         if ((*where)->when <= end) {
1088                                 tmp = where;
1089                                 ++tmp;
1090                                 events.erase(where);
1091                                 where = tmp;
1092
1093                         } else {
1094                                 break;
1095                         }
1096                 }
1097
1098                 reposition_for_rt_add (0);
1099                 mark_dirty ();
1100         }
1101
1102         maybe_signal_changed ();
1103         return true;
1104 }
1105
1106 ControlEvent*
1107 AutomationList::point_factory (double when, double val) const
1108 {
1109         return new ControlEvent (when, val);
1110 }
1111
1112 ControlEvent*
1113 AutomationList::point_factory (const ControlEvent& other) const
1114 {
1115         return new ControlEvent (other);
1116 }
1117
1118 XMLNode&
1119 AutomationList::get_state ()
1120 {
1121         stringstream str;
1122         XMLNode* node = new XMLNode (X_("events"));
1123         iterator xx;
1124
1125         if (events.empty()) {
1126                 return *node;
1127         }
1128
1129         for (xx = events.begin(); xx != events.end(); ++xx) {
1130                 str << (double) (*xx)->when;
1131                 str << ' ';
1132                 str <<(double) (*xx)->value;
1133                 str << '\n';
1134         }
1135
1136         node->add_content (str.str());
1137
1138         return *node;
1139 }
1140
1141 int
1142 AutomationList::set_state (const XMLNode& node)
1143 {
1144         const XMLNodeList& elist = node.children();
1145         XMLNodeConstIterator i;
1146         XMLProperty* prop;
1147
1148         freeze ();
1149
1150         clear ();
1151         
1152         for (i = elist.begin(); i != elist.end(); ++i) {
1153
1154                 if ((*i)->name() == X_("events")) {
1155
1156                         /* new style */
1157
1158                         if (!node.content().empty()) {
1159                                 
1160                                 stringstream str (node.content());
1161                                 
1162                                 double x;
1163                                 double y;
1164                                 bool ok = true;
1165                                 
1166                                 
1167                                 while (str) {
1168                                         str >> x;
1169                                         if (!str) {
1170                                                 ok = false;
1171                                                 break;
1172                                         }
1173                                         str >> y;
1174                                         if (!str) {
1175                                                 ok = false;
1176                                                 break;
1177                                         }
1178                                         add (x, y);
1179                                 }
1180                                 
1181                                 if (!ok) {
1182                                         clear ();
1183                                         error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
1184                                 }
1185                         }
1186
1187                 } else {
1188
1189                         /* old style */
1190
1191                         nframes_t x;
1192                         double y;
1193
1194                         if ((prop = (*i)->property ("x")) == 0) {
1195                                 error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
1196                                 continue;
1197                         }
1198                         x = atoi (prop->value().c_str());
1199                         
1200                         if ((prop = (*i)->property ("y")) == 0) {
1201                                 error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
1202                                 continue;
1203                         }
1204                         y = atof (prop->value().c_str());
1205                         
1206                         add (x, y);
1207                 }
1208         }
1209
1210         thaw ();
1211         return 0;
1212 }
1213