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