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