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