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