Generic MIDI control now saves+restores its state; PBD::ID now requires a buffer...
[ardour.git] / libs / ardour / playlist.cc
1 /*
2     Copyright (C) 2000-2003 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 <fstream>
23 #include <algorithm>
24 #include <unistd.h>
25 #include <cerrno>
26 #include <string>
27 #include <climits>
28
29 #include <sigc++/bind.h>
30
31 #include <pbd/failed_constructor.h>
32 #include <pbd/stl_delete.h>
33 #include <pbd/xml++.h>
34
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/region_factory.h>
39
40 #include "i18n.h"
41
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace PBD;
45
46 sigc::signal<void,Playlist*> Playlist::PlaylistCreated;
47
48 struct ShowMeTheList {
49     ShowMeTheList (Playlist *pl, const string& n) : playlist (pl), name (n) {}
50     ~ShowMeTheList () { 
51             cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl; 
52     };
53     Playlist *playlist;
54     string name;
55 };
56
57 struct RegionSortByLayer {
58     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
59             return a->layer() < b->layer();
60     }
61 };
62
63 struct RegionSortByPosition {
64     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
65             return a->position() < b->position();
66     }
67 };
68
69 struct RegionSortByLastLayerOp {
70     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
71             return a->last_layer_op() < b->last_layer_op();
72     }
73 };
74
75 Playlist::Playlist (Session& sess, string nom, bool hide)
76         : _session (sess)
77 {
78         init (hide);
79         _name = nom;
80         
81 }
82
83 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
84         : _session (sess)
85 {
86         init (hide);
87         _name = "unnamed"; /* reset by set_state */
88         
89         if (set_state (node)) {
90                 throw failed_constructor();
91         }
92 }
93
94 Playlist::Playlist (const Playlist& other, string namestr, bool hide)
95         : _name (namestr), _session (other._session), _orig_diskstream_id(other._orig_diskstream_id)
96 {
97         init (hide);
98
99         RegionList tmp;
100         other.copy_regions (tmp);
101         
102         in_set_state = true;
103
104         for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
105                 add_region_internal( (*x), (*x)->position() );
106         }
107
108         in_set_state = false;
109
110         _splicing  = other._splicing;
111         _nudging   = other._nudging;
112         _edit_mode = other._edit_mode;
113
114         in_set_state = false;
115         in_flush = false;
116         in_partition = false;
117         subcnt = 0;
118         _read_data_count = 0;
119         _frozen = other._frozen;
120         save_on_thaw = false;
121         
122         layer_op_counter = other.layer_op_counter;
123         freeze_length = other.freeze_length;
124         
125 }
126
127 Playlist::Playlist (const Playlist& other, nframes_t start, nframes_t cnt, string str, bool hide)
128         : _name (str), _session (other._session), _orig_diskstream_id(other._orig_diskstream_id)
129 {
130         RegionLock rlock2 (&((Playlist&)other));
131         
132         nframes_t end = start + cnt - 1;
133
134         init (hide);
135
136         for (RegionList::const_iterator i = other.regions.begin(); i != other.regions.end(); i++) {
137
138                 boost::shared_ptr<Region> region;
139                 boost::shared_ptr<Region> new_region;
140                 nframes_t offset = 0;
141                 nframes_t position = 0;
142                 nframes_t len = 0;
143                 string    new_name;
144                 OverlapType overlap;
145
146                 region = *i;
147
148                 overlap = region->coverage (start, end);
149
150                 switch (overlap) {
151                 case OverlapNone:
152                         continue;
153
154                 case OverlapInternal:
155                         offset = start - region->position();
156                         position = 0;
157                         len = cnt;
158                         break;
159
160                 case OverlapStart:
161                         offset = 0;
162                         position = region->position() - start;
163                         len = end - region->position();
164                         break;
165
166                 case OverlapEnd:
167                         offset = start - region->position();
168                         position = 0;
169                         len = region->length() - offset;
170                         break;
171
172                 case OverlapExternal:
173                         offset = 0;
174                         position = region->position() - start;
175                         len = region->length();
176                         break;
177                 }
178
179                 _session.region_name (new_name, region->name(), false);
180
181                 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
182
183                 add_region_internal (new_region, position, true);
184         }
185         
186         /* this constructor does NOT notify others (session) */
187 }
188
189 void
190 Playlist::ref ()
191 {
192         ++_refcnt;
193         InUse (this, true); /* EMIT SIGNAL */
194 }
195
196 void
197 Playlist::unref ()
198 {
199         if (_refcnt > 0) {
200                 _refcnt--; 
201         }
202         if (_refcnt == 0) {
203                 InUse (this, false); /* EMIT SIGNAL */
204                 
205                 if (_hidden) {
206                         /* nobody knows we exist */
207                         delete this;
208                 }
209         }
210 }
211
212
213 void
214 Playlist::copy_regions (RegionList& newlist) const
215 {
216         RegionLock rlock (const_cast<Playlist *> (this));
217
218         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
219                 newlist.push_back (RegionFactory::RegionFactory::create (*i));
220         }
221 }
222
223 void
224 Playlist::init (bool hide)
225 {
226         g_atomic_int_set (&block_notifications, 0);
227         g_atomic_int_set (&ignore_state_changes, 0);
228         pending_modified = false;
229         pending_length = false;
230         _refcnt = 0;
231         _hidden = hide;
232         _splicing = false;
233         _nudging = false;
234         in_set_state = false;
235         _edit_mode = Config->get_edit_mode();
236         in_flush = false;
237         in_partition = false;
238         subcnt = 0;
239         _read_data_count = 0;
240         _frozen = false;
241         save_on_thaw = false;
242         layer_op_counter = 0;
243         freeze_length = 0;
244
245         Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
246 }
247
248 Playlist::Playlist (const Playlist& pl)
249         : _session (pl._session)
250 {
251         fatal << _("playlist const copy constructor called") << endmsg;
252 }
253
254 Playlist::Playlist (Playlist& pl)
255         : _session (pl._session)
256 {
257         fatal << _("playlist non-const copy constructor called") << endmsg;
258 }
259
260 Playlist::~Playlist ()
261 {
262         /* GoingAway must be emitted by derived classes */
263 }
264
265 void
266 Playlist::set_name (const string& str)
267 {
268         /* in a typical situation, a playlist is being used
269            by one diskstream and also is referenced by the
270            Session. if there are more references than that,
271            then don't change the name.
272         */
273
274         if (_refcnt > 2) {
275                 return;
276         }
277
278         _name = str; 
279         NameChanged(); /* EMIT SIGNAL */
280 }
281
282 /***********************************************************************
283  CHANGE NOTIFICATION HANDLING
284  
285  Notifications must be delayed till the region_lock is released. This
286  is necessary because handlers for the signals may need to acquire
287  the lock (e.g. to read from the playlist).
288  ***********************************************************************/
289
290 void
291 Playlist::freeze ()
292 {
293         delay_notifications ();
294         g_atomic_int_inc (&ignore_state_changes);
295 }
296
297 void
298 Playlist::thaw ()
299 {
300         g_atomic_int_dec_and_test (&ignore_state_changes);
301         release_notifications ();
302 }
303
304
305 void
306 Playlist::delay_notifications ()
307 {
308         g_atomic_int_inc (&block_notifications);
309         freeze_length = _get_maximum_extent();
310 }
311
312 void
313 Playlist::release_notifications ()
314 {
315         if (g_atomic_int_dec_and_test (&block_notifications)) { 
316                 flush_notifications ();
317         } 
318 }
319
320
321 void
322 Playlist::notify_modified ()
323 {
324         if (holding_state ()) {
325                 pending_modified = true;
326         } else {
327                 pending_modified = false;
328                 Modified(); /* EMIT SIGNAL */
329         }
330 }
331
332 void
333 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
334 {
335         if (holding_state ()) {
336                 pending_removals.insert (pending_removals.end(), r);
337         } else {
338                 RegionRemoved (r); /* EMIT SIGNAL */
339                 /* this might not be true, but we have to act
340                    as though it could be.
341                 */
342                 LengthChanged (); /* EMIT SIGNAL */
343                 Modified (); /* EMIT SIGNAL */
344         }
345 }
346
347 void
348 Playlist::notify_region_added (boost::shared_ptr<Region> r)
349 {
350         if (holding_state()) {
351                 pending_adds.insert (pending_adds.end(), r);
352         } else {
353                 RegionAdded (r); /* EMIT SIGNAL */
354                 /* this might not be true, but we have to act
355                    as though it could be.
356                 */
357                 LengthChanged (); /* EMIT SIGNAL */
358                 Modified (); /* EMIT SIGNAL */
359         }
360 }
361
362 void
363 Playlist::notify_length_changed ()
364 {
365         if (holding_state ()) {
366                 pending_length = true;
367         } else {
368                 LengthChanged(); /* EMIT SIGNAL */
369                 Modified (); /* EMIT SIGNAL */
370         }
371 }
372
373 void
374 Playlist::flush_notifications ()
375 {
376         RegionList::iterator r;
377         RegionList::iterator a;
378         set<boost::shared_ptr<Region> > dependent_checks_needed;
379         uint32_t n = 0;
380
381         if (in_flush) {
382                 return;
383         }
384
385         in_flush = true;
386
387         /* we have no idea what order the regions ended up in pending
388            bounds (it could be based on selection order, for example).
389            so, to preserve layering in the "most recently moved is higher" 
390            model, sort them by existing layer, then timestamp them.
391         */
392
393         // RegionSortByLayer cmp;
394         // pending_bounds.sort (cmp);
395
396         for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
397                 if (Config->get_layer_model() == MoveAddHigher) {
398                         timestamp_layer_op (*r);
399                 }
400                 pending_length = true;
401                 n++;
402         }
403
404         for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
405                 dependent_checks_needed.insert (*r);
406                 /* don't increment n again - its the same list */
407         }
408
409         for (a = pending_adds.begin(); a != pending_adds.end(); ++a) {
410                 dependent_checks_needed.insert (*a);
411                 RegionAdded (*a); /* EMIT SIGNAL */
412                 n++;
413         }
414
415         for (set<boost::shared_ptr<Region> >::iterator x = dependent_checks_needed.begin(); x != dependent_checks_needed.end(); ++x) {
416                 check_dependents (*x, false);
417         }
418
419         for (r = pending_removals.begin(); r != pending_removals.end(); ++r) {
420                 remove_dependents (*r);
421                 RegionRemoved (*r); /* EMIT SIGNAL */
422                 n++;
423         }
424
425         if ((freeze_length != _get_maximum_extent()) || pending_length) {
426                 pending_length = 0;
427                 LengthChanged(); /* EMIT SIGNAL */
428                 n++;
429         }
430
431         if (n || pending_modified) {
432                 if (!in_set_state) {
433                         possibly_splice ();
434                         relayer ();
435                 }
436                 pending_modified = false;
437                 Modified (); /* EMIT SIGNAL */
438         }
439
440         pending_adds.clear ();
441         pending_removals.clear ();
442         pending_bounds.clear ();
443
444         if (save_on_thaw) {
445                 save_on_thaw = false;
446                 save_state (last_save_reason);
447         }
448         
449         in_flush = false;
450 }
451
452 /*************************************************************
453   PLAYLIST OPERATIONS
454  *************************************************************/
455
456 void
457 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool with_save) 
458
459         RegionLock rlock (this);
460         
461         times = fabs (times);
462         
463         int itimes = (int) floor (times);
464
465         nframes_t pos = position;
466         
467         if (itimes >= 1) {
468                 add_region_internal (region, pos, true);
469                 pos += region->length();
470                 --itimes;
471         }
472         
473         /* later regions will all be spliced anyway */
474         
475         if (!holding_state ()) {
476                 possibly_splice_unlocked ();
477         }
478
479         /* note that itimes can be zero if we being asked to just
480            insert a single fraction of the region.
481         */
482
483         for (int i = 0; i < itimes; ++i) {
484                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
485                 add_region_internal (copy, pos, true);
486                 pos += region->length();
487         }
488         
489         if (floor (times) != times) {
490                 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
491                 string name;
492                 _session.region_name (name, region->name(), false);
493                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
494                 add_region_internal (sub, pos, true);
495         }
496         
497         if (with_save) {
498                 maybe_save_state (_("add region"));
499         }
500 }
501
502 void
503 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position, bool delay_sort)
504 {
505         RegionSortByPosition cmp;
506         nframes_t old_length = 0;
507
508         if (!holding_state()) {
509                  old_length = _get_maximum_extent();
510         }
511
512         region->set_playlist (this);
513         region->set_position (position, this);
514
515         timestamp_layer_op (region);
516
517         regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
518
519         if (!holding_state () && !in_set_state) {
520                 /* layers get assigned from XML state */
521                 relayer ();
522         }
523
524         /* we need to notify the existence of new region before checking dependents. Ick. */
525
526         notify_region_added (region);
527         
528         if (!holding_state ()) {
529                 check_dependents (region, false);
530                 if (old_length != _get_maximum_extent()) {
531                         notify_length_changed ();
532                 }
533         }
534
535         region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), region));
536 }
537
538 void
539 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
540 {
541         RegionLock rlock (this);
542
543         remove_region_internal (old);
544         add_region_internal (newr, pos);
545
546         if (!holding_state ()) {
547                 possibly_splice_unlocked ();
548         }
549
550         maybe_save_state (_("replace region"));
551 }
552
553 void
554 Playlist::remove_region (boost::shared_ptr<Region> region)
555 {
556         RegionLock rlock (this);
557         remove_region_internal (region);
558
559         if (!holding_state ()) {
560                 possibly_splice_unlocked ();
561         }
562
563         maybe_save_state (_("remove region"));
564 }
565
566 int
567 Playlist::remove_region_internal (boost::shared_ptr<Region>region, bool delay_sort)
568 {
569         RegionList::iterator i;
570         nframes_t old_length = 0;
571
572         // cerr << "removing region " << region->name() << endl;
573
574         if (!holding_state()) {
575                 old_length = _get_maximum_extent();
576         }
577
578         for (i = regions.begin(); i != regions.end(); ++i) {
579                 if (*i == region) {
580
581                         regions.erase (i);
582
583                         if (!holding_state ()) {
584                                 relayer ();
585                                 remove_dependents (region);
586                                 
587                                 if (old_length != _get_maximum_extent()) {
588                                         notify_length_changed ();
589                                 }
590                         }
591
592                         notify_region_removed (region);
593                         return 0;
594                 }
595         }
596         return -1;
597 }
598
599 void
600 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
601 {
602         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
603                 if (Config->get_use_overlap_equivalency()) {
604                         if ((*i)->overlap_equivalent (other)) {
605                                 results.push_back ((*i));
606                         } else if ((*i)->equivalent (other)) {
607                                 results.push_back ((*i));
608                         }
609                 }
610         }
611 }
612
613 void
614 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
615 {
616         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
617
618                 if ((*i) && (*i)->region_list_equivalent (other)) {
619                         results.push_back (*i);
620                 }
621         }
622 }
623
624 void
625 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
626 {
627         RegionList thawlist;
628
629         partition_internal (start, end, false, thawlist);
630
631         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
632                 (*i)->thaw ("separation");
633         }
634
635         maybe_save_state (_("separate"));
636 }
637
638 void
639 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
640 {
641         RegionLock rlock (this);
642         boost::shared_ptr<Region> region;
643         boost::shared_ptr<Region> current;
644         string new_name;
645         RegionList::iterator tmp;
646         OverlapType overlap;
647         nframes_t pos1, pos2, pos3, pos4;
648         RegionList new_regions;
649
650         in_partition = true;
651
652         /* need to work from a copy, because otherwise the regions we add during the process
653            get operated on as well.
654         */
655
656         RegionList copy = regions;
657
658         for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
659                 
660                 tmp = i;
661                 ++tmp;
662
663                 current = *i;
664                 
665                 if (current->first_frame() == start && current->last_frame() == end) {
666                         if (cutting) {
667                                 remove_region_internal (current);
668                         }
669                         continue;
670                 }
671                 
672                 if ((overlap = current->coverage (start, end)) == OverlapNone) {
673                         continue;
674                 }
675                 
676                 pos1 = current->position();
677                 pos2 = start;
678                 pos3 = end;
679                 pos4 = current->last_frame();
680
681                 if (overlap == OverlapInternal) {
682                         
683                         /* split: we need 3 new regions, the front, middle and end.
684                            cut:   we need 2 regions, the front and end.
685                         */
686                         
687                         /*
688                                          start                 end
689                           ---------------*************************------------
690                                          P1  P2              P3  P4
691                           SPLIT:
692                           ---------------*****++++++++++++++++====------------
693                           CUT
694                           ---------------*****----------------====------------
695                           
696                         */
697
698                         if (!cutting) {
699                                 
700                                 /* "middle" ++++++ */
701                                 
702                                 _session.region_name (new_name, current->name(), false);
703                                 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
704                                                        regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
705                                 add_region_internal (region, start, true);
706                                 new_regions.push_back (region);
707                         }
708                         
709                         /* "end" ====== */
710                         
711                         _session.region_name (new_name, current->name(), false);
712                         region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, 
713                                                regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
714
715                         add_region_internal (region, end, true);
716                         new_regions.push_back (region);
717
718                         /* "front" ***** */
719                                 
720                         current->freeze ();
721                         thawlist.push_back (current);
722                         current->trim_end (pos2, this);
723
724                 } else if (overlap == OverlapEnd) {
725
726                         /*
727                                                               start           end
728                                     ---------------*************************------------
729                                                    P1           P2         P4   P3
730                                     SPLIT:                                                 
731                                     ---------------**************+++++++++++------------
732                                     CUT:                                                   
733                                     ---------------**************-----------------------
734
735                         */
736
737                         if (!cutting) {
738                                 
739                                 /* end +++++ */
740                                 
741                                 _session.region_name (new_name, current->name(), false);
742                                 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
743                                                        Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
744                                 add_region_internal (region, start, true);
745                                 new_regions.push_back (region);
746                         }
747
748                         /* front ****** */
749
750                         current->freeze ();
751                         thawlist.push_back (current);
752                         current->trim_end (pos2, this);
753
754                 } else if (overlap == OverlapStart) {
755
756                         /* split: we need 2 regions: the front and the end.
757                            cut: just trim current to skip the cut area
758                         */
759                                 
760                         /*
761                                                         start           end
762                                     ---------------*************************------------
763                                        P2          P1 P3                   P4          
764
765                                     SPLIT:
766                                     ---------------****+++++++++++++++++++++------------
767                                     CUT:
768                                     -------------------*********************------------
769                                     
770                         */
771
772                         if (!cutting) {
773                                 
774                                 /* front **** */
775                                  _session.region_name (new_name, current->name(), false);
776                                  region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
777                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
778                                  add_region_internal (region, pos1, true);
779                                  new_regions.push_back (region);
780                         } 
781                         
782                         /* end */
783                         
784                         current->freeze ();
785                         thawlist.push_back (current);
786                         current->trim_front (pos3, this);
787
788                 } else if (overlap == OverlapExternal) {
789
790                         /* split: no split required.
791                            cut: remove the region.
792                         */
793                                 
794                         /*
795                                        start                                      end
796                                     ---------------*************************------------
797                                        P2          P1 P3                   P4          
798
799                                     SPLIT:
800                                     ---------------*************************------------
801                                     CUT:
802                                     ----------------------------------------------------
803                                     
804                         */
805
806                         if (cutting) {
807                                 remove_region_internal (current);
808                         }
809                         new_regions.push_back (current);
810                 }
811         }
812
813         in_partition = false;
814
815         for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
816                 check_dependents (*i, false);
817         }
818 }
819
820 Playlist*
821 Playlist::cut_copy (Playlist* (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
822 {
823         Playlist* ret;
824         Playlist* pl;
825         nframes_t start;
826
827         if (ranges.empty()) {
828                 return 0;
829         }
830
831         start = ranges.front().start;
832
833
834         for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
835
836                 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
837                 
838                 if (i == ranges.begin()) {
839                         ret = pl;
840                 } else {
841                         
842                         /* paste the next section into the nascent playlist,
843                            offset to reflect the start of the first range we
844                            chopped.
845                         */
846
847                         ret->paste (*pl, (*i).start - start, 1.0f);
848                         delete pl;
849                 }
850         }
851
852         if (ret) {
853                 /* manually notify session of new playlist here
854                    because the playlists were constructed without notifying 
855                 */
856                 PlaylistCreated (ret);
857         }
858         
859         return ret;
860 }
861
862 Playlist*
863 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
864 {
865         Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
866         return cut_copy (pmf, ranges, result_is_hidden);
867 }
868
869 Playlist*
870 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
871 {
872         Playlist* (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
873         return cut_copy (pmf, ranges, result_is_hidden);
874 }
875
876 Playlist *
877 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
878 {
879         Playlist *the_copy;
880         RegionList thawlist;
881         char buf[32];
882
883         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
884         string new_name = _name;
885         new_name += '.';
886         new_name += buf;
887
888         if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
889                 return 0;
890         }
891
892         partition_internal (start, start+cnt-1, true, thawlist);
893         possibly_splice ();
894
895         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
896                 (*i)->thaw ("playlist cut");
897         }
898
899         maybe_save_state (_("cut"));
900
901         return the_copy;
902 }
903
904 Playlist *
905 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
906 {
907         char buf[32];
908         
909         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
910         string new_name = _name;
911         new_name += '.';
912         new_name += buf;
913
914         cnt = min (_get_maximum_extent() - start, cnt);
915         return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
916 }
917
918 int
919 Playlist::paste (Playlist& other, nframes_t position, float times)
920 {
921         times = fabs (times);
922         nframes_t old_length;
923
924         {
925                 RegionLock rl1 (this);
926                 RegionLock rl2 (&other);
927
928                 old_length = _get_maximum_extent();
929         
930                 int itimes = (int) floor (times);
931                 nframes_t pos = position;
932                 nframes_t shift = other._get_maximum_extent();
933                 layer_t top_layer = regions.size();
934
935                 while (itimes--) {
936                         for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
937                                 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
938
939                                 /* put these new regions on top of all existing ones, but preserve
940                                    the ordering they had in the original playlist.
941                                 */
942                                 
943                                 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
944                                 add_region_internal (copy_of_region, copy_of_region->position() + pos);
945                         }
946                         pos += shift;
947                 }
948
949                 possibly_splice_unlocked ();
950
951                 /* XXX shall we handle fractional cases at some point? */
952
953                 if (old_length != _get_maximum_extent()) {
954                         notify_length_changed ();
955                 }
956
957                 
958         }
959
960         maybe_save_state (_("paste"));
961
962         return 0;
963 }
964
965
966 void
967 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
968 {
969         times = fabs (times);
970
971         RegionLock rl (this);
972         int itimes = (int) floor (times);
973         nframes_t pos = position;
974
975         while (itimes--) {
976                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
977                 add_region_internal (copy, pos, true);
978                 pos += region->length();
979         }
980
981         if (floor (times) != times) {
982                 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
983                 string name;
984                 _session.region_name (name, region->name(), false);
985                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
986                 add_region_internal (sub, pos, true);
987         }
988
989         maybe_save_state (_("duplicate"));
990 }
991
992 void
993 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
994 {
995         RegionLock rl (this);
996
997         if (!region->covers (playlist_position)) {
998                 return;
999         }
1000
1001         if (region->position() == playlist_position ||
1002             region->last_frame() == playlist_position) {
1003                 return;
1004         }
1005
1006         boost::shared_ptr<Region> left;
1007         boost::shared_ptr<Region> right;
1008         nframes_t before;
1009         nframes_t after;
1010         string before_name;
1011         string after_name;
1012
1013         before = playlist_position - region->position();
1014         after = region->length() - before;
1015         
1016         
1017         _session.region_name (before_name, region->name(), false);
1018         left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1019
1020         _session.region_name (after_name, region->name(), false);
1021         right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1022
1023         add_region_internal (left, region->position(), true);
1024         add_region_internal (right, region->position() + before);
1025         
1026         uint64_t orig_layer_op = region->last_layer_op();
1027         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1028                 if ((*i)->last_layer_op() > orig_layer_op) {
1029                         (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1030                 }
1031         }
1032         
1033         left->set_last_layer_op ( orig_layer_op );
1034         right->set_last_layer_op ( orig_layer_op + 1);
1035
1036         layer_op_counter++;
1037
1038         finalize_split_region (region, left, right);
1039         
1040         if (remove_region_internal (region, true)) {
1041                 return;
1042         }
1043
1044         maybe_save_state (_("split"));
1045 }
1046
1047 void
1048 Playlist::possibly_splice ()
1049 {
1050         if (_edit_mode == Splice) {
1051                 splice_locked ();
1052         }
1053 }
1054
1055 void
1056 Playlist::possibly_splice_unlocked ()
1057 {
1058         if (_edit_mode == Splice) {
1059                 splice_unlocked ();
1060         }
1061 }
1062
1063 void
1064 Playlist::splice_locked ()
1065 {
1066         {
1067                 RegionLock rl (this);
1068                 core_splice ();
1069         }
1070
1071         notify_length_changed ();
1072 }
1073
1074 void
1075 Playlist::splice_unlocked ()
1076 {
1077         core_splice ();
1078         notify_length_changed ();
1079 }
1080
1081 void
1082 Playlist::core_splice ()
1083 {
1084         _splicing = true;
1085         
1086         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1087                 
1088                 RegionList::iterator next;
1089                 
1090                 next = i;
1091                 ++next;
1092                 
1093                 if (next == regions.end()) {
1094                         break;
1095                 }
1096                 
1097                 (*next)->set_position ((*i)->last_frame() + 1, this);
1098         }
1099         
1100         _splicing = false;
1101 }
1102
1103 void
1104 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1105 {
1106         if (in_set_state || _splicing || _nudging) {
1107                 return;
1108         }
1109
1110         if (what_changed & ARDOUR::PositionChanged) {
1111
1112                 /* remove it from the list then add it back in
1113                    the right place again.
1114                 */
1115                 
1116                 RegionSortByPosition cmp;
1117
1118                 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1119                 
1120                 if (i == regions.end()) {
1121                         warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1122                                             _name, region->name())
1123                                 << endmsg;
1124                         return;
1125                 }
1126
1127                 regions.erase (i);
1128                 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1129
1130         }
1131
1132         if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1133         
1134                 if (holding_state ()) {
1135                         pending_bounds.push_back (region);
1136                 } else {
1137                         if (Config->get_layer_model() == MoveAddHigher) {
1138                                 /* it moved or changed length, so change the timestamp */
1139                                 timestamp_layer_op (region);
1140                         }
1141                         
1142                         possibly_splice ();
1143                         check_dependents (region, false);
1144                         notify_length_changed ();
1145                         relayer ();
1146                 }
1147         }
1148 }
1149
1150 void
1151 Playlist::region_changed_proxy (Change what_changed, boost::shared_ptr<Region> region)
1152 {
1153         /* this makes a virtual call to the right kind of playlist ... */
1154
1155         region_changed (what_changed, region);
1156 }
1157
1158 bool
1159 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1160 {
1161         Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1162         bool save = false;
1163
1164         if (in_set_state || in_flush) {
1165                 return false;
1166         }
1167
1168         {
1169                 if (what_changed & BoundsChanged) {
1170                         region_bounds_changed (what_changed, region);
1171                         save = !(_splicing || _nudging);
1172                 }
1173                 
1174                 if ((what_changed & Region::MuteChanged) && 
1175                     !(what_changed &  Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1176                         check_dependents (region, false);
1177                 }
1178                 
1179                 if (what_changed & our_interests) {
1180                         save = true;
1181                 }
1182         }
1183
1184         return save;
1185 }
1186
1187 void
1188 Playlist::clear (bool with_save)
1189 {
1190         RegionList::iterator i;
1191         RegionList tmp;
1192
1193         { 
1194                 RegionLock rl (this);
1195                 tmp = regions;
1196                 regions.clear ();
1197         }
1198         
1199         for (i = tmp.begin(); i != tmp.end(); ++i) {
1200                 notify_region_removed (*i);
1201         }
1202
1203         if (with_save) {
1204                 maybe_save_state (_("clear"));
1205         }
1206 }
1207
1208 /***********************************************************************
1209  FINDING THINGS
1210  **********************************************************************/
1211
1212 Playlist::RegionList *
1213 Playlist::regions_at (nframes_t frame)
1214
1215 {
1216         RegionLock rlock (this);
1217         return find_regions_at (frame);
1218 }       
1219
1220 boost::shared_ptr<Region>
1221 Playlist::top_region_at (nframes_t frame)
1222
1223 {
1224         RegionLock rlock (this);
1225         RegionList *rlist = find_regions_at (frame);
1226         boost::shared_ptr<Region> region;
1227         
1228         if (rlist->size()) {
1229                 RegionSortByLayer cmp;
1230                 rlist->sort (cmp);
1231                 region = rlist->back();
1232         } 
1233
1234         delete rlist;
1235         return region;
1236 }       
1237
1238 Playlist::RegionList *
1239 Playlist::find_regions_at (nframes_t frame)
1240 {
1241         RegionList *rlist = new RegionList;
1242
1243         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1244                 if ((*i)->covers (frame)) {
1245                         rlist->push_back (*i);
1246                 }
1247         }
1248
1249         return rlist;
1250 }
1251
1252 Playlist::RegionList *
1253 Playlist::regions_touched (nframes_t start, nframes_t end)
1254 {
1255         RegionLock rlock (this);
1256         RegionList *rlist = new RegionList;
1257
1258         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1259                 if ((*i)->coverage (start, end) != OverlapNone) {
1260                         rlist->push_back (*i);
1261                 }
1262         }
1263
1264         return rlist;
1265 }
1266
1267
1268 boost::shared_ptr<Region>
1269 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1270 {
1271         RegionLock rlock (this);
1272         boost::shared_ptr<Region> ret;
1273         nframes_t closest = max_frames;
1274
1275         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1276
1277                 nframes_t distance;
1278                 boost::shared_ptr<Region> r = (*i);
1279                 nframes_t pos = 0;
1280
1281                 switch (point) {
1282                 case Start:
1283                         pos = r->first_frame ();
1284                         break;
1285                 case End:
1286                         pos = r->last_frame ();
1287                         break;
1288                 case SyncPoint:
1289                         pos = r->adjust_to_sync (r->first_frame());
1290                         break;
1291                 }
1292
1293                 switch (dir) {
1294                 case 1: /* forwards */
1295
1296                         if (pos > frame) {
1297                                 if ((distance = pos - frame) < closest) {
1298                                         closest = distance;
1299                                         ret = r;
1300                                 }
1301                         }
1302
1303                         break;
1304
1305                 default: /* backwards */
1306
1307                         if (pos < frame) {
1308                                 if ((distance = frame - pos) < closest) {
1309                                         closest = distance;
1310                                         ret = r;
1311                                 }
1312                         }
1313                         break;
1314                 }
1315         }
1316
1317         return ret;
1318 }
1319
1320 /***********************************************************************/
1321
1322
1323
1324 void
1325 Playlist::mark_session_dirty ()
1326 {
1327         if (!in_set_state && !holding_state ()) {
1328                 _session.set_dirty();
1329         }
1330 }
1331
1332 int
1333 Playlist::set_state (const XMLNode& node)
1334 {
1335         in_set_state = true;
1336
1337         XMLNode *child;
1338         XMLNodeList nlist;
1339         XMLNodeConstIterator niter;
1340         XMLPropertyList plist;
1341         XMLPropertyConstIterator piter;
1342         XMLProperty *prop;
1343         boost::shared_ptr<Region> region;
1344         string region_name;
1345
1346         clear (false);
1347
1348         if (node.name() != "Playlist") {
1349                 in_set_state = false;
1350                 return -1;
1351         }
1352
1353         plist = node.properties();
1354
1355         for (piter = plist.begin(); piter != plist.end(); ++piter) {
1356
1357                 prop = *piter;
1358                 
1359                 if (prop->name() == X_("name")) {
1360                         _name = prop->value();
1361                 } else if (prop->name() == X_("orig_diskstream_id")) {
1362                         _orig_diskstream_id = prop->value ();
1363                 } else if (prop->name() == X_("frozen")) {
1364                         _frozen = (prop->value() == X_("yes"));
1365                 }
1366         }
1367
1368         nlist = node.children();
1369
1370         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1371
1372                 child = *niter;
1373                 
1374                 if (child->name() == "Region") {
1375
1376                         if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1377                                 error << _("Playlist: cannot create region from state file") << endmsg;
1378                                 continue;
1379                         }
1380
1381                         add_region (region, region->position(), 1.0, false);
1382
1383                         // So that layer_op ordering doesn't get screwed up
1384                         region->set_last_layer_op( region->layer());
1385
1386                 }                       
1387         }
1388
1389         
1390         /* update dependents, which was not done during add_region_internal 
1391            due to in_set_state being true 
1392         */
1393
1394         for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1395                 check_dependents (*r, false);
1396         }
1397         
1398         in_set_state = false;
1399
1400         return 0;
1401 }
1402
1403 XMLNode&
1404 Playlist::get_state()
1405 {
1406         return state(true);
1407 }
1408
1409 XMLNode&
1410 Playlist::get_template()
1411 {
1412         return state(false);
1413 }
1414
1415 XMLNode&
1416 Playlist::state (bool full_state)
1417 {
1418         XMLNode *node = new XMLNode (X_("Playlist"));
1419         char buf[64];
1420         
1421         node->add_property (X_("name"), _name);
1422
1423         _orig_diskstream_id.print (buf, sizeof (buf));
1424         node->add_property (X_("orig_diskstream_id"), buf);
1425         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1426
1427         if (full_state) {
1428                 RegionLock rlock (this, false);
1429
1430                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1431                         node->add_child_nocopy ((*i)->get_state());
1432                 }
1433         }
1434
1435         if (_extra_xml) {
1436                 node->add_child_copy (*_extra_xml);
1437         }
1438
1439         return *node;
1440 }
1441
1442 bool
1443 Playlist::empty() const
1444 {
1445         return regions.empty();
1446 }
1447
1448 nframes_t
1449 Playlist::get_maximum_extent () const
1450 {
1451         RegionLock rlock (const_cast<Playlist *>(this));
1452         return _get_maximum_extent ();
1453 }
1454
1455 nframes_t
1456 Playlist::_get_maximum_extent () const
1457 {
1458         RegionList::const_iterator i;
1459         nframes_t max_extent = 0;
1460         nframes_t end = 0;
1461
1462         for (i = regions.begin(); i != regions.end(); ++i) {
1463                 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1464                         max_extent = end;
1465                 }
1466         }
1467
1468         return max_extent;
1469 }
1470
1471 string 
1472 Playlist::bump_name (string name, Session &session)
1473 {
1474         string newname = name;
1475
1476         do {
1477                 newname = Playlist::bump_name_once (newname);
1478         } while (session.playlist_by_name(newname)!=NULL);
1479
1480         return newname;
1481 }
1482
1483 string
1484 Playlist::bump_name_once (string name)
1485 {
1486         string::size_type period;
1487         string newname;
1488
1489         if ((period = name.find_last_of ('.')) == string::npos) {
1490                 newname = name;
1491                 newname += ".1";
1492         } else {
1493                 char buf[32];
1494                 int version;
1495                 
1496                 sscanf (name.substr (period+1).c_str(), "%d", &version);
1497                 snprintf (buf, sizeof(buf), "%d", version+1);
1498                 
1499                 newname = name.substr (0, period+1);
1500                 newname += buf;
1501         }
1502
1503         return newname;
1504 }
1505
1506 layer_t
1507 Playlist::top_layer() const
1508 {
1509         RegionLock rlock (const_cast<Playlist *> (this));
1510         layer_t top = 0;
1511
1512         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1513                 top = max (top, (*i)->layer());
1514         }
1515         return top;
1516 }
1517
1518 void
1519 Playlist::set_edit_mode (EditMode mode)
1520 {
1521         _edit_mode = mode;
1522 }
1523
1524 /********************
1525  * Region Layering
1526  ********************/
1527
1528 void
1529 Playlist::relayer ()
1530 {
1531         RegionList::iterator i;
1532         uint32_t layer = 0;
1533
1534         /* don't send multiple Modified notifications
1535            when multiple regions are relayered.
1536         */
1537
1538         freeze ();
1539
1540         if (Config->get_layer_model() == MoveAddHigher || 
1541             Config->get_layer_model() == AddHigher) {
1542
1543                 RegionSortByLastLayerOp cmp;
1544                 RegionList copy = regions;
1545
1546                 copy.sort (cmp);
1547
1548                 for (i = copy.begin(); i != copy.end(); ++i) {
1549                         (*i)->set_layer (layer++);
1550                 }
1551
1552         } else {
1553                 
1554                 /* Session::LaterHigher model */
1555
1556                 for (i = regions.begin(); i != regions.end(); ++i) {
1557                         (*i)->set_layer (layer++);
1558                 }
1559         }
1560
1561         /* sending Modified means that various kinds of layering
1562            models operate correctly at the GUI
1563            level. slightly inefficient, but only slightly.
1564
1565            We force a Modified signal here in case no layers actually
1566            changed.
1567         */
1568
1569         notify_modified ();
1570
1571         thaw ();
1572 }
1573
1574 /* XXX these layer functions are all deprecated */
1575
1576 void
1577 Playlist::raise_region (boost::shared_ptr<Region> region)
1578 {
1579         uint32_t rsz = regions.size();
1580         layer_t target = region->layer() + 1U;
1581
1582         if (target >= rsz) {
1583                 /* its already at the effective top */
1584                 return;
1585         }
1586
1587         move_region_to_layer (target, region, 1);
1588 }
1589
1590 void
1591 Playlist::lower_region (boost::shared_ptr<Region> region)
1592 {
1593         if (region->layer() == 0) {
1594                 /* its already at the bottom */
1595                 return;
1596         }
1597
1598         layer_t target = region->layer() - 1U;
1599
1600         move_region_to_layer (target, region, -1);
1601 }
1602
1603 void
1604 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1605 {
1606         /* does nothing useful if layering mode is later=higher */
1607         if ((Config->get_layer_model() == MoveAddHigher) ||
1608             (Config->get_layer_model() == AddHigher)) {
1609                 timestamp_layer_op (region);
1610                 relayer ();
1611         }
1612 }
1613
1614 void
1615 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1616 {
1617         /* does nothing useful if layering mode is later=higher */
1618         if ((Config->get_layer_model() == MoveAddHigher) ||
1619             (Config->get_layer_model() == AddHigher)) {
1620                 region->set_last_layer_op (0);
1621                 relayer ();
1622         }
1623 }
1624
1625 int
1626 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1627 {
1628         RegionList::iterator i;
1629         typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1630         list<LayerInfo> layerinfo;
1631         layer_t dest;
1632
1633         {
1634                 RegionLock rlock (const_cast<Playlist *> (this));
1635                 
1636                 for (i = regions.begin(); i != regions.end(); ++i) {
1637                         
1638                         if (region == *i) {
1639                                 continue;
1640                         }
1641
1642                         if (dir > 0) {
1643
1644                                 /* region is moving up, move all regions on intermediate layers
1645                                    down 1
1646                                 */
1647                                 
1648                                 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1649                                         dest = (*i)->layer() - 1;
1650                                 } else {
1651                                         /* not affected */
1652                                         continue;
1653                                 }
1654                         } else {
1655
1656                                 /* region is moving down, move all regions on intermediate layers
1657                                    up 1
1658                                 */
1659
1660                                 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1661                                         dest = (*i)->layer() + 1;
1662                                 } else {
1663                                         /* not affected */
1664                                         continue;
1665                                 }
1666                         }
1667
1668                         LayerInfo newpair;
1669                         
1670                         newpair.first = *i;
1671                         newpair.second = dest;
1672                         
1673                         layerinfo.push_back (newpair);
1674                 } 
1675         }
1676
1677         /* now reset the layers without holding the region lock */
1678
1679         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1680                 x->first->set_layer (x->second);
1681         }
1682
1683         region->set_layer (target_layer);
1684
1685         /* now check all dependents */
1686
1687         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1688                 check_dependents (x->first, false);
1689         }
1690         
1691         check_dependents (region, false);
1692         
1693         return 0;
1694 }
1695
1696 void
1697 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1698 {
1699         RegionList::iterator i;
1700         nframes_t new_pos;
1701         bool moved = false;
1702
1703         _nudging = true;
1704
1705         {
1706                 RegionLock rlock (const_cast<Playlist *> (this));
1707                 
1708                 for (i = regions.begin(); i != regions.end(); ++i) {
1709
1710                         if ((*i)->position() >= start) {
1711
1712                                 if (forwards) {
1713
1714                                         if ((*i)->last_frame() > max_frames - distance) {
1715                                                 new_pos = max_frames - (*i)->length();
1716                                         } else {
1717                                                 new_pos = (*i)->position() + distance;
1718                                         }
1719                                         
1720                                 } else {
1721                                         
1722                                         if ((*i)->position() > distance) {
1723                                                 new_pos = (*i)->position() - distance;
1724                                         } else {
1725                                                 new_pos = 0;
1726                                         }
1727                                 }
1728
1729                                 (*i)->set_position (new_pos, this);
1730                                 moved = true;
1731                         }
1732                 }
1733         }
1734
1735         if (moved) {
1736                 _nudging = false;
1737                 maybe_save_state (_("nudged"));
1738                 notify_length_changed ();
1739         }
1740
1741 }
1742
1743 boost::shared_ptr<Region>
1744 Playlist::find_region (const ID& id) const
1745 {
1746         RegionLock rlock (const_cast<Playlist*> (this));
1747         RegionList::const_iterator i;
1748         boost::shared_ptr<Region> ret;
1749
1750         for (i = regions.begin(); i != regions.end(); ++i) {
1751                 if ((*i)->id() == id) {
1752                         ret = *i;
1753                 }
1754         }
1755
1756         return ret;
1757 }
1758         
1759 void
1760 Playlist::save_state (std::string why)
1761 {
1762         if (!in_set_state) {
1763                 StateManager::save_state (why);
1764         }
1765 }
1766
1767 void
1768 Playlist::dump () const
1769 {
1770         boost::shared_ptr<Region> r;
1771
1772         cerr << "Playlist \"" << _name << "\" " << endl
1773              << regions.size() << " regions "
1774              << endl;
1775
1776         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1777                 r = *i;
1778                 cerr << "  " << r->name() << " [" 
1779                      << r->start() << "+" << r->length() 
1780                      << "] at " 
1781                      << r->position()
1782                      << " on layer "
1783                      << r->layer ()
1784                      << endl;
1785         }
1786 }
1787
1788 void
1789 Playlist::set_frozen (bool yn)
1790 {
1791         _frozen = yn;
1792 }
1793
1794 void
1795 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
1796 {
1797 //      struct timeval tv;
1798 //      gettimeofday (&tv, 0);
1799         region->set_last_layer_op (++layer_op_counter);
1800 }
1801
1802 void
1803 Playlist::maybe_save_state (string why)
1804 {
1805         if (holding_state ()) {
1806                 save_on_thaw = true;
1807                 last_save_reason = why;
1808         } else {
1809                 save_state (why);
1810         }
1811 }