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