019c00835368a3037fef8f975b7c7cdea1bb8773
[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         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
603                 if (Config->get_use_overlap_equivalency()) {
604                         if ((*i)->overlap_equivalent (other)) {
605                                 results.push_back ((*i));
606                         } else if ((*i)->equivalent (other)) {
607                                 results.push_back ((*i));
608                         }
609                 }
610         }
611 }
612
613 void
614 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
615 {
616         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
617
618                 if ((*i) && (*i)->region_list_equivalent (other)) {
619                         results.push_back (*i);
620                 }
621         }
622 }
623
624 void
625 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
626 {
627         RegionList thawlist;
628
629         partition_internal (start, end, false, thawlist);
630
631         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
632                 (*i)->thaw ("separation");
633         }
634 }
635
636 void
637 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
638 {
639         RegionLock rlock (this);
640         boost::shared_ptr<Region> region;
641         boost::shared_ptr<Region> current;
642         string new_name;
643         RegionList::iterator tmp;
644         OverlapType overlap;
645         nframes_t pos1, pos2, pos3, pos4;
646         RegionList new_regions;
647
648         in_partition = true;
649
650         /* need to work from a copy, because otherwise the regions we add during the process
651            get operated on as well.
652         */
653
654         RegionList copy = regions;
655
656         for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
657                 
658                 tmp = i;
659                 ++tmp;
660
661                 current = *i;
662                 
663                 if (current->first_frame() == start && current->last_frame() == end) {
664                         if (cutting) {
665                                 remove_region_internal (current);
666                         }
667                         continue;
668                 }
669                 
670                 if ((overlap = current->coverage (start, end)) == OverlapNone) {
671                         continue;
672                 }
673                 
674                 pos1 = current->position();
675                 pos2 = start;
676                 pos3 = end;
677                 pos4 = current->last_frame();
678
679                 if (overlap == OverlapInternal) {
680                         
681                         /* split: we need 3 new regions, the front, middle and end.
682                            cut:   we need 2 regions, the front and end.
683                         */
684                         
685                         /*
686                                          start                 end
687                           ---------------*************************------------
688                                          P1  P2              P3  P4
689                           SPLIT:
690                           ---------------*****++++++++++++++++====------------
691                           CUT
692                           ---------------*****----------------====------------
693                           
694                         */
695
696                         if (!cutting) {
697                                 
698                                 /* "middle" ++++++ */
699                                 
700                                 _session.region_name (new_name, current->name(), false);
701                                 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
702                                                        regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
703                                 add_region_internal (region, start);
704                                 new_regions.push_back (region);
705                         }
706                         
707                         /* "end" ====== */
708                         
709                         _session.region_name (new_name, current->name(), false);
710                         region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, 
711                                                regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
712
713                         add_region_internal (region, end);
714                         new_regions.push_back (region);
715
716                         /* "front" ***** */
717                                 
718                         current->freeze ();
719                         thawlist.push_back (current);
720                         current->trim_end (pos2, this);
721
722                 } else if (overlap == OverlapEnd) {
723
724                         /*
725                                                               start           end
726                                     ---------------*************************------------
727                                                    P1           P2         P4   P3
728                                     SPLIT:                                                 
729                                     ---------------**************+++++++++++------------
730                                     CUT:                                                   
731                                     ---------------**************-----------------------
732
733                         */
734
735                         if (!cutting) {
736                                 
737                                 /* end +++++ */
738                                 
739                                 _session.region_name (new_name, current->name(), false);
740                                 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
741                                                        Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
742                                 add_region_internal (region, start);
743                                 new_regions.push_back (region);
744                         }
745
746                         /* front ****** */
747
748                         current->freeze ();
749                         thawlist.push_back (current);
750                         current->trim_end (pos2, this);
751
752                 } else if (overlap == OverlapStart) {
753
754                         /* split: we need 2 regions: the front and the end.
755                            cut: just trim current to skip the cut area
756                         */
757                                 
758                         /*
759                                                         start           end
760                                     ---------------*************************------------
761                                        P2          P1 P3                   P4          
762
763                                     SPLIT:
764                                     ---------------****+++++++++++++++++++++------------
765                                     CUT:
766                                     -------------------*********************------------
767                                     
768                         */
769
770                         if (!cutting) {
771                                 
772                                 /* front **** */
773                                  _session.region_name (new_name, current->name(), false);
774                                  region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
775                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
776                                  add_region_internal (region, pos1);
777                                  new_regions.push_back (region);
778                         } 
779                         
780                         /* end */
781                         
782                         current->freeze ();
783                         thawlist.push_back (current);
784                         current->trim_front (pos3, this);
785
786                 } else if (overlap == OverlapExternal) {
787
788                         /* split: no split required.
789                            cut: remove the region.
790                         */
791                                 
792                         /*
793                                        start                                      end
794                                     ---------------*************************------------
795                                        P2          P1 P3                   P4          
796
797                                     SPLIT:
798                                     ---------------*************************------------
799                                     CUT:
800                                     ----------------------------------------------------
801                                     
802                         */
803
804                         if (cutting) {
805                                 remove_region_internal (current);
806                         }
807                         new_regions.push_back (current);
808                 }
809         }
810
811         in_partition = false;
812
813         for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
814                 check_dependents (*i, false);
815         }
816 }
817
818 boost::shared_ptr<Playlist>
819 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
820 {
821         boost::shared_ptr<Playlist> ret;
822         boost::shared_ptr<Playlist> pl;
823         nframes_t start;
824
825         if (ranges.empty()) {
826                 return boost::shared_ptr<Playlist>();
827         }
828
829         start = ranges.front().start;
830
831         for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
832
833                 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
834                 
835                 if (i == ranges.begin()) {
836                         ret = pl;
837                 } else {
838                         
839                         /* paste the next section into the nascent playlist,
840                            offset to reflect the start of the first range we
841                            chopped.
842                         */
843
844                         ret->paste (pl, (*i).start - start, 1.0f);
845                 }
846         }
847
848         return ret;
849 }
850
851 boost::shared_ptr<Playlist>
852 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
853 {
854         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
855         return cut_copy (pmf, ranges, result_is_hidden);
856 }
857
858 boost::shared_ptr<Playlist>
859 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
860 {
861         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
862         return cut_copy (pmf, ranges, result_is_hidden);
863 }
864
865 boost::shared_ptr<Playlist>
866 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
867 {
868         boost::shared_ptr<Playlist> the_copy;
869         RegionList thawlist;
870         char buf[32];
871
872         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
873         string new_name = _name;
874         new_name += '.';
875         new_name += buf;
876
877         if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
878                 return boost::shared_ptr<Playlist>();
879         }
880
881         partition_internal (start, start+cnt-1, true, thawlist);
882         possibly_splice ();
883
884         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
885                 (*i)->thaw ("playlist cut");
886         }
887
888         return the_copy;
889 }
890
891 boost::shared_ptr<Playlist>
892 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
893 {
894         char buf[32];
895         
896         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
897         string new_name = _name;
898         new_name += '.';
899         new_name += buf;
900
901         cnt = min (_get_maximum_extent() - start, cnt);
902         return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
903 }
904
905 int
906 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
907 {
908         times = fabs (times);
909         nframes_t old_length;
910
911         {
912                 RegionLock rl1 (this);
913                 RegionLock rl2 (other.get());
914
915                 old_length = _get_maximum_extent();
916         
917                 int itimes = (int) floor (times);
918                 nframes_t pos = position;
919                 nframes_t shift = other->_get_maximum_extent();
920                 layer_t top_layer = regions.size();
921
922                 while (itimes--) {
923                         for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
924                                 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
925
926                                 /* put these new regions on top of all existing ones, but preserve
927                                    the ordering they had in the original playlist.
928                                 */
929                                 
930                                 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
931                                 add_region_internal (copy_of_region, copy_of_region->position() + pos);
932                         }
933                         pos += shift;
934                 }
935
936                 possibly_splice_unlocked ();
937
938                 /* XXX shall we handle fractional cases at some point? */
939
940                 if (old_length != _get_maximum_extent()) {
941                         notify_length_changed ();
942                 }
943
944                 
945         }
946
947         return 0;
948 }
949
950
951 void
952 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
953 {
954         times = fabs (times);
955
956         RegionLock rl (this);
957         int itimes = (int) floor (times);
958         nframes_t pos = position;
959
960         while (itimes--) {
961                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
962                 add_region_internal (copy, pos);
963                 pos += region->length();
964         }
965
966         if (floor (times) != times) {
967                 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
968                 string name;
969                 _session.region_name (name, region->name(), false);
970                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
971                 add_region_internal (sub, pos);
972         }
973 }
974
975 void
976 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
977 {
978         RegionLock rl (this);
979
980         if (!region->covers (playlist_position)) {
981                 return;
982         }
983
984         if (region->position() == playlist_position ||
985             region->last_frame() == playlist_position) {
986                 return;
987         }
988
989         boost::shared_ptr<Region> left;
990         boost::shared_ptr<Region> right;
991         nframes_t before;
992         nframes_t after;
993         string before_name;
994         string after_name;
995
996         before = playlist_position - region->position();
997         after = region->length() - before;
998         
999         
1000         _session.region_name (before_name, region->name(), false);
1001         left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1002
1003         _session.region_name (after_name, region->name(), false);
1004         right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1005
1006         add_region_internal (left, region->position());
1007         add_region_internal (right, region->position() + before);
1008         
1009         uint64_t orig_layer_op = region->last_layer_op();
1010         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1011                 if ((*i)->last_layer_op() > orig_layer_op) {
1012                         (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1013                 }
1014         }
1015         
1016         left->set_last_layer_op ( orig_layer_op );
1017         right->set_last_layer_op ( orig_layer_op + 1);
1018
1019         layer_op_counter++;
1020
1021         finalize_split_region (region, left, right);
1022         
1023         if (remove_region_internal (region)) {
1024                 return;
1025         }
1026 }
1027
1028 void
1029 Playlist::possibly_splice ()
1030 {
1031         if (_edit_mode == Splice) {
1032                 splice_locked ();
1033         }
1034 }
1035
1036 void
1037 Playlist::possibly_splice_unlocked ()
1038 {
1039         if (_edit_mode == Splice) {
1040                 splice_unlocked ();
1041         }
1042 }
1043
1044 void
1045 Playlist::splice_locked ()
1046 {
1047         {
1048                 RegionLock rl (this);
1049                 core_splice ();
1050         }
1051
1052         notify_length_changed ();
1053 }
1054
1055 void
1056 Playlist::splice_unlocked ()
1057 {
1058         core_splice ();
1059         notify_length_changed ();
1060 }
1061
1062 void
1063 Playlist::core_splice ()
1064 {
1065         _splicing = true;
1066         
1067         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1068                 
1069                 RegionList::iterator next;
1070                 
1071                 next = i;
1072                 ++next;
1073                 
1074                 if (next == regions.end()) {
1075                         break;
1076                 }
1077                 
1078                 (*next)->set_position ((*i)->last_frame() + 1, this);
1079         }
1080         
1081         _splicing = false;
1082 }
1083
1084 void
1085 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1086 {
1087         if (in_set_state || _splicing || _nudging) {
1088                 return;
1089         }
1090
1091         if (what_changed & ARDOUR::PositionChanged) {
1092
1093                 /* remove it from the list then add it back in
1094                    the right place again.
1095                 */
1096                 
1097                 RegionSortByPosition cmp;
1098
1099                 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1100                 
1101                 if (i == regions.end()) {
1102                         warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1103                                             _name, region->name())
1104                                 << endmsg;
1105                         return;
1106                 }
1107
1108                 regions.erase (i);
1109                 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1110
1111         }
1112
1113         if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1114                 if (holding_state ()) {
1115                         pending_bounds.push_back (region);
1116                 } else {
1117                         if (Config->get_layer_model() == MoveAddHigher) {
1118                                 /* it moved or changed length, so change the timestamp */
1119                                 timestamp_layer_op (region);
1120                         }
1121                         
1122                         possibly_splice ();
1123                         notify_length_changed ();
1124                         relayer ();
1125                         check_dependents (region, false);
1126                 }
1127         }
1128 }
1129
1130 void
1131 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1132 {
1133         boost::shared_ptr<Region> region (weak_region.lock());
1134
1135         if (!region) {
1136                 return;
1137         }
1138
1139
1140         /* this makes a virtual call to the right kind of playlist ... */
1141
1142         region_changed (what_changed, region);
1143 }
1144
1145 bool
1146 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1147 {
1148         Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1149         bool save = false;
1150
1151         if (in_set_state || in_flush) {
1152                 return false;
1153         }
1154
1155         {
1156                 if (what_changed & BoundsChanged) {
1157                         region_bounds_changed (what_changed, region);
1158                         save = !(_splicing || _nudging);
1159                 }
1160                 
1161                 if ((what_changed & Region::MuteChanged) && 
1162                     !(what_changed &  Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1163                         check_dependents (region, false);
1164                 }
1165                 
1166                 if (what_changed & our_interests) {
1167                         save = true;
1168                 }
1169         }
1170
1171         return save;
1172 }
1173
1174 void
1175 Playlist::clear (bool with_signals)
1176 {
1177         { 
1178                 RegionLock rl (this);
1179                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1180                         pending_removes.insert (*i);
1181                 }
1182                 regions.clear ();
1183         }
1184
1185         if (with_signals) {
1186                 LengthChanged ();
1187                 Modified ();
1188         }
1189
1190 }
1191
1192 /***********************************************************************
1193  FINDING THINGS
1194  **********************************************************************/
1195
1196 Playlist::RegionList *
1197 Playlist::regions_at (nframes_t frame)
1198
1199 {
1200         RegionLock rlock (this);
1201         return find_regions_at (frame);
1202 }       
1203
1204 boost::shared_ptr<Region>
1205 Playlist::top_region_at (nframes_t frame)
1206
1207 {
1208         RegionLock rlock (this);
1209         RegionList *rlist = find_regions_at (frame);
1210         boost::shared_ptr<Region> region;
1211         
1212         if (rlist->size()) {
1213                 RegionSortByLayer cmp;
1214                 rlist->sort (cmp);
1215                 region = rlist->back();
1216         } 
1217
1218         delete rlist;
1219         return region;
1220 }       
1221
1222 Playlist::RegionList *
1223 Playlist::find_regions_at (nframes_t frame)
1224 {
1225         RegionList *rlist = new RegionList;
1226
1227         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1228                 if ((*i)->covers (frame)) {
1229                         rlist->push_back (*i);
1230                 }
1231         }
1232
1233         return rlist;
1234 }
1235
1236 Playlist::RegionList *
1237 Playlist::regions_touched (nframes_t start, nframes_t end)
1238 {
1239         RegionLock rlock (this);
1240         RegionList *rlist = new RegionList;
1241
1242         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1243                 if ((*i)->coverage (start, end) != OverlapNone) {
1244                         rlist->push_back (*i);
1245                 }
1246         }
1247
1248         return rlist;
1249 }
1250
1251
1252 boost::shared_ptr<Region>
1253 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1254 {
1255         RegionLock rlock (this);
1256         boost::shared_ptr<Region> ret;
1257         nframes_t closest = max_frames;
1258
1259
1260         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1261
1262                 nframes_t distance;
1263                 boost::shared_ptr<Region> r = (*i);
1264                 nframes_t pos = 0;
1265
1266                 switch (point) {
1267                 case Start:
1268                         pos = r->first_frame ();
1269                         break;
1270                 case End:
1271                         pos = r->last_frame ();
1272                         break;
1273                 case SyncPoint:
1274                         pos = r->adjust_to_sync (r->first_frame());
1275                         break;
1276                 }
1277
1278                 switch (dir) {
1279                 case 1: /* forwards */
1280
1281                         if (pos >= frame) {
1282                                 if ((distance = pos - frame) < closest) {
1283                                         closest = distance;
1284                                         ret = r;
1285                                 }
1286                         }
1287
1288                         break;
1289
1290                 default: /* backwards */
1291
1292                         if (pos <= frame) {
1293                                 if ((distance = frame - pos) < closest) {
1294                                         closest = distance;
1295                                         ret = r;
1296                                 }
1297                         }
1298                         break;
1299                 }
1300         }
1301
1302         return ret;
1303 }
1304
1305 /***********************************************************************/
1306
1307
1308
1309 void
1310 Playlist::mark_session_dirty ()
1311 {
1312         if (!in_set_state && !holding_state ()) {
1313                 _session.set_dirty();
1314         }
1315 }
1316
1317 int
1318 Playlist::set_state (const XMLNode& node)
1319 {
1320         XMLNode *child;
1321         XMLNodeList nlist;
1322         XMLNodeConstIterator niter;
1323         XMLPropertyList plist;
1324         XMLPropertyConstIterator piter;
1325         XMLProperty *prop;
1326         boost::shared_ptr<Region> region;
1327         string region_name;
1328
1329         in_set_state++;
1330
1331         if (node.name() != "Playlist") {
1332                 in_set_state--;
1333                 return -1;
1334         }
1335
1336         freeze ();
1337
1338         plist = node.properties();
1339
1340         for (piter = plist.begin(); piter != plist.end(); ++piter) {
1341
1342                 prop = *piter;
1343                 
1344                 if (prop->name() == X_("name")) {
1345                         _name = prop->value();
1346                 } else if (prop->name() == X_("orig_diskstream_id")) {
1347                         _orig_diskstream_id = prop->value ();
1348                 } else if (prop->name() == X_("frozen")) {
1349                         _frozen = (prop->value() == X_("yes"));
1350                 }
1351         }
1352
1353         clear (false);
1354         
1355         nlist = node.children();
1356
1357         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1358
1359                 child = *niter;
1360                 
1361                 if (child->name() == "Region") {
1362
1363                         if ((prop = child->property ("id")) == 0) {
1364                                 error << _("region state node has no ID, ignored") << endmsg;
1365                                 continue;
1366                         }
1367                         
1368                         ID id = prop->value ();
1369                         
1370                         if ((region = region_by_id (id))) {
1371
1372                                 Change what_changed = Change (0);
1373
1374                                 if (region->set_live_state (*child, what_changed, true)) {
1375                                         error << _("Playlist: cannot reset region state from XML") << endmsg;
1376                                         continue;
1377                                 }
1378
1379                         } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1380                                 error << _("Playlist: cannot create region from XML") << endmsg;
1381                                 continue;
1382                         }
1383
1384                         add_region (region, region->position(), 1.0);
1385
1386                         // So that layer_op ordering doesn't get screwed up
1387                         region->set_last_layer_op( region->layer());
1388
1389                 }                       
1390         }
1391         
1392         notify_modified ();
1393
1394         thaw ();
1395
1396         /* update dependents, which was not done during add_region_internal 
1397            due to in_set_state being true 
1398         */
1399
1400         for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1401                 check_dependents (*r, false);
1402         }
1403
1404         in_set_state--;
1405
1406         return 0;
1407 }
1408
1409 XMLNode&
1410 Playlist::get_state()
1411 {
1412         return state(true);
1413 }
1414
1415 XMLNode&
1416 Playlist::get_template()
1417 {
1418         return state(false);
1419 }
1420
1421 XMLNode&
1422 Playlist::state (bool full_state)
1423 {
1424         XMLNode *node = new XMLNode (X_("Playlist"));
1425         char buf[64];
1426         
1427         node->add_property (X_("name"), _name);
1428
1429         _orig_diskstream_id.print (buf, sizeof (buf));
1430         node->add_property (X_("orig_diskstream_id"), buf);
1431         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1432
1433         if (full_state) {
1434                 RegionLock rlock (this, false);
1435                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1436                         node->add_child_nocopy ((*i)->get_state());
1437                 }
1438         }
1439
1440         if (_extra_xml) {
1441                 node->add_child_copy (*_extra_xml);
1442         }
1443
1444         return *node;
1445 }
1446
1447 bool
1448 Playlist::empty() const
1449 {
1450         RegionLock rlock (const_cast<Playlist *>(this), false);
1451         return regions.empty();
1452 }
1453
1454 uint32_t
1455 Playlist::n_regions() const
1456 {
1457         RegionLock rlock (const_cast<Playlist *>(this), false);
1458         return regions.size();
1459 }
1460
1461 nframes_t
1462 Playlist::get_maximum_extent () const
1463 {
1464         RegionLock rlock (const_cast<Playlist *>(this), false);
1465         return _get_maximum_extent ();
1466 }
1467
1468 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