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