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