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