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