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