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