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