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