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