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