fix incorrect _start after a split.
[ardour.git] / libs / ardour / playlist.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <stdint.h>
21 #include <set>
22 #include <algorithm>
23 #include <string>
24
25 #include <boost/lexical_cast.hpp>
26
27 #include "pbd/convert.h"
28 #include "pbd/stateful_diff_command.h"
29 #include "pbd/strsplit.h"
30 #include "pbd/xml++.h"
31
32 #include "ardour/debug.h"
33 #include "ardour/playlist.h"
34 #include "ardour/session.h"
35 #include "ardour/region.h"
36 #include "ardour/region_factory.h"
37 #include "ardour/region_sorters.h"
38 #include "ardour/playlist_factory.h"
39 #include "ardour/playlist_source.h"
40 #include "ardour/transient_detector.h"
41 #include "ardour/session_playlists.h"
42 #include "ardour/source_factory.h"
43
44 #include "pbd/i18n.h"
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49
50 namespace ARDOUR {
51         namespace Properties {
52                 PBD::PropertyDescriptor<bool> regions;
53         }
54 }
55
56 struct ShowMeTheList {
57     ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
58     ~ShowMeTheList () {
59             cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
60     };
61     boost::shared_ptr<Playlist> playlist;
62     string name;
63 };
64
65
66
67 void
68 Playlist::make_property_quarks ()
69 {
70         Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
71         DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",
72                                                         Properties::regions.property_id));
73 }
74
75 RegionListProperty::RegionListProperty (Playlist& pl)
76         : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
77         , _playlist (pl)
78 {
79
80 }
81
82 RegionListProperty::RegionListProperty (RegionListProperty const & p)
83         : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
84         , _playlist (p._playlist)
85 {
86
87 }
88
89 RegionListProperty *
90 RegionListProperty::clone () const
91 {
92         return new RegionListProperty (*this);
93 }
94
95 RegionListProperty *
96 RegionListProperty::create () const
97 {
98         return new RegionListProperty (_playlist);
99 }
100
101 void
102 RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
103 {
104         /* All regions (even those which are deleted) have their state saved by other
105            code, so we can just store ID here.
106         */
107
108         node.add_property ("id", region->id().to_s ());
109 }
110
111 boost::shared_ptr<Region>
112 RegionListProperty::get_content_from_xml (XMLNode const & node) const
113 {
114         XMLProperty const * prop = node.property ("id");
115         assert (prop);
116
117         PBD::ID id (prop->value ());
118
119         boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
120
121         if (!ret) {
122                 ret = RegionFactory::region_by_id (id);
123         }
124
125         return ret;
126 }
127
128 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
129         : SessionObject(sess, nom)
130         , regions (*this)
131         , _type(type)
132 {
133         init (hide);
134         first_set_state = false;
135         _name = nom;
136         _set_sort_id ();
137 }
138
139 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
140         : SessionObject(sess, "unnamed playlist")
141         , regions (*this)
142         , _type(type)
143 {
144 #ifndef NDEBUG
145         XMLProperty const * prop = node.property("type");
146         assert(!prop || DataType(prop->value()) == _type);
147 #endif
148
149         init (hide);
150         _name = "unnamed"; /* reset by set_state */
151         _set_sort_id ();
152
153         /* set state called by derived class */
154 }
155
156 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
157         : SessionObject(other->_session, namestr)
158         , regions (*this)
159         , _type(other->_type)
160         , _orig_track_id (other->_orig_track_id)
161         , _shared_with_ids (other->_shared_with_ids)
162 {
163         init (hide);
164
165         RegionList tmp;
166         other->copy_regions (tmp);
167
168         in_set_state++;
169
170         for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
171                 add_region_internal( (*x), (*x)->position());
172         }
173
174         in_set_state--;
175
176         _splicing  = other->_splicing;
177         _rippling  = other->_rippling;
178         _nudging   = other->_nudging;
179         _edit_mode = other->_edit_mode;
180
181         in_set_state = 0;
182         first_set_state = false;
183         in_flush = false;
184         in_partition = false;
185         subcnt = 0;
186         _frozen = other->_frozen;
187 }
188
189 Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
190         : SessionObject(other->_session, str)
191         , regions (*this)
192         , _type(other->_type)
193         , _orig_track_id (other->_orig_track_id)
194         , _shared_with_ids (other->_shared_with_ids)
195 {
196         RegionReadLock rlock2 (const_cast<Playlist*> (other.get()));
197
198         framepos_t end = start + cnt - 1;
199
200         init (hide);
201
202         in_set_state++;
203
204         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
205
206                 boost::shared_ptr<Region> region;
207                 boost::shared_ptr<Region> new_region;
208                 frameoffset_t offset = 0;
209                 framepos_t position = 0;
210                 framecnt_t len = 0;
211                 string    new_name;
212                 Evoral::OverlapType overlap;
213
214                 region = *i;
215
216                 overlap = region->coverage (start, end);
217
218                 switch (overlap) {
219                 case Evoral::OverlapNone:
220                         continue;
221
222                 case Evoral::OverlapInternal:
223                         offset = start - region->position();
224                         position = 0;
225                         len = cnt;
226                         break;
227
228                 case Evoral::OverlapStart:
229                         offset = 0;
230                         position = region->position() - start;
231                         len = end - region->position();
232                         break;
233
234                 case Evoral::OverlapEnd:
235                         offset = start - region->position();
236                         position = 0;
237                         len = region->length() - offset;
238                         break;
239
240                 case Evoral::OverlapExternal:
241                         offset = 0;
242                         position = region->position() - start;
243                         len = region->length();
244                         break;
245                 }
246
247                 RegionFactory::region_name (new_name, region->name(), false);
248
249                 PropertyList plist;
250
251                 plist.add (Properties::start, region->start() + offset);
252                 plist.add (Properties::length, len);
253                 plist.add (Properties::name, new_name);
254                 plist.add (Properties::layer, region->layer());
255                 plist.add (Properties::layering_index, region->layering_index());
256
257                 new_region = RegionFactory::create (region, plist);
258
259                 add_region_internal (new_region, position);
260         }
261
262         //keep track of any dead space at end (for pasting into Ripple or Splice mode)
263         //at the end of construction, any length of cnt beyond the extents of the regions is end_space
264         _end_space = cnt - (get_extent().second - get_extent().first);
265
266         in_set_state--;
267         first_set_state = false;
268 }
269
270 void
271 Playlist::use ()
272 {
273         ++_refcnt;
274         InUse (true); /* EMIT SIGNAL */
275 }
276
277 void
278 Playlist::release ()
279 {
280         if (_refcnt > 0) {
281                 _refcnt--;
282         }
283
284         if (_refcnt == 0) {
285                 InUse (false); /* EMIT SIGNAL */
286         }
287 }
288
289 void
290 Playlist::copy_regions (RegionList& newlist) const
291 {
292         RegionReadLock rlock (const_cast<Playlist *> (this));
293
294         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
295                 newlist.push_back (RegionFactory::create (*i, true));
296         }
297 }
298
299 void
300 Playlist::init (bool hide)
301 {
302         add_property (regions);
303         _xml_node_name = X_("Playlist");
304
305         g_atomic_int_set (&block_notifications, 0);
306         g_atomic_int_set (&ignore_state_changes, 0);
307         pending_contents_change = false;
308         pending_layering = false;
309         first_set_state = true;
310         _refcnt = 0;
311         _hidden = hide;
312         _splicing = false;
313         _rippling = false;
314         _shuffling = false;
315         _nudging = false;
316         in_set_state = 0;
317         in_undo = false;
318         _edit_mode = Config->get_edit_mode();
319         in_flush = false;
320         in_partition = false;
321         subcnt = 0;
322         _frozen = false;
323         _capture_insertion_underway = false;
324         _combine_ops = 0;
325         _end_space = 0;
326
327         _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
328         _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
329
330         ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
331 }
332
333 Playlist::~Playlist ()
334 {
335         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
336
337         {
338                 RegionReadLock rl (this);
339
340                 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
341                         (*i)->set_playlist (boost::shared_ptr<Playlist>());
342                 }
343         }
344
345         /* GoingAway must be emitted by derived classes */
346 }
347
348 void
349 Playlist::_set_sort_id ()
350 {
351         /*
352           Playlists are given names like <track name>.<id>
353           or <track name>.<edit group name>.<id> where id
354           is an integer. We extract the id and sort by that.
355         */
356
357         size_t dot_position = _name.val().find_last_of(".");
358
359         if (dot_position == string::npos) {
360                 _sort_id = 0;
361         } else {
362                 string t = _name.val().substr(dot_position + 1);
363
364                 try {
365                         _sort_id = boost::lexical_cast<int>(t);
366                 }
367
368                 catch (boost::bad_lexical_cast e) {
369                         _sort_id = 0;
370                 }
371         }
372 }
373
374 bool
375 Playlist::set_name (const string& str)
376 {
377         /* in a typical situation, a playlist is being used
378            by one diskstream and also is referenced by the
379            Session. if there are more references than that,
380            then don't change the name.
381         */
382
383         if (_refcnt > 2) {
384                 return false;
385         }
386
387         bool ret =  SessionObject::set_name(str);
388         if (ret) {
389                 _set_sort_id ();
390         }
391         return ret;
392 }
393
394 /***********************************************************************
395  CHANGE NOTIFICATION HANDLING
396
397  Notifications must be delayed till the region_lock is released. This
398  is necessary because handlers for the signals may need to acquire
399  the lock (e.g. to read from the playlist).
400  ***********************************************************************/
401
402 void
403 Playlist::begin_undo ()
404 {
405         in_undo = true;
406         freeze ();
407 }
408
409 void
410 Playlist::end_undo ()
411 {
412         thaw (true);
413         in_undo = false;
414 }
415
416 void
417 Playlist::freeze ()
418 {
419         delay_notifications ();
420         g_atomic_int_inc (&ignore_state_changes);
421 }
422
423 /** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
424 void
425 Playlist::thaw (bool from_undo)
426 {
427         g_atomic_int_dec_and_test (&ignore_state_changes);
428         release_notifications (from_undo);
429 }
430
431
432 void
433 Playlist::delay_notifications ()
434 {
435         g_atomic_int_inc (&block_notifications);
436 }
437
438 /** @param from_undo true if this release is triggered by the end of an undo on this playlist */
439 void
440 Playlist::release_notifications (bool from_undo)
441 {
442         if (g_atomic_int_dec_and_test (&block_notifications)) {
443                 flush_notifications (from_undo);
444         }
445 }
446
447 void
448 Playlist::notify_contents_changed ()
449 {
450         if (holding_state ()) {
451                 pending_contents_change = true;
452         } else {
453                 pending_contents_change = false;
454                 ContentsChanged(); /* EMIT SIGNAL */
455         }
456 }
457
458 void
459 Playlist::notify_layering_changed ()
460 {
461         if (holding_state ()) {
462                 pending_layering = true;
463         } else {
464                 pending_layering = false;
465                 LayeringChanged(); /* EMIT SIGNAL */
466         }
467 }
468
469 void
470 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
471 {
472         if (holding_state ()) {
473                 pending_removes.insert (r);
474                 pending_contents_change = true;
475         } else {
476                 /* this might not be true, but we have to act
477                    as though it could be.
478                 */
479                 pending_contents_change = false;
480                 RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
481                 ContentsChanged (); /* EMIT SIGNAL */
482         }
483 }
484
485 void
486 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
487 {
488         Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
489
490         if (holding_state ()) {
491
492                 pending_range_moves.push_back (move);
493
494         } else {
495
496                 list< Evoral::RangeMove<framepos_t> > m;
497                 m.push_back (move);
498                 RangesMoved (m, false);
499         }
500
501 }
502
503 void
504 Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
505 {
506         if (r->position() >= r->last_position()) {
507                 /* trimmed shorter */
508                 return;
509         }
510
511         Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
512
513         if (holding_state ()) {
514
515                 pending_region_extensions.push_back (extra);
516
517         } else {
518
519                 list<Evoral::Range<framepos_t> > r;
520                 r.push_back (extra);
521                 RegionsExtended (r);
522
523         }
524 }
525
526 void
527 Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
528 {
529         if (r->length() < r->last_length()) {
530                 /* trimmed shorter */
531         }
532
533         Evoral::Range<framepos_t> const extra (r->position() + r->last_length(), r->position() + r->length());
534
535         if (holding_state ()) {
536
537                 pending_region_extensions.push_back (extra);
538
539         } else {
540
541                 list<Evoral::Range<framepos_t> > r;
542                 r.push_back (extra);
543                 RegionsExtended (r);
544         }
545 }
546
547
548 void
549 Playlist::notify_region_added (boost::shared_ptr<Region> r)
550 {
551         /* the length change might not be true, but we have to act
552            as though it could be.
553         */
554
555         if (holding_state()) {
556                 pending_adds.insert (r);
557                 pending_contents_change = true;
558         } else {
559                 r->clear_changes ();
560                 pending_contents_change = false;
561                 RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
562                 ContentsChanged (); /* EMIT SIGNAL */
563
564         }
565 }
566
567 /** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
568 void
569 Playlist::flush_notifications (bool from_undo)
570 {
571         set<boost::shared_ptr<Region> >::iterator s;
572         bool regions_changed = false;
573
574         if (in_flush) {
575                 return;
576         }
577
578         in_flush = true;
579
580         if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
581                 regions_changed = true;
582         }
583
584         /* XXX: it'd be nice if we could use pending_bounds for
585            RegionsExtended and RegionsMoved.
586         */
587
588         /* we have no idea what order the regions ended up in pending
589            bounds (it could be based on selection order, for example).
590            so, to preserve layering in the "most recently moved is higher"
591            model, sort them by existing layer, then timestamp them.
592         */
593
594         // RegionSortByLayer cmp;
595         // pending_bounds.sort (cmp);
596
597         list<Evoral::Range<framepos_t> > crossfade_ranges;
598
599         for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
600                 crossfade_ranges.push_back ((*r)->last_range ());
601                 crossfade_ranges.push_back ((*r)->range ());
602         }
603
604         for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
605                 crossfade_ranges.push_back ((*s)->range ());
606                 remove_dependents (*s);
607                 RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
608         }
609
610         for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
611                 crossfade_ranges.push_back ((*s)->range ());
612                 /* don't emit RegionAdded signal until relayering is done,
613                    so that the region is fully setup by the time
614                    anyone hears that its been added
615                 */
616         }
617
618         /* notify about contents/region changes first so that layering changes
619          * in a UI will take place on the new contents.
620          */
621
622         if (regions_changed || pending_contents_change) {
623                 pending_layering = true;
624                 ContentsChanged (); /* EMIT SIGNAL */
625         }
626
627         for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
628                 (*s)->clear_changes ();
629                 RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
630         }
631
632         if ((regions_changed && !in_set_state) || pending_layering) {
633                 relayer ();
634         }
635
636         coalesce_and_check_crossfades (crossfade_ranges);
637
638         if (!pending_range_moves.empty ()) {
639                 /* We don't need to check crossfades for these as pending_bounds has
640                    already covered it.
641                 */
642                 RangesMoved (pending_range_moves, from_undo);
643         }
644
645         if (!pending_region_extensions.empty ()) {
646                 RegionsExtended (pending_region_extensions);
647         }
648
649         clear_pending ();
650
651         in_flush = false;
652 }
653
654  void
655  Playlist::clear_pending ()
656  {
657          pending_adds.clear ();
658          pending_removes.clear ();
659          pending_bounds.clear ();
660          pending_range_moves.clear ();
661          pending_region_extensions.clear ();
662          pending_contents_change = false;
663          pending_layering = false;
664  }
665
666  /*************************************************************
667    PLAYLIST OPERATIONS
668   *************************************************************/
669
670 /** Note: this calls set_layer (..., DBL_MAX) so it will reset the layering index of region */
671  void
672  Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition, const int32_t sub_num)
673  {
674          RegionWriteLock rlock (this);
675          times = fabs (times);
676
677          int itimes = (int) floor (times);
678
679          framepos_t pos = position;
680
681          if (times == 1 && auto_partition){
682                 RegionList thawlist;
683                 partition_internal (pos - 1, (pos + region->length()), true, thawlist);
684                 for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
685                         (*i)->resume_property_changes ();
686                         _session.add_command (new StatefulDiffCommand (*i));
687                 }
688          }
689
690          if (itimes >= 1) {
691                  add_region_internal (region, pos, sub_num);
692                  set_layer (region, DBL_MAX);
693                  pos += region->length();
694                  --itimes;
695          }
696
697
698          /* note that itimes can be zero if we being asked to just
699             insert a single fraction of the region.
700          */
701
702          for (int i = 0; i < itimes; ++i) {
703                  boost::shared_ptr<Region> copy = RegionFactory::create (region, true, sub_num);
704                  add_region_internal (copy, pos, sub_num);
705                  set_layer (copy, DBL_MAX);
706                  pos += region->length();
707          }
708
709          framecnt_t length = 0;
710
711          if (floor (times) != times) {
712                  length = (framecnt_t) floor (region->length() * (times - floor (times)));
713                  string name;
714                  RegionFactory::region_name (name, region->name(), false);
715
716                  {
717                          PropertyList plist;
718
719                          plist.add (Properties::start, region->start());
720                          plist.add (Properties::length, length);
721                          plist.add (Properties::name, name);
722                          plist.add (Properties::layer, region->layer());
723
724                          boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
725                          add_region_internal (sub, pos, sub_num);
726                          set_layer (sub, DBL_MAX);
727                  }
728          }
729
730          possibly_splice_unlocked (position, (pos + length) - position, region);
731  }
732
733  void
734  Playlist::set_region_ownership ()
735  {
736          RegionWriteLock rl (this);
737          RegionList::iterator i;
738          boost::weak_ptr<Playlist> pl (shared_from_this());
739
740          for (i = regions.begin(); i != regions.end(); ++i) {
741                  (*i)->set_playlist (pl);
742          }
743  }
744
745  bool
746  Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position, const int32_t sub_num)
747  {
748          if (region->data_type() != _type) {
749                  return false;
750          }
751
752          RegionSortByPosition cmp;
753
754          if (!first_set_state) {
755                  boost::shared_ptr<Playlist> foo (shared_from_this());
756                  region->set_playlist (boost::weak_ptr<Playlist>(foo));
757          }
758
759          region->set_position (position, sub_num);
760
761          regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
762          all_regions.insert (region);
763
764          possibly_splice_unlocked (position, region->length(), region);
765
766          if (!holding_state ()) {
767                  /* layers get assigned from XML state, and are not reset during undo/redo */
768                  relayer ();
769          }
770
771          /* we need to notify the existence of new region before checking dependents. Ick. */
772
773          notify_region_added (region);
774
775          region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
776          region->DropReferences.connect_same_thread (region_drop_references_connections, boost::bind (&Playlist::region_going_away, this, boost::weak_ptr<Region> (region)));
777
778          return true;
779  }
780
781  void
782  Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
783  {
784          RegionWriteLock rlock (this);
785
786          bool old_sp = _splicing;
787          _splicing = true;
788
789          remove_region_internal (old);
790          add_region_internal (newr, pos);
791          set_layer (newr, old->layer ());
792
793          _splicing = old_sp;
794
795          possibly_splice_unlocked (pos, old->length() - newr->length());
796  }
797
798  void
799  Playlist::remove_region (boost::shared_ptr<Region> region)
800  {
801          RegionWriteLock rlock (this);
802          remove_region_internal (region);
803  }
804
805  int
806  Playlist::remove_region_internal (boost::shared_ptr<Region> region)
807  {
808          RegionList::iterator i;
809
810          if (!in_set_state) {
811                  /* unset playlist */
812                  region->set_playlist (boost::weak_ptr<Playlist>());
813          }
814
815          /* XXX should probably freeze here .... */
816
817          for (i = regions.begin(); i != regions.end(); ++i) {
818                  if (*i == region) {
819
820                          framepos_t pos = (*i)->position();
821                          framecnt_t distance = (*i)->length();
822
823                          regions.erase (i);
824
825                          possibly_splice_unlocked (pos, -distance);
826
827                          if (!holding_state ()) {
828                                  relayer ();
829                                  remove_dependents (region);
830                          }
831
832                          notify_region_removed (region);
833                          break;
834                  }
835          }
836
837          return -1;
838  }
839
840  void
841  Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
842  {
843          if (Config->get_use_overlap_equivalency()) {
844                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
845                          if ((*i)->overlap_equivalent (other)) {
846                                  results.push_back (*i);
847                          }
848                  }
849          } else {
850                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
851                          if ((*i)->equivalent (other)) {
852                                  results.push_back (*i);
853                          }
854                  }
855          }
856  }
857
858  void
859  Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
860  {
861          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
862
863                  if ((*i) && (*i)->region_list_equivalent (other)) {
864                          results.push_back (*i);
865                  }
866          }
867  }
868
869  void
870  Playlist::get_source_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
871  {
872          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
873
874                  if ((*i) && (*i)->any_source_equivalent (other)) {
875                          results.push_back (*i);
876                  }
877          }
878  }
879
880  void
881  Playlist::partition (framepos_t start, framepos_t end, bool cut)
882  {
883          RegionList thawlist;
884          {
885           RegionWriteLock lock(this);
886           partition_internal (start, end, cut, thawlist);
887          }
888
889          for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
890                  (*i)->resume_property_changes ();
891          }
892  }
893
894 /** Go through each region on the playlist and cut them at start and end, removing the section between
895  *  start and end if cutting == true.  Regions that lie entirely within start and end are always
896  *  removed.
897  */
898
899  void
900  Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
901  {
902          RegionList new_regions;
903
904          {
905
906                  boost::shared_ptr<Region> region;
907                  boost::shared_ptr<Region> current;
908                  string new_name;
909                  RegionList::iterator tmp;
910                  Evoral::OverlapType overlap;
911                  framepos_t pos1, pos2, pos3, pos4;
912
913                  in_partition = true;
914
915                  /* need to work from a copy, because otherwise the regions we add during the process
916                     get operated on as well.
917                  */
918
919                  RegionList copy = regions.rlist();
920
921                  for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
922
923                          tmp = i;
924                          ++tmp;
925
926                          current = *i;
927
928                          if (current->first_frame() >= start && current->last_frame() < end) {
929
930                                  if (cutting) {
931                                          remove_region_internal (current);
932                                  }
933
934                                  continue;
935                          }
936
937                          /* coverage will return OverlapStart if the start coincides
938                             with the end point. we do not partition such a region,
939                             so catch this special case.
940                          */
941
942                          if (current->first_frame() >= end) {
943                                  continue;
944                          }
945
946                          if ((overlap = current->coverage (start, end)) == Evoral::OverlapNone) {
947                                  continue;
948                          }
949
950                          pos1 = current->position();
951                          pos2 = start;
952                          pos3 = end;
953                          pos4 = current->last_frame();
954
955                          if (overlap == Evoral::OverlapInternal) {
956                                  /* split: we need 3 new regions, the front, middle and end.
957                                     cut:   we need 2 regions, the front and end.
958                                  */
959
960                                  /*
961                                           start                 end
962                            ---------------*************************------------
963                                           P1  P2              P3  P4
964                            SPLIT:
965                            ---------------*****++++++++++++++++====------------
966                            CUT
967                            ---------------*****----------------====------------
968
969                                  */
970
971                                  if (!cutting) {
972                                          /* "middle" ++++++ */
973
974                                          RegionFactory::region_name (new_name, current->name(), false);
975
976                                          PropertyList plist;
977
978                                          plist.add (Properties::start, current->start() + (pos2 - pos1));
979                                          plist.add (Properties::length, pos3 - pos2);
980                                          plist.add (Properties::name, new_name);
981                                          plist.add (Properties::layer, current->layer ());
982                                          plist.add (Properties::layering_index, current->layering_index ());
983                                          plist.add (Properties::automatic, true);
984                                          plist.add (Properties::left_of_split, true);
985                                          plist.add (Properties::right_of_split, true);
986
987                                          region = RegionFactory::create (current, plist);
988                                          add_region_internal (region, start);
989                                          new_regions.push_back (region);
990                                  }
991
992                                  /* "end" ====== */
993
994                                  RegionFactory::region_name (new_name, current->name(), false);
995
996                                  PropertyList plist;
997
998                                  plist.add (Properties::start, current->start() + (pos3 - pos1));
999                                  plist.add (Properties::length, pos4 - pos3);
1000                                  plist.add (Properties::name, new_name);
1001                                  plist.add (Properties::layer, current->layer ());
1002                                  plist.add (Properties::layering_index, current->layering_index ());
1003                                  plist.add (Properties::automatic, true);
1004                                  plist.add (Properties::right_of_split, true);
1005
1006                                  region = RegionFactory::create (current, plist);
1007
1008                                  add_region_internal (region, end);
1009                                  new_regions.push_back (region);
1010
1011                                  /* "front" ***** */
1012
1013                                  current->clear_changes ();
1014                                  current->suspend_property_changes ();
1015                                  thawlist.push_back (current);
1016                                  current->cut_end (pos2 - 1);
1017
1018                          } else if (overlap == Evoral::OverlapEnd) {
1019
1020                                  /*
1021                                                                start           end
1022                                      ---------------*************************------------
1023                                                     P1           P2         P4   P3
1024                                      SPLIT:
1025                                      ---------------**************+++++++++++------------
1026                                      CUT:
1027                                      ---------------**************-----------------------
1028                                  */
1029
1030                                  if (!cutting) {
1031
1032                                          /* end +++++ */
1033
1034                                          RegionFactory::region_name (new_name, current->name(), false);
1035
1036                                          PropertyList plist;
1037
1038                                          plist.add (Properties::start, current->start() + (pos2 - pos1));
1039                                          plist.add (Properties::length, pos4 - pos2);
1040                                          plist.add (Properties::name, new_name);
1041                                          plist.add (Properties::layer, current->layer ());
1042                                          plist.add (Properties::layering_index, current->layering_index ());
1043                                          plist.add (Properties::automatic, true);
1044                                          plist.add (Properties::left_of_split, true);
1045
1046                                          region = RegionFactory::create (current, plist);
1047
1048                                          add_region_internal (region, start);
1049                                          new_regions.push_back (region);
1050                                  }
1051
1052                                  /* front ****** */
1053
1054                                  current->clear_changes ();
1055                                  current->suspend_property_changes ();
1056                                  thawlist.push_back (current);
1057                                  current->cut_end (pos2 - 1);
1058
1059                          } else if (overlap == Evoral::OverlapStart) {
1060
1061                                  /* split: we need 2 regions: the front and the end.
1062                                     cut: just trim current to skip the cut area
1063                                  */
1064
1065                                  /*
1066                                                          start           end
1067                                      ---------------*************************------------
1068                                         P2          P1 P3                   P4
1069
1070                                      SPLIT:
1071                                      ---------------****+++++++++++++++++++++------------
1072                                      CUT:
1073                                      -------------------*********************------------
1074
1075                                  */
1076
1077                                  if (!cutting) {
1078                                          /* front **** */
1079                                          RegionFactory::region_name (new_name, current->name(), false);
1080
1081                                          PropertyList plist;
1082
1083                                          plist.add (Properties::start, current->start());
1084                                          plist.add (Properties::length, pos3 - pos1);
1085                                          plist.add (Properties::name, new_name);
1086                                          plist.add (Properties::layer, current->layer ());
1087                                          plist.add (Properties::layering_index, current->layering_index ());
1088                                          plist.add (Properties::automatic, true);
1089                                          plist.add (Properties::right_of_split, true);
1090
1091                                          region = RegionFactory::create (current, plist);
1092
1093                                          add_region_internal (region, pos1);
1094                                          new_regions.push_back (region);
1095                                  }
1096
1097                                  /* end */
1098
1099                                  current->clear_changes ();
1100                                  current->suspend_property_changes ();
1101                                  thawlist.push_back (current);
1102                                  current->trim_front (pos3);
1103                          } else if (overlap == Evoral::OverlapExternal) {
1104
1105                                  /* split: no split required.
1106                                     cut: remove the region.
1107                                  */
1108
1109                                  /*
1110                                         start                                      end
1111                                      ---------------*************************------------
1112                                         P2          P1 P3                   P4
1113
1114                                      SPLIT:
1115                                      ---------------*************************------------
1116                                      CUT:
1117                                      ----------------------------------------------------
1118
1119                                  */
1120
1121                                  if (cutting) {
1122                                          remove_region_internal (current);
1123                                  }
1124
1125                                  new_regions.push_back (current);
1126                          }
1127                  }
1128
1129                  in_partition = false;
1130          }
1131
1132         //keep track of any dead space at end (for pasting into Ripple or Splice mode)
1133         framepos_t wanted_length = end-start;
1134         _end_space = wanted_length - _get_extent().second - _get_extent().first;
1135  }
1136
1137  boost::shared_ptr<Playlist>
1138  Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
1139  {
1140          boost::shared_ptr<Playlist> ret;
1141          boost::shared_ptr<Playlist> pl;
1142          framepos_t start;
1143
1144          if (ranges.empty()) {
1145                  return boost::shared_ptr<Playlist>();
1146          }
1147
1148          start = ranges.front().start;
1149
1150          for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
1151
1152                  pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
1153
1154                  if (i == ranges.begin()) {
1155                          ret = pl;
1156                  } else {
1157
1158                          /* paste the next section into the nascent playlist,
1159                             offset to reflect the start of the first range we
1160                             chopped.
1161                          */
1162
1163                          ret->paste (pl, (*i).start - start, 1.0f, 0);
1164                  }
1165          }
1166
1167          return ret;
1168  }
1169
1170  boost::shared_ptr<Playlist>
1171  Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
1172  {
1173          boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
1174          return cut_copy (pmf, ranges, result_is_hidden);
1175  }
1176
1177  boost::shared_ptr<Playlist>
1178  Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
1179  {
1180          boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
1181          return cut_copy (pmf, ranges, result_is_hidden);
1182  }
1183
1184  boost::shared_ptr<Playlist>
1185  Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1186  {
1187          boost::shared_ptr<Playlist> the_copy;
1188          RegionList thawlist;
1189          char buf[32];
1190
1191          snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1192          string new_name = _name;
1193          new_name += '.';
1194          new_name += buf;
1195
1196          if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
1197                  return boost::shared_ptr<Playlist>();
1198          }
1199
1200          {
1201                 RegionWriteLock rlock (this);
1202                 partition_internal (start, start+cnt-1, true, thawlist);
1203          }
1204
1205          for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
1206                  (*i)->resume_property_changes();
1207          }
1208
1209          return the_copy;
1210  }
1211
1212  boost::shared_ptr<Playlist>
1213  Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
1214  {
1215          char buf[32];
1216
1217          snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
1218          string new_name = _name;
1219          new_name += '.';
1220          new_name += buf;
1221
1222         // cnt = min (_get_extent().second - start, cnt);  (We need the full range length when copy/pasting in Ripple.  Why was this limit here?  It's not in CUT... )
1223
1224          return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
1225  }
1226
1227  int
1228  Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times, const int32_t sub_num)
1229  {
1230          times = fabs (times);
1231
1232          {
1233                  RegionReadLock rl2 (other.get());
1234
1235                  int itimes = (int) floor (times);
1236                  framepos_t pos = position;
1237                  framecnt_t const shift = other->_get_extent().second;
1238                  layer_t top = top_layer ();
1239
1240                  {
1241                          RegionWriteLock rl1 (this);
1242                          while (itimes--) {
1243                                  for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
1244                                          boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
1245
1246                                          /* put these new regions on top of all existing ones, but preserve
1247                                             the ordering they had in the original playlist.
1248                                          */
1249
1250                                          add_region_internal (copy_of_region, (*i)->position() + pos, sub_num);
1251                                          set_layer (copy_of_region, copy_of_region->layer() + top);
1252                                  }
1253                                  pos += shift;
1254                          }
1255                  }
1256          }
1257
1258          return 0;
1259  }
1260
1261
1262  void
1263  Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
1264  {
1265          duplicate(region, position, region->length(), times);
1266  }
1267
1268 /** @param gap from the beginning of the region to the next beginning */
1269  void
1270  Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, float times)
1271  {
1272          times = fabs (times);
1273
1274          RegionWriteLock rl (this);
1275          int itimes = (int) floor (times);
1276
1277          while (itimes--) {
1278                  boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1279                  add_region_internal (copy, position);
1280                  set_layer (copy, DBL_MAX);
1281                  position += gap;
1282          }
1283
1284          if (floor (times) != times) {
1285                  framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1286                  string name;
1287                  RegionFactory::region_name (name, region->name(), false);
1288
1289                  {
1290                          PropertyList plist;
1291
1292                          plist.add (Properties::start, region->start());
1293                          plist.add (Properties::length, length);
1294                          plist.add (Properties::name, name);
1295
1296                          boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1297                          add_region_internal (sub, position);
1298                          set_layer (sub, DBL_MAX);
1299                  }
1300          }
1301  }
1302
1303 /** @param gap from the beginning of the region to the next beginning */
1304 /** @param end the first frame that does _not_ contain a duplicated frame */
1305 void
1306 Playlist::duplicate_until (boost::shared_ptr<Region> region, framepos_t position, framecnt_t gap, framepos_t end)
1307 {
1308          RegionWriteLock rl (this);
1309
1310          while (position + region->length() - 1 < end) {
1311                  boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1312                  add_region_internal (copy, position);
1313                  set_layer (copy, DBL_MAX);
1314                  position += gap;
1315          }
1316
1317          if (position < end) {
1318                  framecnt_t length = min (region->length(), end - position);
1319                  string name;
1320                  RegionFactory::region_name (name, region->name(), false);
1321
1322                  {
1323                          PropertyList plist;
1324
1325                          plist.add (Properties::start, region->start());
1326                          plist.add (Properties::length, length);
1327                          plist.add (Properties::name, name);
1328
1329                          boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1330                          add_region_internal (sub, position);
1331                          set_layer (sub, DBL_MAX);
1332                  }
1333          }
1334 }
1335
1336 void
1337 Playlist::duplicate_range (AudioRange& range, float times)
1338 {
1339         boost::shared_ptr<Playlist> pl = copy (range.start, range.length(), true);
1340         framecnt_t offset = range.end - range.start;
1341         paste (pl, range.start + offset, times, 0);
1342 }
1343
1344 void
1345 Playlist::duplicate_ranges (std::list<AudioRange>& ranges, float times)
1346 {
1347         if (ranges.empty()) {
1348                 return;
1349         }
1350
1351         framepos_t min_pos = max_framepos;
1352         framepos_t max_pos = 0;
1353
1354         for (std::list<AudioRange>::const_iterator i = ranges.begin();
1355              i != ranges.end();
1356              ++i) {
1357                 min_pos = min (min_pos, (*i).start);
1358                 max_pos = max (max_pos, (*i).end);
1359         }
1360
1361         framecnt_t offset = max_pos - min_pos;
1362
1363         int count = 1;
1364         int itimes = (int) floor (times);
1365         while (itimes--) {
1366                 for (list<AudioRange>::iterator i = ranges.begin (); i != ranges.end (); ++i) {
1367                         boost::shared_ptr<Playlist> pl = copy ((*i).start, (*i).length (), true);
1368                         paste (pl, (*i).start + (offset * count), 1.0f, 0);
1369                 }
1370                 ++count;
1371         }
1372 }
1373
1374  void
1375  Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1376  {
1377          RegionWriteLock rlock (this);
1378          RegionList copy (regions.rlist());
1379          RegionList fixup;
1380
1381          for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1382
1383                  if ((*r)->last_frame() < at) {
1384                          /* too early */
1385                          continue;
1386                  }
1387
1388                  if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1389                          /* intersected region */
1390                          if (!move_intersected) {
1391                                  continue;
1392                          }
1393                  }
1394
1395                  /* do not move regions glued to music time - that
1396                     has to be done separately.
1397                  */
1398
1399                  if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1400                          fixup.push_back (*r);
1401                          continue;
1402                  }
1403
1404                  (*r)->set_position ((*r)->position() + distance);
1405          }
1406
1407          /* XXX: may not be necessary; Region::post_set should do this, I think */
1408          for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1409                  (*r)->recompute_position_from_lock_style (0);
1410          }
1411  }
1412
1413  void
1414  Playlist::split (framepos_t at, const int32_t sub_num)
1415  {
1416          RegionWriteLock rlock (this);
1417          RegionList copy (regions.rlist());
1418
1419          /* use a copy since this operation can modify the region list
1420           */
1421
1422          for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1423                  _split_region (*r, at, sub_num);
1424          }
1425  }
1426
1427  void
1428  Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1429  {
1430          RegionWriteLock rl (this);
1431          _split_region (region, playlist_position, sub_num);
1432  }
1433
1434  void
1435  Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position, const int32_t sub_num)
1436  {
1437          if (!region->covers (playlist_position)) {
1438                  return;
1439          }
1440
1441          if (region->position() == playlist_position ||
1442              region->last_frame() == playlist_position) {
1443                  return;
1444          }
1445
1446          boost::shared_ptr<Region> left;
1447          boost::shared_ptr<Region> right;
1448          frameoffset_t before;
1449          frameoffset_t after;
1450          string before_name;
1451          string after_name;
1452
1453          /* split doesn't change anything about length, so don't try to splice */
1454
1455          bool old_sp = _splicing;
1456          _splicing = true;
1457
1458          before = playlist_position - region->position();
1459          after = region->length() - before;
1460
1461          RegionFactory::region_name (before_name, region->name(), false);
1462
1463          {
1464                  PropertyList plist;
1465
1466                  plist.add (Properties::length, before);
1467                  plist.add (Properties::name, before_name);
1468                  plist.add (Properties::left_of_split, true);
1469                  plist.add (Properties::layering_index, region->layering_index ());
1470                  plist.add (Properties::layer, region->layer ());
1471
1472                  /* note: we must use the version of ::create with an offset here,
1473                     since it supplies that offset to the Region constructor, which
1474                     is necessary to get audio region gain envelopes right.
1475                  */
1476                  left = RegionFactory::create (region, 0, plist, true, sub_num);
1477          }
1478
1479          RegionFactory::region_name (after_name, region->name(), false);
1480
1481          {
1482                  PropertyList plist;
1483
1484                  plist.add (Properties::length, after);
1485                  plist.add (Properties::name, after_name);
1486                  plist.add (Properties::right_of_split, true);
1487                  plist.add (Properties::layering_index, region->layering_index ());
1488                  plist.add (Properties::layer, region->layer ());
1489
1490                  /* same note as above */
1491                  right = RegionFactory::create (region, before, plist, true, sub_num);
1492          }
1493
1494          add_region_internal (left, region->position());
1495          add_region_internal (right, region->position() + before);
1496          remove_region_internal (region);
1497
1498          _splicing = old_sp;
1499  }
1500
1501  void
1502  Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1503  {
1504          if (_splicing || in_set_state) {
1505                  /* don't respond to splicing moves or state setting */
1506                  return;
1507          }
1508
1509          if (_edit_mode == Splice) {
1510                  splice_locked (at, distance, exclude);
1511          }
1512  }
1513
1514  void
1515  Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1516  {
1517          if (_splicing || in_set_state) {
1518                  /* don't respond to splicing moves or state setting */
1519                  return;
1520          }
1521
1522          if (_edit_mode == Splice) {
1523                  splice_unlocked (at, distance, exclude);
1524          }
1525  }
1526
1527  void
1528  Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1529  {
1530          {
1531                  RegionWriteLock rl (this);
1532                  core_splice (at, distance, exclude);
1533          }
1534  }
1535
1536  void
1537  Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1538  {
1539          core_splice (at, distance, exclude);
1540  }
1541
1542  void
1543  Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1544  {
1545          _splicing = true;
1546
1547          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1548
1549                  if (exclude && (*i) == exclude) {
1550                          continue;
1551                  }
1552
1553                  if ((*i)->position() >= at) {
1554                          framepos_t new_pos = (*i)->position() + distance;
1555                          if (new_pos < 0) {
1556                                  new_pos = 0;
1557                          } else if (new_pos >= max_framepos - (*i)->length()) {
1558                                  new_pos = max_framepos - (*i)->length();
1559                          }
1560
1561                          (*i)->set_position (new_pos);
1562                  }
1563          }
1564
1565          _splicing = false;
1566
1567          notify_contents_changed ();
1568 }
1569
1570 void
1571 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1572 {
1573         {
1574                 RegionWriteLock rl (this);
1575                 core_ripple (at, distance, exclude);
1576         }
1577 }
1578
1579 void
1580 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1581 {
1582         core_ripple (at, distance, exclude);
1583 }
1584
1585 void
1586 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1587 {
1588         if (distance == 0) {
1589                 return;
1590         }
1591
1592         _rippling = true;
1593         RegionListProperty copy = regions;
1594         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1595                 assert (i != copy.end());
1596
1597                 if (exclude) {
1598                         if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1599                                 continue;
1600                         }
1601                 }
1602
1603                 if ((*i)->position() >= at) {
1604                         framepos_t new_pos = (*i)->position() + distance;
1605                         framepos_t limit = max_framepos - (*i)->length();
1606                         if (new_pos < 0) {
1607                                 new_pos = 0;
1608                         } else if (new_pos >= limit ) {
1609                                 new_pos = limit;
1610                         }
1611
1612                         (*i)->set_position (new_pos);
1613                 }
1614         }
1615
1616         _rippling = false;
1617         notify_contents_changed ();
1618 }
1619
1620
1621 void
1622 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1623 {
1624          if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1625                  return;
1626          }
1627
1628          if (what_changed.contains (Properties::position)) {
1629
1630                  /* remove it from the list then add it back in
1631                     the right place again.
1632                  */
1633
1634                  RegionSortByPosition cmp;
1635
1636                  RegionList::iterator i = find (regions.begin(), regions.end(), region);
1637
1638                  if (i == regions.end()) {
1639                          /* the region bounds are being modified but its not currently
1640                             in the region list. we will use its bounds correctly when/if
1641                             it is added
1642                          */
1643                          return;
1644                  }
1645
1646                  regions.erase (i);
1647                  regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1648          }
1649
1650          if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1651
1652                  frameoffset_t delta = 0;
1653
1654                  if (what_changed.contains (Properties::position)) {
1655                          delta = region->position() - region->last_position();
1656                  }
1657
1658                  if (what_changed.contains (Properties::length)) {
1659                          delta += region->length() - region->last_length();
1660                  }
1661
1662                  if (delta) {
1663                          possibly_splice (region->last_position() + region->last_length(), delta, region);
1664                  }
1665
1666                  if (holding_state ()) {
1667                          pending_bounds.push_back (region);
1668                  } else {
1669                          notify_contents_changed ();
1670                          relayer ();
1671                          list<Evoral::Range<framepos_t> > xf;
1672                          xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1673                          xf.push_back (Evoral::Range<framepos_t> (region->range()));
1674                          coalesce_and_check_crossfades (xf);
1675                  }
1676          }
1677  }
1678
1679  void
1680  Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1681  {
1682          boost::shared_ptr<Region> region (weak_region.lock());
1683
1684          if (!region) {
1685                  return;
1686          }
1687
1688          /* this makes a virtual call to the right kind of playlist ... */
1689
1690          region_changed (what_changed, region);
1691  }
1692
1693  bool
1694  Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1695  {
1696          PropertyChange our_interests;
1697          PropertyChange bounds;
1698          PropertyChange pos_and_length;
1699          bool save = false;
1700
1701          if (in_set_state || in_flush) {
1702                  return false;
1703          }
1704
1705          our_interests.add (Properties::muted);
1706          our_interests.add (Properties::layer);
1707          our_interests.add (Properties::opaque);
1708
1709          bounds.add (Properties::start);
1710          bounds.add (Properties::position);
1711          bounds.add (Properties::length);
1712
1713          pos_and_length.add (Properties::position);
1714          pos_and_length.add (Properties::length);
1715
1716          if (what_changed.contains (bounds)) {
1717                  region_bounds_changed (what_changed, region);
1718                  save = !(_splicing || _nudging);
1719          }
1720
1721          if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1722                  notify_region_moved (region);
1723          } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1724                  notify_region_end_trimmed (region);
1725          } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1726                  notify_region_start_trimmed (region);
1727          }
1728
1729          /* don't notify about layer changes, since we are the only object that can initiate
1730             them, and we notify in ::relayer()
1731          */
1732
1733          if (what_changed.contains (our_interests)) {
1734                  save = true;
1735          }
1736
1737          mark_session_dirty ();
1738
1739          return save;
1740  }
1741
1742  void
1743  Playlist::drop_regions ()
1744  {
1745          RegionWriteLock rl (this);
1746          regions.clear ();
1747          all_regions.clear ();
1748  }
1749
1750  void
1751  Playlist::sync_all_regions_with_regions ()
1752  {
1753          RegionWriteLock rl (this);
1754
1755          all_regions.clear ();
1756
1757          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1758                  all_regions.insert (*i);
1759          }
1760  }
1761
1762  void
1763  Playlist::clear (bool with_signals)
1764  {
1765          {
1766                  RegionWriteLock rl (this);
1767
1768                  region_state_changed_connections.drop_connections ();
1769                  region_drop_references_connections.drop_connections ();
1770
1771                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1772                          pending_removes.insert (*i);
1773                  }
1774
1775                  regions.clear ();
1776
1777                  for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1778                          remove_dependents (*s);
1779                  }
1780          }
1781
1782          if (with_signals) {
1783
1784                  for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1785                          RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1786                  }
1787
1788                  pending_removes.clear ();
1789                  pending_contents_change = false;
1790                  ContentsChanged ();
1791          }
1792
1793  }
1794
1795  /* *********************************************************************
1796   FINDING THINGS
1797   **********************************************************************/
1798
1799 boost::shared_ptr<RegionList>
1800 Playlist::region_list()
1801 {
1802         RegionReadLock rlock (this);
1803         boost::shared_ptr<RegionList> rlist (new RegionList (regions.rlist ()));
1804         return rlist;
1805 }
1806
1807 void
1808 Playlist::deep_sources (std::set<boost::shared_ptr<Source> >& sources) const
1809 {
1810         RegionReadLock rlock (const_cast<Playlist*>(this));
1811
1812         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1813                 (*i)->deep_sources (sources);
1814         }
1815 }
1816
1817 boost::shared_ptr<RegionList>
1818 Playlist::regions_at (framepos_t frame)
1819 {
1820         RegionReadLock rlock (this);
1821         return find_regions_at (frame);
1822 }
1823
1824  uint32_t
1825  Playlist::count_regions_at (framepos_t frame) const
1826  {
1827          RegionReadLock rlock (const_cast<Playlist*>(this));
1828          uint32_t cnt = 0;
1829
1830          for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1831                  if ((*i)->covers (frame)) {
1832                          cnt++;
1833                  }
1834          }
1835
1836          return cnt;
1837  }
1838
1839  boost::shared_ptr<Region>
1840  Playlist::top_region_at (framepos_t frame)
1841
1842  {
1843          RegionReadLock rlock (this);
1844          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1845          boost::shared_ptr<Region> region;
1846
1847          if (rlist->size()) {
1848                  RegionSortByLayer cmp;
1849                  rlist->sort (cmp);
1850                  region = rlist->back();
1851          }
1852
1853          return region;
1854  }
1855
1856  boost::shared_ptr<Region>
1857  Playlist::top_unmuted_region_at (framepos_t frame)
1858
1859  {
1860          RegionReadLock rlock (this);
1861          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1862
1863          for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1864
1865                  RegionList::iterator tmp = i;
1866
1867                  ++tmp;
1868
1869                  if ((*i)->muted()) {
1870                          rlist->erase (i);
1871                  }
1872
1873                  i = tmp;
1874          }
1875
1876          boost::shared_ptr<Region> region;
1877
1878          if (rlist->size()) {
1879                  RegionSortByLayer cmp;
1880                  rlist->sort (cmp);
1881                  region = rlist->back();
1882          }
1883
1884          return region;
1885  }
1886
1887 boost::shared_ptr<RegionList>
1888 Playlist::find_regions_at (framepos_t frame)
1889 {
1890         /* Caller must hold lock */
1891
1892         boost::shared_ptr<RegionList> rlist (new RegionList);
1893
1894         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1895                 if ((*i)->covers (frame)) {
1896                         rlist->push_back (*i);
1897                 }
1898         }
1899
1900         return rlist;
1901 }
1902
1903 boost::shared_ptr<RegionList>
1904 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1905 {
1906         RegionReadLock rlock (this);
1907         boost::shared_ptr<RegionList> rlist (new RegionList);
1908
1909         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1910                 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1911                         rlist->push_back (*i);
1912                 }
1913         }
1914
1915         return rlist;
1916 }
1917
1918 boost::shared_ptr<RegionList>
1919 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1920 {
1921         RegionReadLock rlock (this);
1922         boost::shared_ptr<RegionList> rlist (new RegionList);
1923
1924         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1925                 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1926                         rlist->push_back (*i);
1927                 }
1928         }
1929
1930         return rlist;
1931 }
1932
1933 /** @param start Range start.
1934  *  @param end Range end.
1935  *  @return regions which have some part within this range.
1936  */
1937 boost::shared_ptr<RegionList>
1938 Playlist::regions_touched (framepos_t start, framepos_t end)
1939 {
1940         RegionReadLock rlock (this);
1941         return regions_touched_locked (start, end);
1942 }
1943
1944 boost::shared_ptr<RegionList>
1945 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1946 {
1947         boost::shared_ptr<RegionList> rlist (new RegionList);
1948
1949         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1950                 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1951                         rlist->push_back (*i);
1952                 }
1953         }
1954
1955         return rlist;
1956 }
1957
1958 framepos_t
1959 Playlist::find_next_transient (framepos_t from, int dir)
1960 {
1961         RegionReadLock rlock (this);
1962         AnalysisFeatureList points;
1963         AnalysisFeatureList these_points;
1964
1965         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1966                 if (dir > 0) {
1967                         if ((*i)->last_frame() < from) {
1968                                 continue;
1969                         }
1970                 } else {
1971                         if ((*i)->first_frame() > from) {
1972                                 continue;
1973                         }
1974                 }
1975
1976                 (*i)->get_transients (these_points);
1977
1978                 /* add first frame, just, err, because */
1979
1980                 these_points.push_back ((*i)->first_frame());
1981
1982                 points.insert (points.end(), these_points.begin(), these_points.end());
1983                 these_points.clear ();
1984         }
1985
1986         if (points.empty()) {
1987                 return -1;
1988         }
1989
1990         TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1991         bool reached = false;
1992
1993         if (dir > 0) {
1994                 for (AnalysisFeatureList::const_iterator x = points.begin(); x != points.end(); ++x) {
1995                         if ((*x) >= from) {
1996                                 reached = true;
1997                         }
1998
1999                         if (reached && (*x) > from) {
2000                                 return *x;
2001                         }
2002                 }
2003         } else {
2004                 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
2005                         if ((*x) <= from) {
2006                                 reached = true;
2007                         }
2008
2009                         if (reached && (*x) < from) {
2010                                 return *x;
2011                         }
2012                 }
2013         }
2014
2015         return -1;
2016 }
2017
2018 boost::shared_ptr<Region>
2019 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
2020 {
2021         RegionReadLock rlock (this);
2022         boost::shared_ptr<Region> ret;
2023         framepos_t closest = max_framepos;
2024
2025         bool end_iter = false;
2026
2027         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2028
2029                 if(end_iter) break;
2030
2031                 frameoffset_t distance;
2032                 boost::shared_ptr<Region> r = (*i);
2033                 framepos_t pos = 0;
2034
2035                 switch (point) {
2036                 case Start:
2037                         pos = r->first_frame ();
2038                         break;
2039                 case End:
2040                         pos = r->last_frame ();
2041                         break;
2042                 case SyncPoint:
2043                         pos = r->sync_position ();
2044                         break;
2045                 }
2046
2047                 switch (dir) {
2048                 case 1: /* forwards */
2049
2050                         if (pos > frame) {
2051                                 if ((distance = pos - frame) < closest) {
2052                                         closest = distance;
2053                                         ret = r;
2054                                         end_iter = true;
2055                                 }
2056                         }
2057
2058                         break;
2059
2060                 default: /* backwards */
2061
2062                         if (pos < frame) {
2063                                 if ((distance = frame - pos) < closest) {
2064                                         closest = distance;
2065                                         ret = r;
2066                                 }
2067                         } else {
2068                                 end_iter = true;
2069                         }
2070
2071                         break;
2072                 }
2073         }
2074
2075         return ret;
2076 }
2077
2078  framepos_t
2079  Playlist::find_next_region_boundary (framepos_t frame, int dir)
2080  {
2081          RegionReadLock rlock (this);
2082
2083          framepos_t closest = max_framepos;
2084          framepos_t ret = -1;
2085
2086          if (dir > 0) {
2087
2088                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2089
2090                          boost::shared_ptr<Region> r = (*i);
2091                          frameoffset_t distance;
2092                          const framepos_t first_frame = r->first_frame();
2093                          const framepos_t last_frame = r->last_frame();
2094
2095                          if (first_frame > frame) {
2096
2097                                  distance = first_frame - frame;
2098
2099                                  if (distance < closest) {
2100                                          ret = first_frame;
2101                                          closest = distance;
2102                                  }
2103                          }
2104
2105                          if (last_frame > frame) {
2106
2107                                  distance = last_frame - frame;
2108
2109                                  if (distance < closest) {
2110                                          ret = last_frame;
2111                                          closest = distance;
2112                                  }
2113                          }
2114                  }
2115
2116          } else {
2117
2118                  for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2119
2120                          boost::shared_ptr<Region> r = (*i);
2121                          frameoffset_t distance;
2122                          const framepos_t first_frame = r->first_frame();
2123                          const framepos_t last_frame = r->last_frame();
2124
2125                          if (last_frame < frame) {
2126
2127                                  distance = frame - last_frame;
2128
2129                                  if (distance < closest) {
2130                                          ret = last_frame;
2131                                          closest = distance;
2132                                  }
2133                          }
2134
2135                          if (first_frame < frame) {
2136
2137                                  distance = frame - first_frame;
2138
2139                                  if (distance < closest) {
2140                                          ret = first_frame;
2141                                          closest = distance;
2142                                  }
2143                          }
2144                  }
2145          }
2146
2147          return ret;
2148  }
2149
2150
2151  /***********************************************************************/
2152
2153
2154
2155
2156  void
2157  Playlist::mark_session_dirty ()
2158  {
2159          if (!in_set_state && !holding_state ()) {
2160                  _session.set_dirty();
2161          }
2162  }
2163
2164  void
2165  Playlist::rdiff (vector<Command*>& cmds) const
2166  {
2167          RegionReadLock rlock (const_cast<Playlist *> (this));
2168          Stateful::rdiff (cmds);
2169  }
2170
2171  void
2172  Playlist::clear_owned_changes ()
2173  {
2174          RegionReadLock rlock (this);
2175          Stateful::clear_owned_changes ();
2176  }
2177
2178  void
2179  Playlist::update (const RegionListProperty::ChangeRecord& change)
2180  {
2181          DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2182                                                          name(), change.added.size(), change.removed.size()));
2183
2184          freeze ();
2185          /* add the added regions */
2186          for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2187                  add_region_internal ((*i), (*i)->position());
2188          }
2189          /* remove the removed regions */
2190          for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2191                  remove_region (*i);
2192          }
2193
2194          thaw ();
2195  }
2196
2197  int
2198  Playlist::set_state (const XMLNode& node, int version)
2199  {
2200          XMLNode *child;
2201          XMLNodeList nlist;
2202          XMLNodeConstIterator niter;
2203          XMLPropertyList plist;
2204          XMLPropertyConstIterator piter;
2205          XMLProperty const * prop;
2206          boost::shared_ptr<Region> region;
2207          string region_name;
2208          bool seen_region_nodes = false;
2209          int ret = 0;
2210
2211          in_set_state++;
2212
2213          if (node.name() != "Playlist") {
2214                  in_set_state--;
2215                  return -1;
2216          }
2217
2218          freeze ();
2219
2220          plist = node.properties();
2221
2222          set_id (node);
2223
2224          for (piter = plist.begin(); piter != plist.end(); ++piter) {
2225
2226                  prop = *piter;
2227
2228                  if (prop->name() == X_("name")) {
2229                          _name = prop->value();
2230                          _set_sort_id ();
2231                  } else if (prop->name() == X_("orig-diskstream-id")) {
2232                          /* XXX legacy session: fix up later */
2233                          _orig_track_id = prop->value ();
2234                  } else if (prop->name() == X_("orig-track-id")) {
2235                          _orig_track_id = prop->value ();
2236                  } else if (prop->name() == X_("frozen")) {
2237                          _frozen = string_is_affirmative (prop->value());
2238                  } else if (prop->name() == X_("combine-ops")) {
2239                          _combine_ops = atoi (prop->value());
2240                  } else if (prop->name() == X_("shared-with-ids")) {
2241                          string shared_ids = prop->value ();
2242                          if (!shared_ids.empty()) {
2243                                 vector<string> result;
2244                                 ::split (shared_ids, result, ',');
2245                                 vector<string>::iterator it = result.begin();
2246                                 for (; it != result.end(); ++it) {
2247                                         _shared_with_ids.push_back (PBD::ID(*it));
2248                                 }
2249                          }
2250                  }
2251          }
2252
2253          clear (true);
2254
2255          nlist = node.children();
2256
2257          for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2258
2259                  child = *niter;
2260
2261                  if (child->name() == "Region") {
2262
2263                          seen_region_nodes = true;
2264
2265                          if ((prop = child->property ("id")) == 0) {
2266                                  error << _("region state node has no ID, ignored") << endmsg;
2267                                  continue;
2268                          }
2269
2270                          ID id = prop->value ();
2271
2272                          if ((region = region_by_id (id))) {
2273
2274                                  region->suspend_property_changes ();
2275
2276                                  if (region->set_state (*child, version)) {
2277                                          region->resume_property_changes ();
2278                                          continue;
2279                                  }
2280
2281                          } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2282                                  region->suspend_property_changes ();
2283                          } else {
2284                                  error << _("Playlist: cannot create region from XML") << endmsg;
2285                                 return -1;
2286                         }
2287
2288                          {
2289                                  RegionWriteLock rlock (this);
2290                                  add_region_internal (region, region->position());
2291                          }
2292
2293                         region->resume_property_changes ();
2294
2295                 }
2296         }
2297
2298         if (seen_region_nodes && regions.empty()) {
2299                 ret = -1;
2300         }
2301
2302         thaw ();
2303         notify_contents_changed ();
2304
2305         in_set_state--;
2306         first_set_state = false;
2307
2308         return ret;
2309 }
2310
2311 XMLNode&
2312 Playlist::get_state()
2313 {
2314         return state (true);
2315 }
2316
2317 XMLNode&
2318 Playlist::get_template()
2319 {
2320         return state (false);
2321 }
2322
2323 /** @param full_state true to include regions in the returned state, otherwise false.
2324  */
2325 XMLNode&
2326 Playlist::state (bool full_state)
2327 {
2328         XMLNode *node = new XMLNode (X_("Playlist"));
2329         char buf[64];
2330
2331         node->add_property (X_("id"), id().to_s());
2332         node->add_property (X_("name"), _name);
2333         node->add_property (X_("type"), _type.to_string());
2334
2335         _orig_track_id.print (buf, sizeof (buf));
2336         node->add_property (X_("orig-track-id"), buf);
2337
2338         string shared_ids;
2339         list<PBD::ID>::const_iterator it = _shared_with_ids.begin();
2340         for (; it != _shared_with_ids.end(); ++it) {
2341                 shared_ids += "," + (*it).to_s();
2342         }
2343         if (!shared_ids.empty()) {
2344                 shared_ids.erase(0,1);
2345         }
2346
2347         node->add_property (X_("shared-with-ids"), shared_ids);
2348         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2349
2350         if (full_state) {
2351                 RegionReadLock rlock (this);
2352
2353                 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2354                 node->add_property ("combine-ops", buf);
2355
2356                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2357                         node->add_child_nocopy ((*i)->get_state());
2358                 }
2359         }
2360
2361         if (_extra_xml) {
2362                 node->add_child_copy (*_extra_xml);
2363         }
2364
2365         return *node;
2366 }
2367
2368 bool
2369 Playlist::empty() const
2370 {
2371         RegionReadLock rlock (const_cast<Playlist *>(this));
2372         return regions.empty();
2373 }
2374
2375 uint32_t
2376 Playlist::n_regions() const
2377 {
2378         RegionReadLock rlock (const_cast<Playlist *>(this));
2379         return regions.size();
2380 }
2381
2382 /** @return true if the all_regions list is empty, ie this playlist
2383  *  has never had a region added to it.
2384  */
2385 bool
2386 Playlist::all_regions_empty() const
2387 {
2388         RegionReadLock rl (const_cast<Playlist *> (this));
2389         return all_regions.empty();
2390 }
2391
2392 pair<framepos_t, framepos_t>
2393 Playlist::get_extent () const
2394 {
2395         RegionReadLock rlock (const_cast<Playlist *>(this));
2396         return _get_extent ();
2397 }
2398
2399 pair<framepos_t, framepos_t>
2400 Playlist::get_extent_with_endspace () const
2401 {
2402         pair<framepos_t, framepos_t> l = get_extent();
2403         l.second += _end_space;
2404         return l;
2405 }
2406
2407 pair<framepos_t, framepos_t>
2408 Playlist::_get_extent () const
2409 {
2410         pair<framepos_t, framepos_t> ext (max_framepos, 0);
2411
2412         if (regions.empty()) {
2413                 ext.first = 0;
2414                 return ext;
2415         }
2416
2417         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2418                 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2419                 if (e.first < ext.first) {
2420                         ext.first = e.first;
2421                 }
2422                 if (e.second > ext.second) {
2423                         ext.second = e.second;
2424                 }
2425         }
2426
2427         return ext;
2428 }
2429
2430 string
2431 Playlist::bump_name (string name, Session &session)
2432 {
2433         string newname = name;
2434
2435         do {
2436                 newname = bump_name_once (newname, '.');
2437         } while (session.playlists->by_name (newname)!=NULL);
2438
2439         return newname;
2440 }
2441
2442
2443 layer_t
2444 Playlist::top_layer() const
2445 {
2446         RegionReadLock rlock (const_cast<Playlist *> (this));
2447         layer_t top = 0;
2448
2449         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2450                 top = max (top, (*i)->layer());
2451         }
2452         return top;
2453 }
2454
2455 void
2456 Playlist::set_edit_mode (EditMode mode)
2457 {
2458         _edit_mode = mode;
2459 }
2460
2461 struct RelayerSort {
2462         bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2463                 return a->layering_index() < b->layering_index();
2464         }
2465 };
2466
2467 /** Set a new layer for a region.  This adjusts the layering indices of all
2468  *  regions in the playlist to put the specified region in the appropriate
2469  *  place.  The actual layering will be fixed up when relayer() happens.
2470  */
2471
2472 void
2473 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2474 {
2475         /* Remove the layer we are setting from our region list, and sort it
2476         *  using the layer indeces.
2477         */
2478
2479         RegionList copy = regions.rlist();
2480         copy.remove (region);
2481         copy.sort (RelayerSort ());
2482
2483         /* Put region back in the right place */
2484         RegionList::iterator i = copy.begin();
2485         while (i != copy.end ()) {
2486                 if ((*i)->layer() > new_layer) {
2487                         break;
2488                 }
2489                 ++i;
2490         }
2491
2492         copy.insert (i, region);
2493
2494         setup_layering_indices (copy);
2495 }
2496
2497 void
2498 Playlist::setup_layering_indices (RegionList const & regions)
2499 {
2500         uint64_t j = 0;
2501
2502         for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2503                 (*k)->set_layering_index (j++);
2504         }
2505 }
2506
2507 struct LaterHigherSort {
2508         bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2509                 return a->position() < b->position();
2510         }
2511 };
2512
2513 /** Take the layering indices of each of our regions, compute the layers
2514  *  that they should be on, and write the layers back to the regions.
2515  */
2516 void
2517 Playlist::relayer ()
2518 {
2519         /* never compute layers when setting from XML */
2520
2521         if (in_set_state) {
2522                 return;
2523         }
2524
2525         /* Build up a new list of regions on each layer, stored in a set of lists
2526            each of which represent some period of time on some layer.  The idea
2527            is to avoid having to search the entire region list to establish whether
2528            each region overlaps another */
2529
2530         /* how many pieces to divide this playlist's time up into */
2531         int const divisions = 512;
2532
2533         /* find the start and end positions of the regions on this playlist */
2534         framepos_t start = INT64_MAX;
2535         framepos_t end = 0;
2536         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2537                 start = min (start, (*i)->position());
2538                 end = max (end, (*i)->position() + (*i)->length());
2539         }
2540
2541         /* hence the size of each time division */
2542         double const division_size = (end - start) / double (divisions);
2543
2544         vector<vector<RegionList> > layers;
2545         layers.push_back (vector<RegionList> (divisions));
2546
2547         /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2548         RegionList copy = regions.rlist();
2549         switch (Config->get_layer_model()) {
2550                 case LaterHigher:
2551                         copy.sort (LaterHigherSort ());
2552                         break;
2553                 case Manual:
2554                         copy.sort (RelayerSort ());
2555                         break;
2556         }
2557
2558         DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2559         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2560                 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2561         }
2562
2563         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2564
2565                 /* find the time divisions that this region covers; if there are no regions on the list,
2566                    division_size will equal 0 and in this case we'll just say that
2567                    start_division = end_division = 0.
2568                 */
2569                 int start_division = 0;
2570                 int end_division = 0;
2571
2572                 if (division_size > 0) {
2573                         start_division = floor ( ((*i)->position() - start) / division_size);
2574                         end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2575                         if (end_division == divisions) {
2576                                 end_division--;
2577                         }
2578                 }
2579
2580                 assert (divisions == 0 || end_division < divisions);
2581
2582                 /* find the lowest layer that this region can go on */
2583                 size_t j = layers.size();
2584                 while (j > 0) {
2585                         /* try layer j - 1; it can go on if it overlaps no other region
2586                            that is already on that layer
2587                         */
2588
2589                         bool overlap = false;
2590                         for (int k = start_division; k <= end_division; ++k) {
2591                                 RegionList::iterator l = layers[j-1][k].begin ();
2592                                 while (l != layers[j-1][k].end()) {
2593                                         if ((*l)->overlap_equivalent (*i)) {
2594                                                 overlap = true;
2595                                                 break;
2596                                         }
2597                                         l++;
2598                                 }
2599
2600                                 if (overlap) {
2601                                         break;
2602                                 }
2603                         }
2604
2605                         if (overlap) {
2606                                 /* overlap, so we must use layer j */
2607                                 break;
2608                         }
2609
2610                         --j;
2611                 }
2612
2613                 if (j == layers.size()) {
2614                         /* we need a new layer for this region */
2615                         layers.push_back (vector<RegionList> (divisions));
2616                 }
2617
2618                 /* put a reference to this region in each of the divisions that it exists in */
2619                 for (int k = start_division; k <= end_division; ++k) {
2620                         layers[j][k].push_back (*i);
2621                 }
2622
2623                 (*i)->set_layer (j);
2624         }
2625
2626         /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2627            relayering because we just removed the only region on the top layer, nothing will
2628            appear to have changed, but the StreamView must still sort itself out.  We could
2629            probably keep a note of the top layer last time we relayered, and check that,
2630            but premature optimisation &c...
2631         */
2632         notify_layering_changed ();
2633
2634         /* This relayer() may have been called as a result of a region removal, in which
2635            case we need to setup layering indices to account for the one that has just
2636            gone away.
2637         */
2638         setup_layering_indices (copy);
2639 }
2640
2641 void
2642 Playlist::raise_region (boost::shared_ptr<Region> region)
2643 {
2644         set_layer (region, region->layer() + 1.5);
2645         relayer ();
2646 }
2647
2648 void
2649 Playlist::lower_region (boost::shared_ptr<Region> region)
2650 {
2651         set_layer (region, region->layer() - 1.5);
2652         relayer ();
2653 }
2654
2655 void
2656 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2657 {
2658         set_layer (region, DBL_MAX);
2659         relayer ();
2660 }
2661
2662 void
2663 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2664 {
2665         set_layer (region, -0.5);
2666         relayer ();
2667 }
2668
2669 void
2670 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2671 {
2672         RegionList::iterator i;
2673         bool moved = false;
2674
2675         _nudging = true;
2676
2677         {
2678                 RegionWriteLock rlock (const_cast<Playlist *> (this));
2679
2680                 for (i = regions.begin(); i != regions.end(); ++i) {
2681
2682                         if ((*i)->position() >= start) {
2683
2684                                 framepos_t new_pos;
2685
2686                                 if (forwards) {
2687
2688                                         if ((*i)->last_frame() > max_framepos - distance) {
2689                                                 new_pos = max_framepos - (*i)->length();
2690                                         } else {
2691                                                 new_pos = (*i)->position() + distance;
2692                                         }
2693
2694                                 } else {
2695
2696                                         if ((*i)->position() > distance) {
2697                                                 new_pos = (*i)->position() - distance;
2698                                         } else {
2699                                                 new_pos = 0;
2700                                         }
2701                                 }
2702
2703                                 (*i)->set_position (new_pos);
2704                                 moved = true;
2705                         }
2706                 }
2707         }
2708
2709         if (moved) {
2710                 _nudging = false;
2711                 notify_contents_changed ();
2712         }
2713
2714 }
2715
2716 bool
2717 Playlist::uses_source (boost::shared_ptr<const Source> src, bool shallow) const
2718 {
2719         RegionReadLock rlock (const_cast<Playlist*> (this));
2720
2721         for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2722                 /* Note: passing the second argument as false can cause at best
2723                    incredibly deep and time-consuming recursion, and at worst
2724                    cycles if the user has managed to create cycles of reference
2725                    between compound regions. We generally only this during
2726                    cleanup, and @param shallow is passed as true.
2727                 */
2728                 if ((*r)->uses_source (src, shallow)) {
2729                         return true;
2730                 }
2731         }
2732
2733         return false;
2734 }
2735
2736
2737 boost::shared_ptr<Region>
2738 Playlist::find_region (const ID& id) const
2739 {
2740         RegionReadLock rlock (const_cast<Playlist*> (this));
2741
2742         /* searches all regions currently in use by the playlist */
2743
2744         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2745                 if ((*i)->id() == id) {
2746                         return *i;
2747                 }
2748         }
2749
2750         return boost::shared_ptr<Region> ();
2751 }
2752
2753 uint32_t
2754 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2755 {
2756         RegionReadLock rlock (const_cast<Playlist*> (this));
2757         uint32_t cnt = 0;
2758
2759         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2760                 if ((*i) == r) {
2761                         cnt++;
2762                 }
2763         }
2764
2765         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2766         for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2767                 /* check if region is used in a compound */
2768                 if (it->second == r) {
2769                         /* region is referenced as 'original' of a compound */
2770                         ++cnt;
2771                         break;
2772                 }
2773                 if (r->whole_file() && r->max_source_level() > 0) {
2774                         /* region itself ia a compound.
2775                          * the compound regions are not referenced -> check regions inside compound
2776                          */
2777                         const SourceList& sl = r->sources();
2778                         for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2779                                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2780                                 if (!ps) continue;
2781                                 if (ps->playlist()->region_use_count(it->first)) {
2782                                         // break out of both loops
2783                                         return ++cnt;
2784                                 }
2785                         }
2786                 }
2787         }
2788         return cnt;
2789 }
2790
2791 boost::shared_ptr<Region>
2792 Playlist::region_by_id (const ID& id) const
2793 {
2794         /* searches all regions ever added to this playlist */
2795
2796         for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2797                 if ((*i)->id() == id) {
2798                         return *i;
2799                 }
2800         }
2801         return boost::shared_ptr<Region> ();
2802 }
2803
2804 void
2805 Playlist::dump () const
2806 {
2807         boost::shared_ptr<Region> r;
2808
2809         cerr << "Playlist \"" << _name << "\" " << endl
2810              << regions.size() << " regions "
2811              << endl;
2812
2813         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2814                 r = *i;
2815                 cerr << "  " << r->name() << " ["
2816                      << r->start() << "+" << r->length()
2817                      << "] at "
2818                      << r->position()
2819                      << " on layer "
2820                      << r->layer ()
2821                      << endl;
2822         }
2823 }
2824
2825 void
2826 Playlist::set_frozen (bool yn)
2827 {
2828         _frozen = yn;
2829 }
2830
2831 void
2832 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2833 {
2834         bool moved = false;
2835
2836         if (region->locked()) {
2837                 return;
2838         }
2839
2840         _shuffling = true;
2841
2842         {
2843                 RegionWriteLock rlock (const_cast<Playlist*> (this));
2844
2845
2846                 if (dir > 0) {
2847
2848                         RegionList::iterator next;
2849
2850                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2851                                 if ((*i) == region) {
2852                                         next = i;
2853                                         ++next;
2854
2855                                         if (next != regions.end()) {
2856
2857                                                 if ((*next)->locked()) {
2858                                                         break;
2859                                                 }
2860
2861                                                 framepos_t new_pos;
2862
2863                                                 if ((*next)->position() != region->last_frame() + 1) {
2864                                                         /* they didn't used to touch, so after shuffle,
2865                                                            just have them swap positions.
2866                                                         */
2867                                                         new_pos = (*next)->position();
2868                                                 } else {
2869                                                         /* they used to touch, so after shuffle,
2870                                                            make sure they still do. put the earlier
2871                                                            region where the later one will end after
2872                                                            it is moved.
2873                                                         */
2874                                                         new_pos = region->position() + (*next)->length();
2875                                                 }
2876
2877                                                 (*next)->set_position (region->position());
2878                                                 region->set_position (new_pos);
2879
2880                                                 /* avoid a full sort */
2881
2882                                                 regions.erase (i); // removes the region from the list */
2883                                                 next++;
2884                                                 regions.insert (next, region); // adds it back after next
2885
2886                                                 moved = true;
2887                                         }
2888                                         break;
2889                                 }
2890                         }
2891                 } else {
2892
2893                         RegionList::iterator prev = regions.end();
2894
2895                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2896                                 if ((*i) == region) {
2897
2898                                         if (prev != regions.end()) {
2899
2900                                                 if ((*prev)->locked()) {
2901                                                         break;
2902                                                 }
2903
2904                                                 framepos_t new_pos;
2905                                                 if (region->position() != (*prev)->last_frame() + 1) {
2906                                                         /* they didn't used to touch, so after shuffle,
2907                                                            just have them swap positions.
2908                                                         */
2909                                                         new_pos = region->position();
2910                                                 } else {
2911                                                         /* they used to touch, so after shuffle,
2912                                                            make sure they still do. put the earlier
2913                                                            one where the later one will end after
2914                                                         */
2915                                                         new_pos = (*prev)->position() + region->length();
2916                                                 }
2917
2918                                                 region->set_position ((*prev)->position());
2919                                                 (*prev)->set_position (new_pos);
2920
2921                                                 /* avoid a full sort */
2922
2923                                                 regions.erase (i); // remove region
2924                                                 regions.insert (prev, region); // insert region before prev
2925
2926                                                 moved = true;
2927                                         }
2928
2929                                         break;
2930                                 }
2931                         }
2932                 }
2933         }
2934
2935         _shuffling = false;
2936
2937         if (moved) {
2938
2939                 relayer ();
2940                 notify_contents_changed();
2941         }
2942
2943 }
2944
2945 bool
2946 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2947 {
2948         RegionReadLock rlock (const_cast<Playlist*> (this));
2949
2950         if (regions.size() > 1) {
2951                 return true;
2952         }
2953
2954         return false;
2955 }
2956
2957 void
2958 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2959 {
2960         ripple_locked (at, distance, exclude);
2961 }
2962
2963 void
2964 Playlist::update_after_tempo_map_change ()
2965 {
2966         RegionWriteLock rlock (const_cast<Playlist*> (this));
2967         RegionList copy (regions.rlist());
2968
2969         freeze ();
2970
2971         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2972                 (*i)->update_after_tempo_map_change ();
2973         }
2974         /* possibly causes a contents changed notification (flush_notifications()) */
2975         thaw ();
2976 }
2977
2978 void
2979 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2980 {
2981         RegionReadLock rl (this);
2982         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2983                 s (*i);
2984         }
2985 }
2986
2987 bool
2988 Playlist::has_region_at (framepos_t const p) const
2989 {
2990         RegionReadLock (const_cast<Playlist *> (this));
2991
2992         RegionList::const_iterator i = regions.begin ();
2993         while (i != regions.end() && !(*i)->covers (p)) {
2994                 ++i;
2995         }
2996
2997         return (i != regions.end());
2998 }
2999
3000 /** Look from a session frame time and find the start time of the next region
3001  *  which is on the top layer of this playlist.
3002  *  @param t Time to look from.
3003  *  @return Position of next top-layered region, or max_framepos if there isn't one.
3004  */
3005 framepos_t
3006 Playlist::find_next_top_layer_position (framepos_t t) const
3007 {
3008         RegionReadLock rlock (const_cast<Playlist *> (this));
3009
3010         layer_t const top = top_layer ();
3011
3012         RegionList copy = regions.rlist ();
3013         copy.sort (RegionSortByPosition ());
3014
3015         for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
3016                 if ((*i)->position() >= t && (*i)->layer() == top) {
3017                         return (*i)->position();
3018                 }
3019         }
3020
3021         return max_framepos;
3022 }
3023
3024 boost::shared_ptr<Region>
3025 Playlist::combine (const RegionList& r)
3026 {
3027         PropertyList plist;
3028         uint32_t channels = 0;
3029         uint32_t layer = 0;
3030         framepos_t earliest_position = max_framepos;
3031         vector<TwoRegions> old_and_new_regions;
3032         vector<boost::shared_ptr<Region> > originals;
3033         vector<boost::shared_ptr<Region> > copies;
3034         string parent_name;
3035         string child_name;
3036         uint32_t max_level = 0;
3037
3038         /* find the maximum depth of all the regions we're combining */
3039
3040         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3041                 max_level = max (max_level, (*i)->max_source_level());
3042         }
3043
3044         parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
3045         child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
3046
3047         boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
3048
3049         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3050                 earliest_position = min (earliest_position, (*i)->position());
3051         }
3052
3053         /* enable this so that we do not try to create xfades etc. as we add
3054          * regions
3055          */
3056
3057         pl->in_partition = true;
3058
3059         /* sort by position then layer.
3060          * route_time_axis passes 'selected_regions' - which is not sorted.
3061          * here we need the top-most first, then every layer's region sorted by position.
3062          */
3063         RegionList sorted(r);
3064         sorted.sort(RegionSortByLayerAndPosition());
3065
3066         for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
3067
3068                 /* copy the region */
3069
3070                 boost::shared_ptr<Region> original_region = (*i);
3071                 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
3072
3073                 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
3074                 originals.push_back (original_region);
3075                 copies.push_back (copied_region);
3076
3077                 RegionFactory::add_compound_association (original_region, copied_region);
3078
3079                 /* make position relative to zero */
3080
3081                 pl->add_region (copied_region, original_region->position() - earliest_position);
3082                 copied_region->set_layer (original_region->layer ());
3083
3084                 /* use the maximum number of channels for any region */
3085
3086                 channels = max (channels, original_region->n_channels());
3087
3088                 /* it will go above the layer of the highest existing region */
3089
3090                 layer = max (layer, original_region->layer());
3091         }
3092
3093         pl->in_partition = false;
3094
3095         pre_combine (copies);
3096
3097         /* now create a new PlaylistSource for each channel in the new playlist */
3098
3099         SourceList sources;
3100         pair<framepos_t,framepos_t> extent = pl->get_extent();
3101
3102         for (uint32_t chn = 0; chn < channels; ++chn) {
3103                 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
3104
3105         }
3106
3107         /* now a new whole-file region using the list of sources */
3108
3109         plist.add (Properties::start, 0);
3110         plist.add (Properties::length, extent.second);
3111         plist.add (Properties::name, parent_name);
3112         plist.add (Properties::whole_file, true);
3113
3114         boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
3115
3116         /* now the non-whole-file region that we will actually use in the
3117          * playlist
3118          */
3119
3120         plist.clear ();
3121         plist.add (Properties::start, 0);
3122         plist.add (Properties::length, extent.second);
3123         plist.add (Properties::name, child_name);
3124         plist.add (Properties::layer, layer+1);
3125
3126         boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3127
3128         /* remove all the selected regions from the current playlist
3129          */
3130
3131         freeze ();
3132
3133         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3134                 remove_region (*i);
3135         }
3136
3137         /* do type-specific stuff with the originals and the new compound
3138            region
3139         */
3140
3141         post_combine (originals, compound_region);
3142
3143         /* add the new region at the right location */
3144
3145         add_region (compound_region, earliest_position);
3146
3147         _combine_ops++;
3148
3149         thaw ();
3150
3151         return compound_region;
3152 }
3153
3154 void
3155 Playlist::uncombine (boost::shared_ptr<Region> target)
3156 {
3157         boost::shared_ptr<PlaylistSource> pls;
3158         boost::shared_ptr<const Playlist> pl;
3159         vector<boost::shared_ptr<Region> > originals;
3160         vector<TwoRegions> old_and_new_regions;
3161
3162         // (1) check that its really a compound region
3163
3164         if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3165                 return;
3166         }
3167
3168         pl = pls->playlist();
3169
3170         framepos_t adjusted_start = 0; // gcc isn't smart enough
3171         framepos_t adjusted_end = 0;   // gcc isn't smart enough
3172
3173         /* the leftmost (earliest) edge of the compound region
3174            starts at zero in its source, or larger if it
3175            has been trimmed or content-scrolled.
3176
3177            the rightmost (latest) edge of the compound region
3178            relative to its source is the starting point plus
3179            the length of the region.
3180         */
3181
3182         // (2) get all the original regions
3183
3184         const RegionList& rl (pl->region_list_property().rlist());
3185         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3186         frameoffset_t move_offset = 0;
3187
3188         /* there are two possibilities here:
3189            1) the playlist that the playlist source was based on
3190            is us, so just add the originals (which belonged to
3191            us anyway) back in the right place.
3192
3193            2) the playlist that the playlist source was based on
3194            is NOT us, so we need to make copies of each of
3195            the original regions that we find, and add them
3196            instead.
3197         */
3198         bool same_playlist = (pls->original() == id());
3199
3200         for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3201
3202                 boost::shared_ptr<Region> current (*i);
3203
3204                 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3205
3206                 if (ca == cassocs.end()) {
3207                         continue;
3208                 }
3209
3210                 boost::shared_ptr<Region> original (ca->second);
3211                 cassocs.erase(ca);
3212                 bool modified_region;
3213
3214                 if (i == rl.begin()) {
3215                         move_offset = (target->position() - original->position()) - target->start();
3216                         adjusted_start = original->position() + target->start();
3217                         adjusted_end = adjusted_start + target->length();
3218                 }
3219
3220                 if (!same_playlist) {
3221                         framepos_t pos = original->position();
3222                         /* make a copy, but don't announce it */
3223                         original = RegionFactory::create (original, false);
3224                         /* the pure copy constructor resets position() to zero,
3225                            so fix that up.
3226                         */
3227                         original->set_position (pos);
3228                 }
3229
3230                 /* check to see how the original region (in the
3231                  * playlist before compounding occurred) overlaps
3232                  * with the new state of the compound region.
3233                  */
3234
3235                 original->clear_changes ();
3236                 modified_region = false;
3237
3238                 switch (original->coverage (adjusted_start, adjusted_end)) {
3239                 case Evoral::OverlapNone:
3240                         /* original region does not cover any part
3241                            of the current state of the compound region
3242                         */
3243                         continue;
3244
3245                 case Evoral::OverlapInternal:
3246                         /* overlap is just a small piece inside the
3247                          * original so trim both ends
3248                          */
3249                         original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3250                         modified_region = true;
3251                         break;
3252
3253                 case Evoral::OverlapExternal:
3254                         /* overlap fully covers original, so leave it
3255                            as is
3256                         */
3257                         break;
3258
3259                 case Evoral::OverlapEnd:
3260                         /* overlap starts within but covers end,
3261                            so trim the front of the region
3262                         */
3263                         original->trim_front (adjusted_start);
3264                         modified_region = true;
3265                         break;
3266
3267                 case Evoral::OverlapStart:
3268                         /* overlap covers start but ends within, so
3269                          * trim the end of the region.
3270                          */
3271                         original->trim_end (adjusted_end);
3272                         modified_region = true;
3273                         break;
3274                 }
3275
3276                 if (move_offset) {
3277                         /* fix the position to match any movement of the compound region.
3278                          */
3279                         original->set_position (original->position() + move_offset);
3280                         modified_region = true;
3281                 }
3282
3283                 if (modified_region) {
3284                         _session.add_command (new StatefulDiffCommand (original));
3285                 }
3286
3287                 /* and add to the list of regions waiting to be
3288                  * re-inserted
3289                  */
3290
3291                 originals.push_back (original);
3292                 old_and_new_regions.push_back (TwoRegions (*i, original));
3293         }
3294
3295         pre_uncombine (originals, target);
3296
3297         in_partition = true;
3298         freeze ();
3299
3300         // (3) remove the compound region
3301
3302         remove_region (target);
3303
3304         // (4) add the constituent regions
3305
3306         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3307                 add_region ((*i), (*i)->position());
3308                 set_layer((*i), (*i)->layer());
3309                 if (!RegionFactory::region_by_id((*i)->id())) {
3310                         RegionFactory::map_add(*i);
3311                 }
3312         }
3313
3314         in_partition = false;
3315         thaw ();
3316 }
3317
3318 void
3319 Playlist::fade_range (list<AudioRange>& ranges)
3320 {
3321          for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3322                  for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3323                          (*i)->fade_range ((*r).start, (*r).end);
3324                  }
3325          }
3326 }
3327
3328 uint32_t
3329 Playlist::max_source_level () const
3330 {
3331         RegionReadLock rlock (const_cast<Playlist *> (this));
3332         uint32_t lvl = 0;
3333
3334         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3335                 lvl = max (lvl, (*i)->max_source_level());
3336         }
3337
3338         return lvl;
3339 }
3340
3341 void
3342 Playlist::set_orig_track_id (const PBD::ID& id)
3343 {
3344         if (shared_with(id)) {
3345                 // Swap 'shared_id' / origin_track_id
3346                 unshare_with (id);
3347                 share_with (_orig_track_id);
3348         }
3349         _orig_track_id = id;
3350 }
3351
3352 void
3353 Playlist::share_with (const PBD::ID& id)
3354 {
3355         if (!shared_with(id)) {
3356                 _shared_with_ids.push_back (id);
3357         }
3358 }
3359
3360 void
3361 Playlist::unshare_with (const PBD::ID& id)
3362 {
3363         list<PBD::ID>::iterator it = _shared_with_ids.begin ();
3364         while (it != _shared_with_ids.end()) {
3365                 if (*it == id) {
3366                         _shared_with_ids.erase (it);
3367                         break;
3368                 }
3369                 ++it;
3370         }
3371 }
3372
3373 bool
3374 Playlist::shared_with (const PBD::ID& id) const
3375 {
3376         bool shared = false;
3377         list<PBD::ID>::const_iterator it = _shared_with_ids.begin ();
3378         while (it != _shared_with_ids.end() && !shared) {
3379                 if (*it == id) {
3380                         shared = true;
3381                 }
3382                 ++it;
3383         }
3384
3385         return shared;
3386 }
3387
3388 void
3389 Playlist::reset_shares ()
3390 {
3391         _shared_with_ids.clear();
3392 }
3393
3394 /** Take a list of ranges, coalesce any that can be coalesced, then call
3395  *  check_crossfades for each one.
3396  */
3397 void
3398 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3399 {
3400         /* XXX: it's a shame that this coalesce algorithm also exists in
3401            TimeSelection::consolidate().
3402         */
3403
3404         /* XXX: xfade: this is implemented in Evoral::RangeList */
3405
3406 restart:
3407         for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3408                 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3409
3410                         if (i == j) {
3411                                 continue;
3412                         }
3413
3414                         // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3415                         if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3416                                 i->from = min (i->from, j->from);
3417                                 i->to = max (i->to, j->to);
3418                                 ranges.erase (j);
3419                                 goto restart;
3420                         }
3421                 }
3422         }
3423 }
3424
3425 void
3426 Playlist::set_capture_insertion_in_progress (bool yn)
3427 {
3428         _capture_insertion_underway = yn;
3429 }