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