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