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