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