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