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