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