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