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