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