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