fix duplicating multiple selected regions - fixes #6202
[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          framepos_t pos = position + 1;
1260
1261          while (itimes--) {
1262                  boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
1263                  add_region_internal (copy, pos);
1264                  set_layer (copy, DBL_MAX);
1265                  pos += gap;
1266          }
1267
1268          if (floor (times) != times) {
1269                  framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
1270                  string name;
1271                  RegionFactory::region_name (name, region->name(), false);
1272
1273                  {
1274                          PropertyList plist;
1275
1276                          plist.add (Properties::start, region->start());
1277                          plist.add (Properties::length, length);
1278                          plist.add (Properties::name, name);
1279
1280                          boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
1281                          add_region_internal (sub, pos);
1282                          set_layer (sub, DBL_MAX);
1283                  }
1284          }
1285  }
1286
1287  void
1288  Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
1289  {
1290          RegionWriteLock rlock (this);
1291          RegionList copy (regions.rlist());
1292          RegionList fixup;
1293
1294          for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1295
1296                  if ((*r)->last_frame() < at) {
1297                          /* too early */
1298                          continue;
1299                  }
1300
1301                  if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
1302                          /* intersected region */
1303                          if (!move_intersected) {
1304                                  continue;
1305                          }
1306                  }
1307
1308                  /* do not move regions glued to music time - that
1309                     has to be done separately.
1310                  */
1311
1312                  if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
1313                          fixup.push_back (*r);
1314                          continue;
1315                  }
1316
1317                  (*r)->set_position ((*r)->position() + distance);
1318          }
1319
1320          /* XXX: may not be necessary; Region::post_set should do this, I think */
1321          for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
1322                  (*r)->recompute_position_from_lock_style ();
1323          }
1324  }
1325
1326  void
1327  Playlist::split (framepos_t at)
1328  {
1329          RegionWriteLock rlock (this);
1330          RegionList copy (regions.rlist());
1331
1332          /* use a copy since this operation can modify the region list
1333           */
1334
1335          for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
1336                  _split_region (*r, at);
1337          }
1338  }
1339
1340  void
1341  Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1342  {
1343          RegionWriteLock rl (this);
1344          _split_region (region, playlist_position);
1345  }
1346
1347  void
1348  Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
1349  {
1350          if (!region->covers (playlist_position)) {
1351                  return;
1352          }
1353
1354          if (region->position() == playlist_position ||
1355              region->last_frame() == playlist_position) {
1356                  return;
1357          }
1358
1359          boost::shared_ptr<Region> left;
1360          boost::shared_ptr<Region> right;
1361          frameoffset_t before;
1362          frameoffset_t after;
1363          string before_name;
1364          string after_name;
1365
1366          /* split doesn't change anything about length, so don't try to splice */
1367
1368          bool old_sp = _splicing;
1369          _splicing = true;
1370
1371          before = playlist_position - region->position();
1372          after = region->length() - before;
1373
1374          RegionFactory::region_name (before_name, region->name(), false);
1375
1376          {
1377                  PropertyList plist;
1378
1379                  plist.add (Properties::position, region->position ());
1380                  plist.add (Properties::length, before);
1381                  plist.add (Properties::name, before_name);
1382                  plist.add (Properties::left_of_split, true);
1383                  plist.add (Properties::layering_index, region->layering_index ());
1384                  plist.add (Properties::layer, region->layer ());
1385
1386                  /* note: we must use the version of ::create with an offset here,
1387                     since it supplies that offset to the Region constructor, which
1388                     is necessary to get audio region gain envelopes right.
1389                  */
1390                  left = RegionFactory::create (region, 0, plist);
1391          }
1392
1393          RegionFactory::region_name (after_name, region->name(), false);
1394
1395          {
1396                  PropertyList plist;
1397
1398                  plist.add (Properties::position, region->position() + before);
1399                  plist.add (Properties::length, after);
1400                  plist.add (Properties::name, after_name);
1401                  plist.add (Properties::right_of_split, true);
1402                  plist.add (Properties::layering_index, region->layering_index ());
1403                  plist.add (Properties::layer, region->layer ());
1404
1405                  /* same note as above */
1406                  right = RegionFactory::create (region, before, plist);
1407          }
1408
1409          add_region_internal (left, region->position());
1410          add_region_internal (right, region->position() + before);
1411          remove_region_internal (region);
1412
1413          _splicing = old_sp;
1414  }
1415
1416  void
1417  Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1418  {
1419          if (_splicing || in_set_state) {
1420                  /* don't respond to splicing moves or state setting */
1421                  return;
1422          }
1423
1424          if (_edit_mode == Splice) {
1425                  splice_locked (at, distance, exclude);
1426          } 
1427  }
1428
1429  void
1430  Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1431  {
1432          if (_splicing || in_set_state) {
1433                  /* don't respond to splicing moves or state setting */
1434                  return;
1435          }
1436
1437          if (_edit_mode == Splice) {
1438                  splice_unlocked (at, distance, exclude);
1439          }
1440  }
1441
1442  void
1443  Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1444  {
1445          {
1446                  RegionWriteLock rl (this);
1447                  core_splice (at, distance, exclude);
1448          }
1449  }
1450
1451  void
1452  Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1453  {
1454          core_splice (at, distance, exclude);
1455  }
1456
1457  void
1458  Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
1459  {
1460          _splicing = true;
1461
1462          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1463
1464                  if (exclude && (*i) == exclude) {
1465                          continue;
1466                  }
1467
1468                  if ((*i)->position() >= at) {
1469                          framepos_t new_pos = (*i)->position() + distance;
1470                          if (new_pos < 0) {
1471                                  new_pos = 0;
1472                          } else if (new_pos >= max_framepos - (*i)->length()) {
1473                                  new_pos = max_framepos - (*i)->length();
1474                          }
1475
1476                          (*i)->set_position (new_pos);
1477                  }
1478          }
1479
1480          _splicing = false;
1481
1482          notify_contents_changed ();
1483 }
1484
1485 void
1486 Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude)
1487 {
1488         {
1489                 RegionWriteLock rl (this);
1490                 core_ripple (at, distance, exclude);
1491         }
1492 }
1493
1494 void
1495 Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude)
1496 {
1497         core_ripple (at, distance, exclude);
1498 }
1499
1500 void
1501 Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
1502 {
1503         if (distance == 0) {
1504                 return;
1505         }
1506
1507         _rippling = true;
1508         RegionListProperty copy = regions;
1509         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
1510                 assert (i != copy.end());
1511
1512                 if (exclude) {
1513                         if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) {
1514                                 continue;
1515                         }
1516                 }
1517
1518                 if ((*i)->position() >= at) {
1519                         framepos_t new_pos = (*i)->position() + distance;
1520                         framepos_t limit = max_framepos - (*i)->length();
1521                         if (new_pos < 0) {
1522                                 new_pos = 0;
1523                         } else if (new_pos >= limit ) {
1524                                 new_pos = limit;
1525                         } 
1526                                 
1527                         (*i)->set_position (new_pos);
1528                 }
1529         }
1530
1531         _rippling = false;
1532         notify_contents_changed ();
1533 }
1534
1535
1536 void
1537 Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1538 {
1539          if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
1540                  return;
1541          }
1542
1543          if (what_changed.contains (Properties::position)) {
1544
1545                  /* remove it from the list then add it back in
1546                     the right place again.
1547                  */
1548
1549                  RegionSortByPosition cmp;
1550
1551                  RegionList::iterator i = find (regions.begin(), regions.end(), region);
1552
1553                  if (i == regions.end()) {
1554                          /* the region bounds are being modified but its not currently
1555                             in the region list. we will use its bounds correctly when/if
1556                             it is added
1557                          */
1558                          return;
1559                  }
1560
1561                  regions.erase (i);
1562                  regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1563          }
1564
1565          if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
1566
1567                  frameoffset_t delta = 0;
1568
1569                  if (what_changed.contains (Properties::position)) {
1570                          delta = region->position() - region->last_position();
1571                  }
1572
1573                  if (what_changed.contains (Properties::length)) {
1574                          delta += region->length() - region->last_length();
1575                  }
1576
1577                  if (delta) {
1578                          possibly_splice (region->last_position() + region->last_length(), delta, region);
1579                  }
1580
1581                  if (holding_state ()) {
1582                          pending_bounds.push_back (region);
1583                  } else {
1584                          notify_contents_changed ();
1585                          relayer ();
1586                          list<Evoral::Range<framepos_t> > xf;
1587                          xf.push_back (Evoral::Range<framepos_t> (region->last_range()));
1588                          xf.push_back (Evoral::Range<framepos_t> (region->range()));
1589                          coalesce_and_check_crossfades (xf);
1590                  }
1591          }
1592  }
1593
1594  void
1595  Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
1596  {
1597          boost::shared_ptr<Region> region (weak_region.lock());
1598
1599          if (!region) {
1600                  return;
1601          }
1602
1603          /* this makes a virtual call to the right kind of playlist ... */
1604
1605          region_changed (what_changed, region);
1606  }
1607
1608  bool
1609  Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
1610  {
1611          PropertyChange our_interests;
1612          PropertyChange bounds;
1613          PropertyChange pos_and_length;
1614          bool save = false;
1615
1616          if (in_set_state || in_flush) {
1617                  return false;
1618          }
1619
1620          our_interests.add (Properties::muted);
1621          our_interests.add (Properties::layer);
1622          our_interests.add (Properties::opaque);
1623
1624          bounds.add (Properties::start);
1625          bounds.add (Properties::position);
1626          bounds.add (Properties::length);
1627
1628          pos_and_length.add (Properties::position);
1629          pos_and_length.add (Properties::length);
1630
1631          if (what_changed.contains (bounds)) {
1632                  region_bounds_changed (what_changed, region);
1633                  save = !(_splicing || _nudging);
1634          }
1635
1636          if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
1637                  notify_region_moved (region);
1638          } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1639                  notify_region_end_trimmed (region);
1640          } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
1641                  notify_region_start_trimmed (region);
1642          }
1643
1644          /* don't notify about layer changes, since we are the only object that can initiate
1645             them, and we notify in ::relayer()
1646          */
1647
1648          if (what_changed.contains (our_interests)) {
1649                  save = true;
1650          }
1651
1652          mark_session_dirty ();
1653      
1654          return save;
1655  }
1656
1657  void
1658  Playlist::drop_regions ()
1659  {
1660          RegionWriteLock rl (this);
1661          regions.clear ();
1662          all_regions.clear ();
1663  }
1664
1665  void
1666  Playlist::sync_all_regions_with_regions ()
1667  {
1668          RegionWriteLock rl (this);
1669
1670          all_regions.clear ();
1671
1672          for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1673                  all_regions.insert (*i);
1674          }
1675  }
1676
1677  void
1678  Playlist::clear (bool with_signals)
1679  {
1680          {
1681                  RegionWriteLock rl (this);
1682
1683                  region_state_changed_connections.drop_connections ();
1684
1685                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1686                          pending_removes.insert (*i);
1687                  }
1688
1689                  regions.clear ();
1690
1691                  for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1692                          remove_dependents (*s);
1693                  }
1694          }
1695
1696          if (with_signals) {
1697
1698                  for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
1699                          RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
1700                  }
1701
1702                  pending_removes.clear ();
1703                  pending_contents_change = false;
1704                  ContentsChanged ();
1705          }
1706
1707  }
1708
1709  /***********************************************************************
1710   FINDING THINGS
1711   **********************************************************************/
1712
1713 boost::shared_ptr<RegionList>
1714 Playlist::regions_at (framepos_t frame)
1715 {
1716         RegionReadLock rlock (this);
1717         return find_regions_at (frame);
1718 }
1719
1720  uint32_t
1721  Playlist::count_regions_at (framepos_t frame) const
1722  {
1723          RegionReadLock rlock (const_cast<Playlist*>(this));
1724          uint32_t cnt = 0;
1725
1726          for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1727                  if ((*i)->covers (frame)) {
1728                          cnt++;
1729                  }
1730          }
1731
1732          return cnt;
1733  }
1734
1735  boost::shared_ptr<Region>
1736  Playlist::top_region_at (framepos_t frame)
1737
1738  {
1739          RegionReadLock rlock (this);
1740          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1741          boost::shared_ptr<Region> region;
1742
1743          if (rlist->size()) {
1744                  RegionSortByLayer cmp;
1745                  rlist->sort (cmp);
1746                  region = rlist->back();
1747          }
1748
1749          return region;
1750  }
1751
1752  boost::shared_ptr<Region>
1753  Playlist::top_unmuted_region_at (framepos_t frame)
1754
1755  {
1756          RegionReadLock rlock (this);
1757          boost::shared_ptr<RegionList> rlist = find_regions_at (frame);
1758
1759          for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
1760
1761                  RegionList::iterator tmp = i;
1762                  ++tmp;
1763
1764                  if ((*i)->muted()) {
1765                          rlist->erase (i);
1766                  }
1767
1768                  i = tmp;
1769          }
1770
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<RegionList>
1783 Playlist::find_regions_at (framepos_t frame)
1784 {
1785         /* Caller must hold lock */
1786         
1787         boost::shared_ptr<RegionList> rlist (new RegionList);
1788         
1789         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1790                 if ((*i)->covers (frame)) {
1791                         rlist->push_back (*i);
1792                 }
1793         }
1794         
1795         return rlist;
1796 }
1797
1798 boost::shared_ptr<RegionList>
1799 Playlist::regions_with_start_within (Evoral::Range<framepos_t> range)
1800 {
1801         RegionReadLock rlock (this);
1802         boost::shared_ptr<RegionList> rlist (new RegionList);
1803
1804         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1805                 if ((*i)->first_frame() >= range.from && (*i)->first_frame() <= range.to) {
1806                         rlist->push_back (*i);
1807                 }
1808         }
1809
1810         return rlist;
1811 }
1812
1813 boost::shared_ptr<RegionList>
1814 Playlist::regions_with_end_within (Evoral::Range<framepos_t> range)
1815 {
1816         RegionReadLock rlock (this);
1817         boost::shared_ptr<RegionList> rlist (new RegionList);
1818
1819         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1820                 if ((*i)->last_frame() >= range.from && (*i)->last_frame() <= range.to) {
1821                         rlist->push_back (*i);
1822                 }
1823         }
1824
1825         return rlist;
1826 }
1827
1828 /** @param start Range start.
1829  *  @param end Range end.
1830  *  @return regions which have some part within this range.
1831  */
1832 boost::shared_ptr<RegionList>
1833 Playlist::regions_touched (framepos_t start, framepos_t end)
1834 {
1835         RegionReadLock rlock (this);
1836         return regions_touched_locked (start, end);
1837 }
1838
1839 boost::shared_ptr<RegionList>
1840 Playlist::regions_touched_locked (framepos_t start, framepos_t end)
1841 {
1842         boost::shared_ptr<RegionList> rlist (new RegionList);
1843         
1844         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1845                 if ((*i)->coverage (start, end) != Evoral::OverlapNone) {
1846                         rlist->push_back (*i);
1847                 }
1848         }
1849         
1850         return rlist;
1851 }
1852
1853 framepos_t
1854 Playlist::find_next_transient (framepos_t from, int dir)
1855 {
1856         RegionReadLock rlock (this);
1857         AnalysisFeatureList points;
1858         AnalysisFeatureList these_points;
1859         
1860         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1861                 if (dir > 0) {
1862                         if ((*i)->last_frame() < from) {
1863                                 continue;
1864                         }
1865                 } else {
1866                         if ((*i)->first_frame() > from) {
1867                                 continue;
1868                         }
1869                 }
1870                 
1871                 (*i)->get_transients (these_points);
1872                 
1873                 /* add first frame, just, err, because */
1874                 
1875                 these_points.push_back ((*i)->first_frame());
1876                 
1877                 points.insert (points.end(), these_points.begin(), these_points.end());
1878                 these_points.clear ();
1879         }
1880         
1881         if (points.empty()) {
1882                 return -1;
1883         }
1884         
1885         TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
1886         bool reached = false;
1887         
1888         if (dir > 0) {
1889                 for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
1890                         if ((*x) >= from) {
1891                                 reached = true;
1892                         }
1893                         
1894                         if (reached && (*x) > from) {
1895                                 return *x;
1896                         }
1897                 }
1898         } else {
1899                 for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
1900                         if ((*x) <= from) {
1901                                 reached = true;
1902                         }
1903                         
1904                         if (reached && (*x) < from) {
1905                                 return *x;
1906                         }
1907                 }
1908         }
1909         
1910         return -1;
1911 }
1912
1913 boost::shared_ptr<Region>
1914 Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
1915 {
1916         RegionReadLock rlock (this);
1917         boost::shared_ptr<Region> ret;
1918         framepos_t closest = max_framepos;
1919         
1920         bool end_iter = false;
1921         
1922         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1923                 
1924                 if(end_iter) break;
1925                 
1926                 frameoffset_t distance;
1927                 boost::shared_ptr<Region> r = (*i);
1928                 framepos_t pos = 0;
1929                 
1930                 switch (point) {
1931                 case Start:
1932                         pos = r->first_frame ();
1933                         break;
1934                 case End:
1935                         pos = r->last_frame ();
1936                         break;
1937                 case SyncPoint:
1938                         pos = r->sync_position ();
1939                         break;
1940                 }
1941                 
1942                 switch (dir) {
1943                 case 1: /* forwards */
1944                         
1945                         if (pos > frame) {
1946                                 if ((distance = pos - frame) < closest) {
1947                                         closest = distance;
1948                                         ret = r;
1949                                         end_iter = true;
1950                                 }
1951                         }
1952                         
1953                         break;
1954                         
1955                 default: /* backwards */
1956                         
1957                         if (pos < frame) {
1958                                 if ((distance = frame - pos) < closest) {
1959                                         closest = distance;
1960                                         ret = r;
1961                                 }
1962                         } else {
1963                                 end_iter = true;
1964                         }
1965                         
1966                         break;
1967                 }
1968         }
1969         
1970         return ret;
1971 }
1972
1973  framepos_t
1974  Playlist::find_next_region_boundary (framepos_t frame, int dir)
1975  {
1976          RegionReadLock rlock (this);
1977
1978          framepos_t closest = max_framepos;
1979          framepos_t ret = -1;
1980
1981          if (dir > 0) {
1982
1983                  for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1984
1985                          boost::shared_ptr<Region> r = (*i);
1986                          frameoffset_t distance;
1987
1988                          if (r->first_frame() > frame) {
1989
1990                                  distance = r->first_frame() - frame;
1991
1992                                  if (distance < closest) {
1993                                          ret = r->first_frame();
1994                                          closest = distance;
1995                                  }
1996                          }
1997
1998                          if (r->last_frame () > frame) {
1999
2000                                  distance = r->last_frame () - frame;
2001
2002                                  if (distance < closest) {
2003                                          ret = r->last_frame ();
2004                                          closest = distance;
2005                                  }
2006                          }
2007                  }
2008
2009          } else {
2010
2011                  for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
2012
2013                          boost::shared_ptr<Region> r = (*i);
2014                          frameoffset_t distance;
2015
2016                          if (r->last_frame() < frame) {
2017
2018                                  distance = frame - r->last_frame();
2019
2020                                  if (distance < closest) {
2021                                          ret = r->last_frame();
2022                                          closest = distance;
2023                                  }
2024                          }
2025
2026                          if (r->first_frame() < frame) {
2027
2028                                  distance = frame - r->first_frame();
2029
2030                                  if (distance < closest) {
2031                                          ret = r->first_frame();
2032                                          closest = distance;
2033                                  }
2034                          }
2035                  }
2036          }
2037
2038          return ret;
2039  }
2040
2041
2042  /***********************************************************************/
2043
2044
2045
2046
2047  void
2048  Playlist::mark_session_dirty ()
2049  {
2050          if (!in_set_state && !holding_state ()) {
2051                  _session.set_dirty();
2052          }
2053  }
2054
2055  void
2056  Playlist::rdiff (vector<Command*>& cmds) const
2057  {
2058          RegionReadLock rlock (const_cast<Playlist *> (this));
2059          Stateful::rdiff (cmds);
2060  }
2061
2062  void
2063  Playlist::clear_owned_changes ()
2064  {
2065          RegionReadLock rlock (this);
2066          Stateful::clear_owned_changes ();
2067  }
2068
2069  void
2070  Playlist::update (const RegionListProperty::ChangeRecord& change)
2071  {
2072          DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n",
2073                                                          name(), change.added.size(), change.removed.size()));
2074
2075          freeze ();
2076          /* add the added regions */
2077          for (RegionListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
2078                  add_region_internal ((*i), (*i)->position());
2079          }
2080          /* remove the removed regions */
2081          for (RegionListProperty::ChangeContainer::const_iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
2082                  remove_region (*i);
2083          }
2084
2085          thaw ();
2086  }
2087
2088  int
2089  Playlist::set_state (const XMLNode& node, int version)
2090  {
2091          XMLNode *child;
2092          XMLNodeList nlist;
2093          XMLNodeConstIterator niter;
2094          XMLPropertyList plist;
2095          XMLPropertyConstIterator piter;
2096          XMLProperty *prop;
2097          boost::shared_ptr<Region> region;
2098          string region_name;
2099          bool seen_region_nodes = false;
2100          int ret = 0;
2101
2102          in_set_state++;
2103
2104          if (node.name() != "Playlist") {
2105                  in_set_state--;
2106                  return -1;
2107          }
2108
2109          freeze ();
2110
2111          plist = node.properties();
2112
2113          set_id (node);
2114
2115          for (piter = plist.begin(); piter != plist.end(); ++piter) {
2116
2117                  prop = *piter;
2118
2119                  if (prop->name() == X_("name")) {
2120                          _name = prop->value();
2121                          _set_sort_id ();
2122                  } else if (prop->name() == X_("orig-diskstream-id")) {
2123                          /* XXX legacy session: fix up later */
2124                          _orig_track_id = prop->value ();
2125                  } else if (prop->name() == X_("orig-track-id")) {
2126                          _orig_track_id = prop->value ();
2127                  } else if (prop->name() == X_("frozen")) {
2128                          _frozen = string_is_affirmative (prop->value());
2129                  } else if (prop->name() == X_("combine-ops")) {
2130                          _combine_ops = atoi (prop->value());
2131                  }
2132          }
2133
2134          clear (true);
2135
2136          nlist = node.children();
2137
2138          for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2139
2140                  child = *niter;
2141
2142                  if (child->name() == "Region") {
2143
2144                          seen_region_nodes = true;
2145
2146                          if ((prop = child->property ("id")) == 0) {
2147                                  error << _("region state node has no ID, ignored") << endmsg;
2148                                  continue;
2149                          }
2150
2151                          ID id = prop->value ();
2152
2153                          if ((region = region_by_id (id))) {
2154
2155                                  region->suspend_property_changes ();
2156
2157                                  if (region->set_state (*child, version)) {
2158                                          region->resume_property_changes ();
2159                                          continue;
2160                                  }
2161
2162                          } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
2163                                  region->suspend_property_changes ();
2164                          } else {
2165                                  error << _("Playlist: cannot create region from XML") << endmsg;
2166                                 return -1;
2167                         }
2168
2169                          {
2170                                  RegionWriteLock rlock (this);
2171                                  add_region_internal (region, region->position());
2172                          }
2173                         
2174                         region->resume_property_changes ();
2175
2176                 }
2177         }
2178
2179         if (seen_region_nodes && regions.empty()) {
2180                 ret = -1;
2181         }
2182                 
2183         thaw ();
2184         notify_contents_changed ();
2185
2186         in_set_state--;
2187         first_set_state = false;
2188
2189         return ret;
2190 }
2191
2192 XMLNode&
2193 Playlist::get_state()
2194 {
2195         return state (true);
2196 }
2197
2198 XMLNode&
2199 Playlist::get_template()
2200 {
2201         return state (false);
2202 }
2203
2204 /** @param full_state true to include regions in the returned state, otherwise false.
2205  */
2206 XMLNode&
2207 Playlist::state (bool full_state)
2208 {
2209         XMLNode *node = new XMLNode (X_("Playlist"));
2210         char buf[64];
2211
2212         node->add_property (X_("id"), id().to_s());
2213         node->add_property (X_("name"), _name);
2214         node->add_property (X_("type"), _type.to_string());
2215
2216         _orig_track_id.print (buf, sizeof (buf));
2217         node->add_property (X_("orig-track-id"), buf);
2218         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
2219
2220         if (full_state) {
2221                 RegionReadLock rlock (this);
2222
2223                 snprintf (buf, sizeof (buf), "%u", _combine_ops);
2224                 node->add_property ("combine-ops", buf);
2225
2226                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2227                         node->add_child_nocopy ((*i)->get_state());
2228                 }
2229         }
2230
2231         if (_extra_xml) {
2232                 node->add_child_copy (*_extra_xml);
2233         }
2234
2235         return *node;
2236 }
2237
2238 bool
2239 Playlist::empty() const
2240 {
2241         RegionReadLock rlock (const_cast<Playlist *>(this));
2242         return regions.empty();
2243 }
2244
2245 uint32_t
2246 Playlist::n_regions() const
2247 {
2248         RegionReadLock rlock (const_cast<Playlist *>(this));
2249         return regions.size();
2250 }
2251
2252 /** @return true if the all_regions list is empty, ie this playlist
2253  *  has never had a region added to it.
2254  */
2255 bool
2256 Playlist::all_regions_empty() const
2257 {
2258         RegionReadLock rl (const_cast<Playlist *> (this));
2259         return all_regions.empty();
2260 }
2261
2262 pair<framepos_t, framepos_t>
2263 Playlist::get_extent () const
2264 {
2265         RegionReadLock rlock (const_cast<Playlist *>(this));
2266         return _get_extent ();
2267 }
2268
2269 pair<framepos_t, framepos_t>
2270 Playlist::get_extent_with_endspace () const
2271 {
2272         pair<framepos_t, framepos_t> l = get_extent();
2273         l.second += _end_space;
2274         return l;
2275 }
2276
2277 pair<framepos_t, framepos_t>
2278 Playlist::_get_extent () const
2279 {
2280         pair<framepos_t, framepos_t> ext (max_framepos, 0);
2281
2282         if (regions.empty()) {
2283                 ext.first = 0;
2284                 return ext;
2285         }
2286
2287         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2288                 pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
2289                 if (e.first < ext.first) {
2290                         ext.first = e.first;
2291                 }
2292                 if (e.second > ext.second) {
2293                         ext.second = e.second;
2294                 }
2295         }
2296
2297         return ext;
2298 }
2299
2300 string
2301 Playlist::bump_name (string name, Session &session)
2302 {
2303         string newname = name;
2304
2305         do {
2306                 newname = bump_name_once (newname, '.');
2307         } while (session.playlists->by_name (newname)!=NULL);
2308
2309         return newname;
2310 }
2311
2312
2313 layer_t
2314 Playlist::top_layer() const
2315 {
2316         RegionReadLock rlock (const_cast<Playlist *> (this));
2317         layer_t top = 0;
2318
2319         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2320                 top = max (top, (*i)->layer());
2321         }
2322         return top;
2323 }
2324
2325 void
2326 Playlist::set_edit_mode (EditMode mode)
2327 {
2328         _edit_mode = mode;
2329 }
2330
2331 struct RelayerSort {
2332         bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2333                 return a->layering_index() < b->layering_index();
2334         }
2335 };
2336
2337 /** Set a new layer for a region.  This adjusts the layering indices of all
2338  *  regions in the playlist to put the specified region in the appropriate
2339  *  place.  The actual layering will be fixed up when relayer() happens.
2340  */
2341
2342 void
2343 Playlist::set_layer (boost::shared_ptr<Region> region, double new_layer)
2344 {
2345         /* Remove the layer we are setting from our region list, and sort it
2346         *  using the layer indeces.
2347         */
2348
2349         RegionList copy = regions.rlist();
2350         copy.remove (region);
2351         copy.sort (RelayerSort ());
2352
2353         /* Put region back in the right place */
2354         RegionList::iterator i = copy.begin();
2355         while (i != copy.end ()) {
2356                 if ((*i)->layer() > new_layer) {
2357                         break;
2358                 }
2359                 ++i;
2360         }
2361         
2362         copy.insert (i, region);
2363
2364         setup_layering_indices (copy);
2365 }
2366
2367 void
2368 Playlist::setup_layering_indices (RegionList const & regions)
2369 {
2370         uint64_t j = 0;
2371
2372         for (RegionList::const_iterator k = regions.begin(); k != regions.end(); ++k) {
2373                 (*k)->set_layering_index (j++);
2374         }
2375 }
2376
2377 struct LaterHigherSort {
2378         bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
2379                 return a->position() < b->position();
2380         }
2381 };
2382
2383 /** Take the layering indices of each of our regions, compute the layers
2384  *  that they should be on, and write the layers back to the regions.
2385  */
2386 void
2387 Playlist::relayer ()
2388 {
2389         /* never compute layers when setting from XML */
2390
2391         if (in_set_state) {
2392                 return;
2393         }
2394
2395         /* Build up a new list of regions on each layer, stored in a set of lists
2396            each of which represent some period of time on some layer.  The idea
2397            is to avoid having to search the entire region list to establish whether
2398            each region overlaps another */
2399
2400         /* how many pieces to divide this playlist's time up into */
2401         int const divisions = 512;
2402
2403         /* find the start and end positions of the regions on this playlist */
2404         framepos_t start = INT64_MAX;
2405         framepos_t end = 0;
2406         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2407                 start = min (start, (*i)->position());
2408                 end = max (end, (*i)->position() + (*i)->length());
2409         }
2410
2411         /* hence the size of each time division */
2412         double const division_size = (end - start) / double (divisions);
2413
2414         vector<vector<RegionList> > layers;
2415         layers.push_back (vector<RegionList> (divisions));
2416
2417         /* Sort our regions into layering index order (for manual layering) or position order (for later is higher)*/
2418         RegionList copy = regions.rlist();
2419         switch (Config->get_layer_model()) {
2420                 case LaterHigher:
2421                         copy.sort (LaterHigherSort ());
2422                         break;
2423                 case Manual:
2424                         copy.sort (RelayerSort ());
2425                         break;
2426         }
2427
2428         DEBUG_TRACE (DEBUG::Layering, "relayer() using:\n");
2429         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2430                 DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 %2\n", (*i)->name(), (*i)->layering_index()));
2431         }
2432
2433         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2434
2435                 /* find the time divisions that this region covers; if there are no regions on the list,
2436                    division_size will equal 0 and in this case we'll just say that
2437                    start_division = end_division = 0.
2438                 */
2439                 int start_division = 0;
2440                 int end_division = 0;
2441
2442                 if (division_size > 0) {
2443                         start_division = floor ( ((*i)->position() - start) / division_size);
2444                         end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
2445                         if (end_division == divisions) {
2446                                 end_division--;
2447                         }
2448                 }
2449
2450                 assert (divisions == 0 || end_division < divisions);
2451
2452                 /* find the lowest layer that this region can go on */
2453                 size_t j = layers.size();
2454                 while (j > 0) {
2455                         /* try layer j - 1; it can go on if it overlaps no other region
2456                            that is already on that layer
2457                         */
2458
2459                         bool overlap = false;
2460                         for (int k = start_division; k <= end_division; ++k) {
2461                                 RegionList::iterator l = layers[j-1][k].begin ();
2462                                 while (l != layers[j-1][k].end()) {
2463                                         if ((*l)->overlap_equivalent (*i)) {
2464                                                 overlap = true;
2465                                                 break;
2466                                         }
2467                                         l++;
2468                                 }
2469
2470                                 if (overlap) {
2471                                         break;
2472                                 }
2473                         }
2474
2475                         if (overlap) {
2476                                 /* overlap, so we must use layer j */
2477                                 break;
2478                         }
2479
2480                         --j;
2481                 }
2482
2483                 if (j == layers.size()) {
2484                         /* we need a new layer for this region */
2485                         layers.push_back (vector<RegionList> (divisions));
2486                 }
2487
2488                 /* put a reference to this region in each of the divisions that it exists in */
2489                 for (int k = start_division; k <= end_division; ++k) {
2490                         layers[j][k].push_back (*i);
2491                 }
2492
2493                 (*i)->set_layer (j);
2494         }
2495
2496         /* It's a little tricky to know when we could avoid calling this; e.g. if we are
2497            relayering because we just removed the only region on the top layer, nothing will
2498            appear to have changed, but the StreamView must still sort itself out.  We could
2499            probably keep a note of the top layer last time we relayered, and check that,
2500            but premature optimisation &c...
2501         */
2502         notify_layering_changed ();
2503
2504         /* This relayer() may have been called as a result of a region removal, in which
2505            case we need to setup layering indices to account for the one that has just
2506            gone away.
2507         */
2508         setup_layering_indices (copy);
2509 }
2510
2511 void
2512 Playlist::raise_region (boost::shared_ptr<Region> region)
2513 {
2514         set_layer (region, region->layer() + 1.5);
2515         relayer ();
2516 }
2517
2518 void
2519 Playlist::lower_region (boost::shared_ptr<Region> region)
2520 {
2521         set_layer (region, region->layer() - 1.5);
2522         relayer ();
2523 }
2524
2525 void
2526 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
2527 {
2528         set_layer (region, DBL_MAX);
2529         relayer ();
2530 }
2531
2532 void
2533 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
2534 {
2535         set_layer (region, -0.5);
2536         relayer ();
2537 }
2538
2539 void
2540 Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
2541 {
2542         RegionList::iterator i;
2543         bool moved = false;
2544
2545         _nudging = true;
2546
2547         {
2548                 RegionWriteLock rlock (const_cast<Playlist *> (this));
2549
2550                 for (i = regions.begin(); i != regions.end(); ++i) {
2551
2552                         if ((*i)->position() >= start) {
2553
2554                                 framepos_t new_pos;
2555
2556                                 if (forwards) {
2557
2558                                         if ((*i)->last_frame() > max_framepos - distance) {
2559                                                 new_pos = max_framepos - (*i)->length();
2560                                         } else {
2561                                                 new_pos = (*i)->position() + distance;
2562                                         }
2563
2564                                 } else {
2565
2566                                         if ((*i)->position() > distance) {
2567                                                 new_pos = (*i)->position() - distance;
2568                                         } else {
2569                                                 new_pos = 0;
2570                                         }
2571                                 }
2572
2573                                 (*i)->set_position (new_pos);
2574                                 moved = true;
2575                         }
2576                 }
2577         }
2578
2579         if (moved) {
2580                 _nudging = false;
2581                 notify_contents_changed ();
2582         }
2583
2584 }
2585
2586 bool
2587 Playlist::uses_source (boost::shared_ptr<const Source> src) const
2588 {
2589         RegionReadLock rlock (const_cast<Playlist*> (this));
2590
2591         for (set<boost::shared_ptr<Region> >::const_iterator r = all_regions.begin(); r != all_regions.end(); ++r) {
2592                 if ((*r)->uses_source (src)) {
2593                         return true;
2594                 }
2595         }
2596
2597         return false;
2598 }
2599
2600 boost::shared_ptr<Region>
2601 Playlist::find_region (const ID& id) const
2602 {
2603         RegionReadLock rlock (const_cast<Playlist*> (this));
2604
2605         /* searches all regions currently in use by the playlist */
2606
2607         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2608                 if ((*i)->id() == id) {
2609                         return *i;
2610                 }
2611         }
2612
2613         return boost::shared_ptr<Region> ();
2614 }
2615
2616 uint32_t
2617 Playlist::region_use_count (boost::shared_ptr<Region> r) const
2618 {
2619         RegionReadLock rlock (const_cast<Playlist*> (this));
2620         uint32_t cnt = 0;
2621
2622         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2623                 if ((*i) == r) {
2624                         cnt++;
2625                 }
2626         }
2627
2628         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
2629         for (RegionFactory::CompoundAssociations::iterator it = cassocs.begin(); it != cassocs.end(); ++it) {
2630                 /* check if region is used in a compound */
2631                 if (it->second == r) {
2632                         /* region is referenced as 'original' of a compound */
2633                         ++cnt;
2634                         break;
2635                 }
2636                 if (r->whole_file() && r->max_source_level() > 0) {
2637                         /* region itself ia a compound.
2638                          * the compound regions are not referenced -> check regions inside compound
2639                          */
2640                         const SourceList& sl = r->sources();
2641                         for (SourceList::const_iterator s = sl.begin(); s != sl.end(); ++s) {
2642                                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource>(*s);
2643                                 if (!ps) continue;
2644                                 if (ps->playlist()->region_use_count(it->first)) {
2645                                         // break out of both loops
2646                                         return ++cnt;
2647                                 }
2648                         }
2649                 }
2650         }
2651         return cnt;
2652 }
2653
2654 boost::shared_ptr<Region>
2655 Playlist::region_by_id (const ID& id) const
2656 {
2657         /* searches all regions ever added to this playlist */
2658
2659         for (set<boost::shared_ptr<Region> >::const_iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2660                 if ((*i)->id() == id) {
2661                         return *i;
2662                 }
2663         }
2664         return boost::shared_ptr<Region> ();
2665 }
2666
2667 void
2668 Playlist::dump () const
2669 {
2670         boost::shared_ptr<Region> r;
2671
2672         cerr << "Playlist \"" << _name << "\" " << endl
2673              << regions.size() << " regions "
2674              << endl;
2675
2676         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2677                 r = *i;
2678                 cerr << "  " << r->name() << " ["
2679                      << r->start() << "+" << r->length()
2680                      << "] at "
2681                      << r->position()
2682                      << " on layer "
2683                      << r->layer ()
2684                      << endl;
2685         }
2686 }
2687
2688 void
2689 Playlist::set_frozen (bool yn)
2690 {
2691         _frozen = yn;
2692 }
2693
2694 void
2695 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2696 {
2697         bool moved = false;
2698
2699         if (region->locked()) {
2700                 return;
2701         }
2702
2703         _shuffling = true;
2704
2705         {
2706                 RegionWriteLock rlock (const_cast<Playlist*> (this));
2707
2708
2709                 if (dir > 0) {
2710
2711                         RegionList::iterator next;
2712
2713                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2714                                 if ((*i) == region) {
2715                                         next = i;
2716                                         ++next;
2717
2718                                         if (next != regions.end()) {
2719
2720                                                 if ((*next)->locked()) {
2721                                                         break;
2722                                                 }
2723
2724                                                 framepos_t new_pos;
2725
2726                                                 if ((*next)->position() != region->last_frame() + 1) {
2727                                                         /* they didn't used to touch, so after shuffle,
2728                                                            just have them swap positions.
2729                                                         */
2730                                                         new_pos = (*next)->position();
2731                                                 } else {
2732                                                         /* they used to touch, so after shuffle,
2733                                                            make sure they still do. put the earlier
2734                                                            region where the later one will end after
2735                                                            it is moved.
2736                                                         */
2737                                                         new_pos = region->position() + (*next)->length();
2738                                                 }
2739
2740                                                 (*next)->set_position (region->position());
2741                                                 region->set_position (new_pos);
2742
2743                                                 /* avoid a full sort */
2744
2745                                                 regions.erase (i); // removes the region from the list */
2746                                                 next++;
2747                                                 regions.insert (next, region); // adds it back after next
2748
2749                                                 moved = true;
2750                                         }
2751                                         break;
2752                                 }
2753                         }
2754                 } else {
2755
2756                         RegionList::iterator prev = regions.end();
2757
2758                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
2759                                 if ((*i) == region) {
2760
2761                                         if (prev != regions.end()) {
2762
2763                                                 if ((*prev)->locked()) {
2764                                                         break;
2765                                                 }
2766
2767                                                 framepos_t new_pos;
2768                                                 if (region->position() != (*prev)->last_frame() + 1) {
2769                                                         /* they didn't used to touch, so after shuffle,
2770                                                            just have them swap positions.
2771                                                         */
2772                                                         new_pos = region->position();
2773                                                 } else {
2774                                                         /* they used to touch, so after shuffle,
2775                                                            make sure they still do. put the earlier
2776                                                            one where the later one will end after
2777                                                         */
2778                                                         new_pos = (*prev)->position() + region->length();
2779                                                 }
2780
2781                                                 region->set_position ((*prev)->position());
2782                                                 (*prev)->set_position (new_pos);
2783
2784                                                 /* avoid a full sort */
2785
2786                                                 regions.erase (i); // remove region
2787                                                 regions.insert (prev, region); // insert region before prev
2788
2789                                                 moved = true;
2790                                         }
2791
2792                                         break;
2793                                 }
2794                         }
2795                 }
2796         }
2797
2798         _shuffling = false;
2799
2800         if (moved) {
2801
2802                 relayer ();
2803                 notify_contents_changed();
2804         }
2805
2806 }
2807
2808 bool
2809 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
2810 {
2811         RegionReadLock rlock (const_cast<Playlist*> (this));
2812
2813         if (regions.size() > 1) {
2814                 return true;
2815         }
2816
2817         return false;
2818 }
2819
2820 void
2821 Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude)
2822 {
2823         ripple_locked (at, distance, exclude);
2824 }
2825
2826 void
2827 Playlist::update_after_tempo_map_change ()
2828 {
2829         RegionWriteLock rlock (const_cast<Playlist*> (this));
2830         RegionList copy (regions.rlist());
2831
2832         freeze ();
2833
2834         for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
2835                 (*i)->update_after_tempo_map_change ();
2836         }
2837
2838         thaw ();
2839 }
2840
2841 void
2842 Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
2843 {
2844         RegionWriteLock rl (this, false);
2845         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
2846                 s (*i);
2847         }
2848 }
2849
2850 bool
2851 Playlist::has_region_at (framepos_t const p) const
2852 {
2853         RegionReadLock (const_cast<Playlist *> (this));
2854
2855         RegionList::const_iterator i = regions.begin ();
2856         while (i != regions.end() && !(*i)->covers (p)) {
2857                 ++i;
2858         }
2859
2860         return (i != regions.end());
2861 }
2862
2863 /** Remove any region that uses a given source */
2864 void
2865 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
2866 {
2867         RegionWriteLock rl (this);
2868
2869         RegionList::iterator i = regions.begin();
2870         while (i != regions.end()) {
2871                 RegionList::iterator j = i;
2872                 ++j;
2873
2874                 if ((*i)->uses_source (s)) {
2875                         remove_region_internal (*i);
2876                 }
2877
2878                 i = j;
2879         }
2880 }
2881
2882 /** Look from a session frame time and find the start time of the next region
2883  *  which is on the top layer of this playlist.
2884  *  @param t Time to look from.
2885  *  @return Position of next top-layered region, or max_framepos if there isn't one.
2886  */
2887 framepos_t
2888 Playlist::find_next_top_layer_position (framepos_t t) const
2889 {
2890         RegionReadLock rlock (const_cast<Playlist *> (this));
2891
2892         layer_t const top = top_layer ();
2893
2894         RegionList copy = regions.rlist ();
2895         copy.sort (RegionSortByPosition ());
2896
2897         for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
2898                 if ((*i)->position() >= t && (*i)->layer() == top) {
2899                         return (*i)->position();
2900                 }
2901         }
2902
2903         return max_framepos;
2904 }
2905
2906 boost::shared_ptr<Region>
2907 Playlist::combine (const RegionList& r)
2908 {
2909         PropertyList plist;
2910         uint32_t channels = 0;
2911         uint32_t layer = 0;
2912         framepos_t earliest_position = max_framepos;
2913         vector<TwoRegions> old_and_new_regions;
2914         vector<boost::shared_ptr<Region> > originals;
2915         vector<boost::shared_ptr<Region> > copies;
2916         string parent_name;
2917         string child_name;
2918         uint32_t max_level = 0;
2919
2920         /* find the maximum depth of all the regions we're combining */
2921
2922         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2923                 max_level = max (max_level, (*i)->max_source_level());
2924         }
2925
2926         parent_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, true);
2927         child_name = RegionFactory::compound_region_name (name(), combine_ops(), max_level, false);
2928
2929         boost::shared_ptr<Playlist> pl = PlaylistFactory::create (_type, _session, parent_name, true);
2930
2931         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
2932                 earliest_position = min (earliest_position, (*i)->position());
2933         }
2934
2935         /* enable this so that we do not try to create xfades etc. as we add
2936          * regions
2937          */
2938
2939         pl->in_partition = true;
2940
2941         /* sort by position then layer.
2942          * route_time_axis passes 'selected_regions' - which is not sorted.
2943          * here we need the top-most first, then every layer's region sorted by position.
2944          */
2945         RegionList sorted(r);
2946         sorted.sort(RegionSortByLayerAndPosition());
2947
2948         for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
2949
2950                 /* copy the region */
2951
2952                 boost::shared_ptr<Region> original_region = (*i);
2953                 boost::shared_ptr<Region> copied_region = RegionFactory::create (original_region, false);
2954
2955                 old_and_new_regions.push_back (TwoRegions (original_region,copied_region));
2956                 originals.push_back (original_region);
2957                 copies.push_back (copied_region);
2958
2959                 RegionFactory::add_compound_association (original_region, copied_region);
2960
2961                 /* make position relative to zero */
2962
2963                 pl->add_region (copied_region, original_region->position() - earliest_position);
2964                 copied_region->set_layer (original_region->layer ());
2965
2966                 /* use the maximum number of channels for any region */
2967
2968                 channels = max (channels, original_region->n_channels());
2969
2970                 /* it will go above the layer of the highest existing region */
2971
2972                 layer = max (layer, original_region->layer());
2973         }
2974
2975         pl->in_partition = false;
2976
2977         pre_combine (copies);
2978
2979         /* now create a new PlaylistSource for each channel in the new playlist */
2980
2981         SourceList sources;
2982         pair<framepos_t,framepos_t> extent = pl->get_extent();
2983
2984         for (uint32_t chn = 0; chn < channels; ++chn) {
2985                 sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
2986
2987         }
2988
2989         /* now a new whole-file region using the list of sources */
2990
2991         plist.add (Properties::start, 0);
2992         plist.add (Properties::length, extent.second);
2993         plist.add (Properties::name, parent_name);
2994         plist.add (Properties::whole_file, true);
2995
2996         boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
2997
2998         /* now the non-whole-file region that we will actually use in the
2999          * playlist
3000          */
3001
3002         plist.clear ();
3003         plist.add (Properties::start, 0);
3004         plist.add (Properties::length, extent.second);
3005         plist.add (Properties::name, child_name);
3006         plist.add (Properties::layer, layer+1);
3007
3008         boost::shared_ptr<Region> compound_region = RegionFactory::create (parent_region, plist, true);
3009
3010         /* remove all the selected regions from the current playlist
3011          */
3012
3013         freeze ();
3014
3015         for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
3016                 remove_region (*i);
3017         }
3018
3019         /* do type-specific stuff with the originals and the new compound
3020            region
3021         */
3022
3023         post_combine (originals, compound_region);
3024
3025         /* add the new region at the right location */
3026
3027         add_region (compound_region, earliest_position);
3028
3029         _combine_ops++;
3030
3031         thaw ();
3032
3033         return compound_region;
3034 }
3035
3036 void
3037 Playlist::uncombine (boost::shared_ptr<Region> target)
3038 {
3039         boost::shared_ptr<PlaylistSource> pls;
3040         boost::shared_ptr<const Playlist> pl;
3041         vector<boost::shared_ptr<Region> > originals;
3042         vector<TwoRegions> old_and_new_regions;
3043
3044         // (1) check that its really a compound region
3045
3046         if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
3047                 return;
3048         }
3049
3050         pl = pls->playlist();
3051
3052         framepos_t adjusted_start = 0; // gcc isn't smart enough
3053         framepos_t adjusted_end = 0;   // gcc isn't smart enough
3054
3055         /* the leftmost (earliest) edge of the compound region
3056            starts at zero in its source, or larger if it
3057            has been trimmed or content-scrolled.
3058
3059            the rightmost (latest) edge of the compound region
3060            relative to its source is the starting point plus
3061            the length of the region.
3062         */
3063
3064         // (2) get all the original regions
3065
3066         const RegionList& rl (pl->region_list().rlist());
3067         RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
3068         frameoffset_t move_offset = 0;
3069
3070         /* there are two possibilities here:
3071            1) the playlist that the playlist source was based on
3072            is us, so just add the originals (which belonged to
3073            us anyway) back in the right place.
3074
3075            2) the playlist that the playlist source was based on
3076            is NOT us, so we need to make copies of each of
3077            the original regions that we find, and add them
3078            instead.
3079         */
3080         bool same_playlist = (pls->original() == id());
3081
3082         for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
3083
3084                 boost::shared_ptr<Region> current (*i);
3085
3086                 RegionFactory::CompoundAssociations::iterator ca = cassocs.find (*i);
3087
3088                 if (ca == cassocs.end()) {
3089                         continue;
3090                 }
3091
3092                 boost::shared_ptr<Region> original (ca->second);
3093                 cassocs.erase(ca);
3094                 bool modified_region;
3095
3096                 if (i == rl.begin()) {
3097                         move_offset = (target->position() - original->position()) - target->start();
3098                         adjusted_start = original->position() + target->start();
3099                         adjusted_end = adjusted_start + target->length();
3100                 }
3101
3102                 if (!same_playlist) {
3103                         framepos_t pos = original->position();
3104                         /* make a copy, but don't announce it */
3105                         original = RegionFactory::create (original, false);
3106                         /* the pure copy constructor resets position() to zero,
3107                            so fix that up.
3108                         */
3109                         original->set_position (pos);
3110                 }
3111
3112                 /* check to see how the original region (in the
3113                  * playlist before compounding occured) overlaps
3114                  * with the new state of the compound region.
3115                  */
3116
3117                 original->clear_changes ();
3118                 modified_region = false;
3119
3120                 switch (original->coverage (adjusted_start, adjusted_end)) {
3121                 case Evoral::OverlapNone:
3122                         /* original region does not cover any part
3123                            of the current state of the compound region
3124                         */
3125                         continue;
3126
3127                 case Evoral::OverlapInternal:
3128                         /* overlap is just a small piece inside the
3129                          * original so trim both ends
3130                          */
3131                         original->trim_to (adjusted_start, adjusted_end - adjusted_start);
3132                         modified_region = true;
3133                         break;
3134
3135                 case Evoral::OverlapExternal:
3136                         /* overlap fully covers original, so leave it
3137                            as is
3138                         */
3139                         break;
3140
3141                 case Evoral::OverlapEnd:
3142                         /* overlap starts within but covers end,
3143                            so trim the front of the region
3144                         */
3145                         original->trim_front (adjusted_start);
3146                         modified_region = true;
3147                         break;
3148
3149                 case Evoral::OverlapStart:
3150                         /* overlap covers start but ends within, so
3151                          * trim the end of the region.
3152                          */
3153                         original->trim_end (adjusted_end);
3154                         modified_region = true;
3155                         break;
3156                 }
3157
3158                 if (move_offset) {
3159                         /* fix the position to match any movement of the compound region.
3160                          */
3161                         original->set_position (original->position() + move_offset);
3162                         modified_region = true;
3163                 }
3164
3165                 if (modified_region) {
3166                         _session.add_command (new StatefulDiffCommand (original));
3167                 }
3168
3169                 /* and add to the list of regions waiting to be
3170                  * re-inserted
3171                  */
3172
3173                 originals.push_back (original);
3174                 old_and_new_regions.push_back (TwoRegions (*i, original));
3175         }
3176
3177         pre_uncombine (originals, target);
3178
3179         in_partition = true;
3180         freeze ();
3181
3182         // (3) remove the compound region
3183
3184         remove_region (target);
3185
3186         // (4) add the constituent regions
3187
3188         for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
3189                 add_region ((*i), (*i)->position());
3190                 set_layer((*i), (*i)->layer());
3191                 if (!RegionFactory::region_by_id((*i)->id())) {
3192                         RegionFactory::map_add(*i);
3193                 }
3194         }
3195
3196         in_partition = false;
3197         thaw ();
3198 }
3199
3200 void
3201 Playlist::fade_range (list<AudioRange>& ranges)
3202 {
3203          for (list<AudioRange>::iterator r = ranges.begin(); r != ranges.end(); ++r) {
3204                  for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3205                          (*i)->fade_range ((*r).start, (*r).end);
3206                  }
3207          }
3208 }
3209
3210 uint32_t
3211 Playlist::max_source_level () const
3212 {
3213         RegionReadLock rlock (const_cast<Playlist *> (this));
3214         uint32_t lvl = 0;
3215
3216         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
3217                 lvl = max (lvl, (*i)->max_source_level());
3218         }
3219
3220         return lvl;
3221 }
3222
3223 void
3224 Playlist::set_orig_track_id (const PBD::ID& id)
3225 {
3226         _orig_track_id = id;
3227 }
3228
3229 /** Take a list of ranges, coalesce any that can be coalesced, then call
3230  *  check_crossfades for each one.
3231  */
3232 void
3233 Playlist::coalesce_and_check_crossfades (list<Evoral::Range<framepos_t> > ranges)
3234 {
3235         /* XXX: it's a shame that this coalesce algorithm also exists in
3236            TimeSelection::consolidate().
3237         */
3238
3239         /* XXX: xfade: this is implemented in Evoral::RangeList */
3240
3241 restart:
3242         for (list<Evoral::Range<framepos_t> >::iterator i = ranges.begin(); i != ranges.end(); ++i) {
3243                 for (list<Evoral::Range<framepos_t> >::iterator j = ranges.begin(); j != ranges.end(); ++j) {
3244
3245                         if (i == j) {
3246                                 continue;
3247                         }
3248
3249                         // XXX i->from can be > i->to - is this right? coverage() will return OverlapNone in this case
3250                         if (Evoral::coverage (i->from, i->to, j->from, j->to) != Evoral::OverlapNone) {
3251                                 i->from = min (i->from, j->from);
3252                                 i->to = max (i->to, j->to);
3253                                 ranges.erase (j);
3254                                 goto restart;
3255                         }
3256                 }
3257         }
3258 }
3259
3260 void
3261 Playlist::set_capture_insertion_in_progress (bool yn)
3262 {
3263         _capture_insertion_underway = yn;
3264 }