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