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