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