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