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