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