new shuffle-splice mode; fix editormode selectors lacking text at startup; default...
[ardour.git] / libs / ardour / playlist.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <set>
21 #include <fstream>
22 #include <algorithm>
23 #include <unistd.h>
24 #include <cerrno>
25 #include <string>
26 #include <climits>
27
28 #include <sigc++/bind.h>
29
30 #include <pbd/failed_constructor.h>
31 #include <pbd/stl_delete.h>
32 #include <pbd/xml++.h>
33 #include <pbd/stacktrace.h>
34
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/region.h>
38 #include <ardour/region_factory.h>
39 #include <ardour/playlist_factory.h>
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46
47 struct ShowMeTheList {
48     ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
49     ~ShowMeTheList () { 
50             cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl; 
51     };
52     boost::shared_ptr<Playlist> playlist;
53     string name;
54 };
55
56 struct RegionSortByLayer {
57     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
58             return a->layer() < b->layer();
59     }
60 };
61
62 struct RegionSortByPosition {
63     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
64             return a->position() < b->position();
65     }
66 };
67
68 struct RegionSortByLastLayerOp {
69     bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
70             return a->last_layer_op() < b->last_layer_op();
71     }
72 };
73
74 Playlist::Playlist (Session& sess, string nom, bool hide)
75         : _session (sess)
76 {
77         init (hide);
78         first_set_state = false;
79         _name = nom;
80         
81 }
82
83 Playlist::Playlist (Session& sess, const XMLNode& node, bool hide)
84         : _session (sess)
85 {
86         init (hide);
87         _name = "unnamed"; /* reset by set_state */
88
89         /* set state called by derived class */
90 }
91
92 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
93         : _name (namestr), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
94 {
95         init (hide);
96
97         RegionList tmp;
98         other->copy_regions (tmp);
99         
100         in_set_state++;
101
102         for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
103                 add_region_internal( (*x), (*x)->position());
104         }
105
106         in_set_state--;
107
108         _splicing  = other->_splicing;
109         _nudging   = other->_nudging;
110         _edit_mode = other->_edit_mode;
111
112         in_set_state = 0;
113         first_set_state = false;
114         in_flush = false;
115         in_partition = false;
116         subcnt = 0;
117         _read_data_count = 0;
118         _frozen = other->_frozen;
119         
120         layer_op_counter = other->layer_op_counter;
121         freeze_length = other->freeze_length;
122 }
123
124 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
125         : _name (str), _session (other->_session), _orig_diskstream_id(other->_orig_diskstream_id)
126 {
127         RegionLock rlock2 (const_cast<Playlist*> (other.get()));
128
129         nframes_t end = start + cnt - 1;
130
131         init (hide);
132
133         in_set_state++;
134
135         for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
136
137                 boost::shared_ptr<Region> region;
138                 boost::shared_ptr<Region> new_region;
139                 nframes_t offset = 0;
140                 nframes_t position = 0;
141                 nframes_t len = 0;
142                 string    new_name;
143                 OverlapType overlap;
144
145                 region = *i;
146
147                 overlap = region->coverage (start, end);
148
149                 switch (overlap) {
150                 case OverlapNone:
151                         continue;
152
153                 case OverlapInternal:
154                         offset = start - region->position();
155                         position = 0;
156                         len = cnt;
157                         break;
158
159                 case OverlapStart:
160                         offset = 0;
161                         position = region->position() - start;
162                         len = end - region->position();
163                         break;
164
165                 case OverlapEnd:
166                         offset = start - region->position();
167                         position = 0;
168                         len = region->length() - offset;
169                         break;
170
171                 case OverlapExternal:
172                         offset = 0;
173                         position = region->position() - start;
174                         len = region->length();
175                         break;
176                 }
177
178                 _session.region_name (new_name, region->name(), false);
179
180                 new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
181
182                 add_region_internal (new_region, position);
183         }
184         
185         in_set_state--;
186         first_set_state = false;
187
188         /* this constructor does NOT notify others (session) */
189 }
190
191 void
192 Playlist::use ()
193 {
194         ++_refcnt;
195         InUse (true); /* EMIT SIGNAL */
196 }
197
198 void
199 Playlist::release ()
200 {
201         if (_refcnt > 0) {
202                 _refcnt--; 
203         }
204
205         if (_refcnt == 0) {
206                 InUse (false); /* EMIT SIGNAL */
207         }
208 }
209
210 void
211 Playlist::copy_regions (RegionList& newlist) const
212 {
213         RegionLock rlock (const_cast<Playlist *> (this));
214
215         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
216                 newlist.push_back (RegionFactory::RegionFactory::create (*i));
217         }
218 }
219
220 void
221 Playlist::init (bool hide)
222 {
223         g_atomic_int_set (&block_notifications, 0);
224         g_atomic_int_set (&ignore_state_changes, 0);
225         pending_modified = false;
226         pending_length = false;
227         first_set_state = true;
228         _refcnt = 0;
229         _hidden = hide;
230         _splicing = false;
231         _shuffling = false;
232         _nudging = false;
233         in_set_state = 0;
234         _edit_mode = Config->get_edit_mode();
235         in_flush = false;
236         in_partition = false;
237         subcnt = 0;
238         _read_data_count = 0;
239         _frozen = false;
240         layer_op_counter = 0;
241         freeze_length = 0;
242
243         Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
244 }
245
246 Playlist::Playlist (const Playlist& pl)
247         : _session (pl._session)
248 {
249         fatal << _("playlist const copy constructor called") << endmsg;
250 }
251
252 Playlist::Playlist (Playlist& pl)
253         : _session (pl._session)
254 {
255         fatal << _("playlist non-const copy constructor called") << endmsg;
256 }
257
258 Playlist::~Playlist ()
259 {
260         { 
261                 RegionLock rl (this);
262
263                 for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
264                         (*i)->set_playlist (boost::shared_ptr<Playlist>());
265                 }
266         }
267
268         /* GoingAway must be emitted by derived classes */
269 }
270
271 void
272 Playlist::set_name (string str)
273 {
274         /* in a typical situation, a playlist is being used
275            by one diskstream and also is referenced by the
276            Session. if there are more references than that,
277            then don't change the name.
278         */
279
280         if (_refcnt > 2) {
281                 return;
282         }
283
284         _name = str; 
285         NameChanged(); /* EMIT SIGNAL */
286 }
287
288 /***********************************************************************
289  CHANGE NOTIFICATION HANDLING
290  
291  Notifications must be delayed till the region_lock is released. This
292  is necessary because handlers for the signals may need to acquire
293  the lock (e.g. to read from the playlist).
294  ***********************************************************************/
295
296 void
297 Playlist::freeze ()
298 {
299         delay_notifications ();
300         g_atomic_int_inc (&ignore_state_changes);
301 }
302
303 void
304 Playlist::thaw ()
305 {
306         g_atomic_int_dec_and_test (&ignore_state_changes);
307         release_notifications ();
308 }
309
310
311 void
312 Playlist::delay_notifications ()
313 {
314         g_atomic_int_inc (&block_notifications);
315         freeze_length = _get_maximum_extent();
316 }
317
318 void
319 Playlist::release_notifications ()
320 {
321         if (g_atomic_int_dec_and_test (&block_notifications)) { 
322                 flush_notifications ();
323         } 
324 }
325
326
327 void
328 Playlist::notify_modified ()
329 {
330         if (holding_state ()) {
331                 pending_modified = true;
332         } else {
333                 pending_modified = false;
334                 Modified(); /* EMIT SIGNAL */
335         }
336 }
337
338 void
339 Playlist::notify_region_removed (boost::shared_ptr<Region> r)
340 {
341         if (holding_state ()) {
342                 pending_removes.insert (r);
343                 pending_modified = true;
344                 pending_length = true;
345         } else {
346                 /* this might not be true, but we have to act
347                    as though it could be.
348                 */
349                 LengthChanged (); /* EMIT SIGNAL */
350                 Modified (); /* EMIT SIGNAL */
351         }
352 }
353
354 void
355 Playlist::notify_region_added (boost::shared_ptr<Region> r)
356 {
357         /* the length change might not be true, but we have to act
358            as though it could be.
359         */
360
361         if (holding_state()) {
362                 pending_adds.insert (r);
363                 pending_modified = true;
364                 pending_length = true;
365         } else {
366                 LengthChanged (); /* EMIT SIGNAL */
367                 Modified (); /* EMIT SIGNAL */
368         }
369 }
370
371 void
372 Playlist::notify_length_changed ()
373 {
374         if (holding_state ()) {
375                 pending_length = true;
376         } else {
377                 LengthChanged(); /* EMIT SIGNAL */
378                 Modified (); /* EMIT SIGNAL */
379         }
380 }
381
382 void
383 Playlist::flush_notifications ()
384 {
385         set<boost::shared_ptr<Region> > dependent_checks_needed;
386         set<boost::shared_ptr<Region> >::iterator s;
387         uint32_t n = 0;
388
389         if (in_flush) {
390                 return;
391         }
392
393         in_flush = true;
394
395         /* we have no idea what order the regions ended up in pending
396            bounds (it could be based on selection order, for example).
397            so, to preserve layering in the "most recently moved is higher" 
398            model, sort them by existing layer, then timestamp them.
399         */
400
401         // RegionSortByLayer cmp;
402         // pending_bounds.sort (cmp);
403
404         for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
405                 if (Config->get_layer_model() == MoveAddHigher) {
406                         timestamp_layer_op (*r);
407                 }
408                 pending_length = true;
409                 dependent_checks_needed.insert (*r);
410                 n++;
411         }
412
413         for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
414                 dependent_checks_needed.insert (*s);
415                 n++;
416         }
417
418         for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
419                 remove_dependents (*s);
420                 n++;
421         }
422
423         if ((freeze_length != _get_maximum_extent()) || pending_length) {
424                 pending_length = 0;
425                 LengthChanged(); /* EMIT SIGNAL */
426                 n++;
427         }
428
429         if (n || pending_modified) {
430                 if (!in_set_state) {
431                         relayer ();
432                 }
433                 pending_modified = false;
434                 Modified (); /* EMIT SIGNAL */
435         }
436
437         for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
438                 check_dependents (*s, false);
439         }
440
441         pending_adds.clear ();
442         pending_removes.clear ();
443         pending_bounds.clear ();
444
445         in_flush = false;
446 }
447
448 /*************************************************************
449   PLAYLIST OPERATIONS
450  *************************************************************/
451
452 void
453 Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times) 
454
455         RegionLock rlock (this);
456         
457         times = fabs (times);
458         
459         int itimes = (int) floor (times);
460
461         nframes_t pos = position;
462         
463         if (itimes >= 1) {
464                 add_region_internal (region, pos);
465                 pos += region->length();
466                 --itimes;
467         }
468
469         
470         /* note that itimes can be zero if we being asked to just
471            insert a single fraction of the region.
472         */
473
474         for (int i = 0; i < itimes; ++i) {
475                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
476                 add_region_internal (copy, pos);
477                 pos += region->length();
478         }
479         
480         nframes_t length = 0;
481
482         if (floor (times) != times) {
483                 length = (nframes_t) floor (region->length() * (times - floor (times)));
484                 string name;
485                 _session.region_name (name, region->name(), false);
486                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
487                 add_region_internal (sub, pos);
488         }
489
490
491         possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
492 }
493
494 void
495 Playlist::set_region_ownership ()
496 {
497         RegionLock rl (this);
498         RegionList::iterator i;
499         boost::weak_ptr<Playlist> pl (shared_from_this());
500
501         for (i = regions.begin(); i != regions.end(); ++i) {
502                 (*i)->set_playlist (pl);
503         }
504 }
505
506 void
507 Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
508 {
509         RegionSortByPosition cmp;
510         nframes_t old_length = 0;
511
512         if (!holding_state()) {
513                  old_length = _get_maximum_extent();
514         }
515
516         if (!first_set_state) {
517                 boost::shared_ptr<Playlist> foo (shared_from_this());
518                 region->set_playlist (boost::weak_ptr<Playlist>(foo));
519         } 
520
521         region->set_position (position, this);
522
523         timestamp_layer_op (region);
524
525         regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
526         all_regions.insert (region);
527
528         possibly_splice_unlocked (position, region->length(), region);
529
530         if (!holding_state () && !in_set_state) {
531                 /* layers get assigned from XML state */
532                 relayer ();
533         }
534
535         /* we need to notify the existence of new region before checking dependents. Ick. */
536
537         notify_region_added (region);
538         
539         if (!holding_state ()) {
540                 check_dependents (region, false);
541                 if (old_length != _get_maximum_extent()) {
542                         notify_length_changed ();
543                 }
544         }
545
546         region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy), 
547                                                   boost::weak_ptr<Region> (region)));
548 }
549
550 void
551 Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
552 {
553         RegionLock rlock (this);
554
555         bool old_sp = _splicing;
556         _splicing = true;
557
558         remove_region_internal (old);
559         add_region_internal (newr, pos);
560
561         _splicing = old_sp;
562
563         possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
564 }
565
566 void
567 Playlist::remove_region (boost::shared_ptr<Region> region)
568 {
569         RegionLock rlock (this);
570         remove_region_internal (region);
571 }
572
573 int
574 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
575 {
576         RegionList::iterator i;
577         nframes_t old_length = 0;
578
579         if (!holding_state()) {
580                 old_length = _get_maximum_extent();
581         }
582
583         if (!in_set_state) {
584                 /* unset playlist */
585                 region->set_playlist (boost::weak_ptr<Playlist>());
586         }
587
588         for (i = regions.begin(); i != regions.end(); ++i) {
589                 if (*i == region) {
590
591                         nframes_t pos = (*i)->position();
592                         nframes64_t distance = (*i)->length();
593
594                         regions.erase (i);
595
596                         possibly_splice_unlocked (pos, -distance);
597
598                         if (!holding_state ()) {
599                                 relayer ();
600                                 remove_dependents (region);
601                                 
602                                 if (old_length != _get_maximum_extent()) {
603                                         notify_length_changed ();
604                                 }
605                         }
606
607                         notify_region_removed (region);
608                         return 0;
609                 }
610         }
611
612
613
614         return -1;
615 }
616
617 void
618 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
619 {
620         if (Config->get_use_overlap_equivalency()) {
621                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
622                         if ((*i)->overlap_equivalent (other)) {
623                                 results.push_back ((*i));
624                         }
625                 }
626         } else {
627                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
628                         if ((*i)->equivalent (other)) {
629                                 results.push_back ((*i));
630                         }
631                 }
632         }
633 }
634
635 void
636 Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
637 {
638         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
639
640                 if ((*i) && (*i)->region_list_equivalent (other)) {
641                         results.push_back (*i);
642                 }
643         }
644 }
645
646 void
647 Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
648 {
649         RegionList thawlist;
650
651         partition_internal (start, end, false, thawlist);
652
653         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
654                 (*i)->thaw ("separation");
655         }
656 }
657
658 void
659 Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
660 {
661         RegionLock rlock (this);
662         boost::shared_ptr<Region> region;
663         boost::shared_ptr<Region> current;
664         string new_name;
665         RegionList::iterator tmp;
666         OverlapType overlap;
667         nframes_t pos1, pos2, pos3, pos4;
668         RegionList new_regions;
669
670         in_partition = true;
671
672         /* need to work from a copy, because otherwise the regions we add during the process
673            get operated on as well.
674         */
675
676         RegionList copy = regions;
677
678         for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
679                 
680                 tmp = i;
681                 ++tmp;
682
683                 current = *i;
684                 
685                 if (current->first_frame() == start && current->last_frame() == end) {
686                         if (cutting) {
687                                 remove_region_internal (current);
688                         }
689                         continue;
690                 }
691                 
692                 if ((overlap = current->coverage (start, end)) == OverlapNone) {
693                         continue;
694                 }
695                 
696                 pos1 = current->position();
697                 pos2 = start;
698                 pos3 = end;
699                 pos4 = current->last_frame();
700
701                 if (overlap == OverlapInternal) {
702                         
703                         /* split: we need 3 new regions, the front, middle and end.
704                            cut:   we need 2 regions, the front and end.
705                         */
706                         
707                         /*
708                                          start                 end
709                           ---------------*************************------------
710                                          P1  P2              P3  P4
711                           SPLIT:
712                           ---------------*****++++++++++++++++====------------
713                           CUT
714                           ---------------*****----------------====------------
715                           
716                         */
717
718                         if (!cutting) {
719                                 
720                                 /* "middle" ++++++ */
721                                 
722                                 _session.region_name (new_name, current->name(), false);
723                                 region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
724                                                        regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
725                                 add_region_internal (region, start);
726                                 new_regions.push_back (region);
727                         }
728                         
729                         /* "end" ====== */
730                         
731                         _session.region_name (new_name, current->name(), false);
732                         region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name, 
733                                                regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
734
735                         add_region_internal (region, end);
736                         new_regions.push_back (region);
737
738                         /* "front" ***** */
739                                 
740                         current->freeze ();
741                         thawlist.push_back (current);
742                         current->trim_end (pos2, this);
743
744                 } else if (overlap == OverlapEnd) {
745
746                         /*
747                                                               start           end
748                                     ---------------*************************------------
749                                                    P1           P2         P4   P3
750                                     SPLIT:                                                 
751                                     ---------------**************+++++++++++------------
752                                     CUT:                                                   
753                                     ---------------**************-----------------------
754
755                         */
756
757                         if (!cutting) {
758                                 
759                                 /* end +++++ */
760                                 
761                                 _session.region_name (new_name, current->name(), false);
762                                 region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
763                                                        Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
764                                 add_region_internal (region, start);
765                                 new_regions.push_back (region);
766                         }
767
768                         /* front ****** */
769
770                         current->freeze ();
771                         thawlist.push_back (current);
772                         current->trim_end (pos2, this);
773
774                 } else if (overlap == OverlapStart) {
775
776                         /* split: we need 2 regions: the front and the end.
777                            cut: just trim current to skip the cut area
778                         */
779                                 
780                         /*
781                                                         start           end
782                                     ---------------*************************------------
783                                        P2          P1 P3                   P4          
784
785                                     SPLIT:
786                                     ---------------****+++++++++++++++++++++------------
787                                     CUT:
788                                     -------------------*********************------------
789                                     
790                         */
791
792                         if (!cutting) {
793                                 
794                                 /* front **** */
795                                  _session.region_name (new_name, current->name(), false);
796                                  region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
797                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
798                                  add_region_internal (region, pos1);
799                                  new_regions.push_back (region);
800                         } 
801                         
802                         /* end */
803                         
804                         current->freeze ();
805                         thawlist.push_back (current);
806                         current->trim_front (pos3, this);
807
808                 } else if (overlap == OverlapExternal) {
809
810                         /* split: no split required.
811                            cut: remove the region.
812                         */
813                                 
814                         /*
815                                        start                                      end
816                                     ---------------*************************------------
817                                        P2          P1 P3                   P4          
818
819                                     SPLIT:
820                                     ---------------*************************------------
821                                     CUT:
822                                     ----------------------------------------------------
823                                     
824                         */
825
826                         if (cutting) {
827                                 remove_region_internal (current);
828                         }
829                         new_regions.push_back (current);
830                 }
831         }
832
833         in_partition = false;
834
835         for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
836                 check_dependents (*i, false);
837         }
838 }
839
840 boost::shared_ptr<Playlist>
841 Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
842 {
843         boost::shared_ptr<Playlist> ret;
844         boost::shared_ptr<Playlist> pl;
845         nframes_t start;
846
847         if (ranges.empty()) {
848                 return boost::shared_ptr<Playlist>();
849         }
850
851         start = ranges.front().start;
852
853         for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
854
855                 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
856                 
857                 if (i == ranges.begin()) {
858                         ret = pl;
859                 } else {
860                         
861                         /* paste the next section into the nascent playlist,
862                            offset to reflect the start of the first range we
863                            chopped.
864                         */
865
866                         ret->paste (pl, (*i).start - start, 1.0f);
867                 }
868         }
869
870         return ret;
871 }
872
873 boost::shared_ptr<Playlist>
874 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
875 {
876         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
877         return cut_copy (pmf, ranges, result_is_hidden);
878 }
879
880 boost::shared_ptr<Playlist>
881 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
882 {
883         boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
884         return cut_copy (pmf, ranges, result_is_hidden);
885 }
886
887 boost::shared_ptr<Playlist>
888 Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
889 {
890         boost::shared_ptr<Playlist> the_copy;
891         RegionList thawlist;
892         char buf[32];
893
894         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
895         string new_name = _name;
896         new_name += '.';
897         new_name += buf;
898
899         if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
900                 return boost::shared_ptr<Playlist>();
901         }
902
903         partition_internal (start, start+cnt-1, true, thawlist);
904
905         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
906                 (*i)->thaw ("playlist cut");
907         }
908
909         return the_copy;
910 }
911
912 boost::shared_ptr<Playlist>
913 Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
914 {
915         char buf[32];
916         
917         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
918         string new_name = _name;
919         new_name += '.';
920         new_name += buf;
921
922         cnt = min (_get_maximum_extent() - start, cnt);
923         return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
924 }
925
926 int
927 Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
928 {
929         times = fabs (times);
930         nframes_t old_length;
931
932         {
933                 RegionLock rl1 (this);
934                 RegionLock rl2 (other.get());
935
936                 old_length = _get_maximum_extent();
937         
938                 int itimes = (int) floor (times);
939                 nframes_t pos = position;
940                 nframes_t shift = other->_get_maximum_extent();
941                 layer_t top_layer = regions.size();
942
943                 while (itimes--) {
944                         for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
945                                 boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
946
947                                 /* put these new regions on top of all existing ones, but preserve
948                                    the ordering they had in the original playlist.
949                                 */
950                                 
951                                 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
952                                 add_region_internal (copy_of_region, copy_of_region->position() + pos);
953                         }
954                         pos += shift;
955                 }
956
957
958                 /* XXX shall we handle fractional cases at some point? */
959
960                 if (old_length != _get_maximum_extent()) {
961                         notify_length_changed ();
962                 }
963
964                 
965         }
966
967         return 0;
968 }
969
970
971 void
972 Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
973 {
974         times = fabs (times);
975
976         RegionLock rl (this);
977         int itimes = (int) floor (times);
978         nframes_t pos = position;
979
980         while (itimes--) {
981                 boost::shared_ptr<Region> copy = RegionFactory::create (region);
982                 add_region_internal (copy, pos);
983                 pos += region->length();
984         }
985
986         if (floor (times) != times) {
987                 nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
988                 string name;
989                 _session.region_name (name, region->name(), false);
990                 boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
991                 add_region_internal (sub, pos);
992         }
993 }
994
995 void
996 Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
997 {
998         RegionLock rl (this);
999
1000         if (!region->covers (playlist_position)) {
1001                 return;
1002         }
1003
1004         if (region->position() == playlist_position ||
1005             region->last_frame() == playlist_position) {
1006                 return;
1007         }
1008
1009         boost::shared_ptr<Region> left;
1010         boost::shared_ptr<Region> right;
1011         nframes_t before;
1012         nframes_t after;
1013         string before_name;
1014         string after_name;
1015
1016         /* split doesn't change anything about length, so don't try to splice */
1017         
1018         bool old_sp = _splicing;
1019         _splicing = true;
1020
1021         before = playlist_position - region->position();
1022         after = region->length() - before;
1023         
1024         _session.region_name (before_name, region->name(), false);
1025         left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
1026
1027         _session.region_name (after_name, region->name(), false);
1028         right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
1029
1030         add_region_internal (left, region->position());
1031         add_region_internal (right, region->position() + before);
1032
1033         uint64_t orig_layer_op = region->last_layer_op();
1034         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1035                 if ((*i)->last_layer_op() > orig_layer_op) {
1036                         (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1037                 }
1038         }
1039         
1040         left->set_last_layer_op ( orig_layer_op );
1041         right->set_last_layer_op ( orig_layer_op + 1);
1042
1043         layer_op_counter++;
1044
1045         finalize_split_region (region, left, right);
1046         
1047         remove_region_internal (region);
1048
1049         _splicing = old_sp;
1050 }
1051
1052 void
1053 Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1054 {
1055         if (_splicing || in_set_state) {
1056                 /* don't respond to splicing moves or state setting */
1057                 return;
1058         }
1059
1060         if (_edit_mode == Splice) {
1061                 splice_locked (at, distance, exclude);
1062         }
1063 }
1064
1065 void
1066 Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1067 {
1068         if (_splicing || in_set_state) {
1069                 /* don't respond to splicing moves or state setting */
1070                 return;
1071         }
1072
1073         if (_edit_mode == Splice) {
1074                 splice_unlocked (at, distance, exclude);
1075         }
1076 }
1077
1078 void
1079 Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1080 {
1081         {
1082                 RegionLock rl (this);
1083                 core_splice (at, distance, exclude);
1084         }
1085 }
1086
1087 void
1088 Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1089 {
1090         core_splice (at, distance, exclude);
1091 }
1092
1093 void
1094 Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
1095 {
1096         _splicing = true;
1097
1098         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1099
1100                 if (exclude && (*i) == exclude) {
1101                         continue;
1102                 }
1103
1104                 if ((*i)->position() >= at) {
1105                         nframes64_t new_pos = (*i)->position() + distance;
1106                         if (new_pos < 0) {
1107                                 new_pos = 0;
1108                         } else if (new_pos >= max_frames - (*i)->length()) {
1109                                 new_pos = max_frames - (*i)->length();
1110                         } 
1111                                 
1112                         (*i)->set_position (new_pos, this);
1113                 }
1114         }
1115
1116         _splicing = false;
1117
1118         notify_length_changed ();
1119 }
1120
1121 void
1122 Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
1123 {
1124         if (in_set_state || _splicing || _nudging || _shuffling) {
1125                 return;
1126         }
1127
1128         if (what_changed & ARDOUR::PositionChanged) {
1129
1130                 /* remove it from the list then add it back in
1131                    the right place again.
1132                 */
1133                 
1134                 RegionSortByPosition cmp;
1135
1136                 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1137                 
1138                 if (i == regions.end()) {
1139                         warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1140                                             _name, region->name())
1141                                 << endmsg;
1142                         return;
1143                 }
1144
1145                 regions.erase (i);
1146                 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
1147         }
1148
1149         if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1150                 
1151                 nframes64_t delta = 0;
1152                 
1153                 if (what_changed & ARDOUR::PositionChanged) {
1154                         delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
1155                 } 
1156                 
1157                 if (what_changed & ARDOUR::LengthChanged) {
1158                         delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
1159                 } 
1160
1161                 if (delta) {
1162                         possibly_splice (region->last_position() + region->last_length(), delta, region);
1163                 }
1164
1165                 if (holding_state ()) {
1166                         pending_bounds.push_back (region);
1167                 } else {
1168                         if (Config->get_layer_model() == MoveAddHigher) {
1169                                 /* it moved or changed length, so change the timestamp */
1170                                 timestamp_layer_op (region);
1171                         }
1172                         
1173                         notify_length_changed ();
1174                         relayer ();
1175                         check_dependents (region, false);
1176                 }
1177         }
1178 }
1179
1180 void
1181 Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
1182 {
1183         boost::shared_ptr<Region> region (weak_region.lock());
1184
1185         if (!region) {
1186                 return;
1187         }
1188
1189
1190         /* this makes a virtual call to the right kind of playlist ... */
1191
1192         region_changed (what_changed, region);
1193 }
1194
1195 bool
1196 Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
1197 {
1198         Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1199         bool save = false;
1200
1201         if (in_set_state || in_flush) {
1202                 return false;
1203         }
1204
1205         {
1206                 if (what_changed & BoundsChanged) {
1207                         region_bounds_changed (what_changed, region);
1208                         save = !(_splicing || _nudging);
1209                 }
1210                 
1211                 if ((what_changed & our_interests) && 
1212                     !(what_changed &  Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1213                         check_dependents (region, false);
1214                 }
1215                 
1216                 if (what_changed & our_interests) {
1217                         save = true;
1218                 }
1219         }
1220
1221         return save;
1222 }
1223
1224 void
1225 Playlist::drop_regions ()
1226 {
1227         RegionLock rl (this);
1228         regions.clear ();
1229         all_regions.clear ();
1230 }
1231
1232 void
1233 Playlist::clear (bool with_signals)
1234 {
1235         { 
1236                 RegionLock rl (this);
1237                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1238                         pending_removes.insert (*i);
1239                 }
1240                 regions.clear ();
1241         }
1242
1243         if (with_signals) {
1244                 LengthChanged ();
1245                 Modified ();
1246         }
1247
1248 }
1249
1250 /***********************************************************************
1251  FINDING THINGS
1252  **********************************************************************/
1253
1254 Playlist::RegionList *
1255 Playlist::regions_at (nframes_t frame)
1256
1257 {
1258         RegionLock rlock (this);
1259         return find_regions_at (frame);
1260 }       
1261
1262 boost::shared_ptr<Region>
1263 Playlist::top_region_at (nframes_t frame)
1264
1265 {
1266         RegionLock rlock (this);
1267         RegionList *rlist = find_regions_at (frame);
1268         boost::shared_ptr<Region> region;
1269         
1270         if (rlist->size()) {
1271                 RegionSortByLayer cmp;
1272                 rlist->sort (cmp);
1273                 region = rlist->back();
1274         } 
1275
1276         delete rlist;
1277         return region;
1278 }       
1279
1280 Playlist::RegionList*
1281 Playlist::regions_to_read (nframes_t start, nframes_t end)
1282 {
1283         /* Caller must hold lock */
1284
1285         RegionList covering;
1286         set<nframes_t> to_check;
1287         set<boost::shared_ptr<Region> > unique;
1288         RegionList here;
1289
1290         to_check.insert (start);
1291         to_check.insert (end);
1292
1293         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1294
1295                 /* find all/any regions that span start+end */
1296
1297                 switch ((*i)->coverage (start, end)) {
1298                 case OverlapNone:
1299                         break;
1300
1301                 case OverlapInternal:
1302                         covering.push_back (*i);
1303                         break;
1304
1305                 case OverlapStart:
1306                         to_check.insert ((*i)->position());
1307                         covering.push_back (*i);
1308                         break;
1309
1310                 case OverlapEnd:
1311                         to_check.insert ((*i)->last_frame());
1312                         covering.push_back (*i);
1313                         break;
1314
1315                 case OverlapExternal:
1316                         covering.push_back (*i);
1317                         to_check.insert ((*i)->position());
1318                         to_check.insert ((*i)->last_frame());
1319                         break;
1320                 }
1321
1322                 /* don't go too far */
1323
1324                 if ((*i)->position() > end) {
1325                         break;
1326                 }
1327         }
1328
1329         RegionList* rlist = new RegionList;
1330
1331         /* find all the regions that cover each position .... */
1332
1333         if (covering.size() == 1) {
1334
1335                 rlist->push_back (covering.front());
1336                 
1337         } else {
1338         
1339                 for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
1340                         
1341                         here.clear ();
1342                         
1343                         for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
1344                         
1345                                 if ((*x)->covers (*t)) {
1346                                         here.push_back (*x);
1347                                 }
1348                         }
1349                         
1350                         RegionSortByLayer cmp;
1351                         here.sort (cmp);
1352                         
1353                         /* ... and get the top/transparent regions at "here" */
1354                         
1355                         for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
1356                                 
1357                                 unique.insert (*c);
1358                                 
1359                                 if ((*c)->opaque()) {
1360                                         
1361                                         /* the other regions at this position are hidden by this one */
1362                                         
1363                                         break;
1364                                 }
1365                         }
1366                 }
1367                 
1368                 for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
1369                         rlist->push_back (*s);
1370                 }
1371
1372                 if (rlist->size() > 1) {
1373                         /* now sort by time order */
1374                         
1375                         RegionSortByPosition cmp;
1376                         rlist->sort (cmp);
1377                 }
1378         }
1379
1380         return rlist;
1381 }
1382
1383 Playlist::RegionList *
1384 Playlist::find_regions_at (nframes_t frame)
1385 {
1386         /* Caller must hold lock */
1387
1388         RegionList *rlist = new RegionList;
1389
1390         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1391                 if ((*i)->covers (frame)) {
1392                         rlist->push_back (*i);
1393                 }
1394         }
1395
1396         return rlist;
1397 }
1398
1399 Playlist::RegionList *
1400 Playlist::regions_touched (nframes_t start, nframes_t end)
1401 {
1402         RegionLock rlock (this);
1403         RegionList *rlist = new RegionList;
1404
1405         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1406                 if ((*i)->coverage (start, end) != OverlapNone) {
1407                         rlist->push_back (*i);
1408                 }
1409         }
1410
1411         return rlist;
1412 }
1413
1414
1415 boost::shared_ptr<Region>
1416 Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
1417 {
1418         RegionLock rlock (this);
1419         boost::shared_ptr<Region> ret;
1420         nframes_t closest = max_frames;
1421
1422
1423         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1424
1425                 nframes_t distance;
1426                 boost::shared_ptr<Region> r = (*i);
1427                 nframes_t pos = 0;
1428
1429                 switch (point) {
1430                 case Start:
1431                         pos = r->first_frame ();
1432                         break;
1433                 case End:
1434                         pos = r->last_frame ();
1435                         break;
1436                 case SyncPoint:
1437                         pos = r->adjust_to_sync (r->first_frame());
1438                         break;
1439                 }
1440
1441                 switch (dir) {
1442                 case 1: /* forwards */
1443
1444                         if (pos >= frame) {
1445                                 if ((distance = pos - frame) < closest) {
1446                                         closest = distance;
1447                                         ret = r;
1448                                 }
1449                         }
1450
1451                         break;
1452
1453                 default: /* backwards */
1454
1455                         if (pos <= frame) {
1456                                 if ((distance = frame - pos) < closest) {
1457                                         closest = distance;
1458                                         ret = r;
1459                                 }
1460                         }
1461                         break;
1462                 }
1463         }
1464
1465         return ret;
1466 }
1467
1468 nframes64_t
1469 Playlist::find_next_region_boundary (nframes64_t frame, int dir)
1470 {
1471         RegionLock rlock (this);
1472
1473         nframes64_t closest = max_frames;
1474         nframes64_t ret = -1;
1475
1476         if (dir > 0) {
1477
1478                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1479                         
1480                         boost::shared_ptr<Region> r = (*i);
1481                         nframes64_t distance;
1482                         nframes64_t end = r->position() + r->length();
1483                         bool reset;
1484
1485                         reset = false;
1486
1487                         if (r->first_frame() > frame) {
1488
1489                                 distance = r->first_frame() - frame;
1490                                 
1491                                 if (distance < closest) {
1492                                         ret = r->first_frame();
1493                                         closest = distance;
1494                                         reset = true;
1495                                 }
1496                         }
1497
1498                         if (end > frame) {
1499                                 
1500                                 distance = end - frame;
1501                                 
1502                                 if (distance < closest) {
1503                                         ret = end;
1504                                         closest = distance;
1505                                         reset = true;
1506                                 }
1507                         }
1508
1509                         if (reset) {
1510                                 break;
1511                         }
1512                 }
1513
1514         } else {
1515
1516                 for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
1517                         
1518                         boost::shared_ptr<Region> r = (*i);
1519                         nframes64_t distance;
1520                         bool reset;
1521
1522                         reset = false;
1523
1524                         if (r->last_frame() < frame) {
1525
1526                                 distance = frame - r->last_frame();
1527                                 
1528                                 if (distance < closest) {
1529                                         ret = r->last_frame();
1530                                         closest = distance;
1531                                         reset = true;
1532                                 }
1533                         }
1534
1535                         if (r->first_frame() < frame) {
1536                                 distance = frame - r->last_frame();
1537                                 
1538                                 if (distance < closest) {
1539                                         ret = r->first_frame();
1540                                         closest = distance;
1541                                         reset = true;
1542                                 }
1543                         }
1544
1545                         if (reset) {
1546                                 break;
1547                         }
1548                 }
1549         }
1550
1551         return ret;
1552 }
1553
1554 /***********************************************************************/
1555
1556
1557
1558 void
1559 Playlist::mark_session_dirty ()
1560 {
1561         if (!in_set_state && !holding_state ()) {
1562                 _session.set_dirty();
1563         }
1564 }
1565
1566 int
1567 Playlist::set_state (const XMLNode& node)
1568 {
1569         XMLNode *child;
1570         XMLNodeList nlist;
1571         XMLNodeConstIterator niter;
1572         XMLPropertyList plist;
1573         XMLPropertyConstIterator piter;
1574         XMLProperty *prop;
1575         boost::shared_ptr<Region> region;
1576         string region_name;
1577
1578         in_set_state++;
1579
1580         if (node.name() != "Playlist") {
1581                 in_set_state--;
1582                 return -1;
1583         }
1584
1585         freeze ();
1586
1587         plist = node.properties();
1588
1589         for (piter = plist.begin(); piter != plist.end(); ++piter) {
1590
1591                 prop = *piter;
1592                 
1593                 if (prop->name() == X_("name")) {
1594                         _name = prop->value();
1595                 } else if (prop->name() == X_("orig_diskstream_id")) {
1596                         _orig_diskstream_id = prop->value ();
1597                 } else if (prop->name() == X_("frozen")) {
1598                         _frozen = (prop->value() == X_("yes"));
1599                 }
1600         }
1601
1602         clear (false);
1603         
1604         nlist = node.children();
1605
1606         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1607
1608                 child = *niter;
1609                 
1610                 if (child->name() == "Region") {
1611
1612                         if ((prop = child->property ("id")) == 0) {
1613                                 error << _("region state node has no ID, ignored") << endmsg;
1614                                 continue;
1615                         }
1616                         
1617                         ID id = prop->value ();
1618                         
1619                         if ((region = region_by_id (id))) {
1620
1621                                 Change what_changed = Change (0);
1622
1623                                 if (region->set_live_state (*child, what_changed, true)) {
1624                                         error << _("Playlist: cannot reset region state from XML") << endmsg;
1625                                         continue;
1626                                 }
1627
1628                         } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
1629                                 error << _("Playlist: cannot create region from XML") << endmsg;
1630                                 continue;
1631                         }
1632
1633                         add_region (region, region->position(), 1.0);
1634
1635                         // So that layer_op ordering doesn't get screwed up
1636                         region->set_last_layer_op( region->layer());
1637
1638                 }                       
1639         }
1640         
1641         notify_modified ();
1642
1643         thaw ();
1644
1645         /* update dependents, which was not done during add_region_internal 
1646            due to in_set_state being true 
1647         */
1648
1649         for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1650                 check_dependents (*r, false);
1651         }
1652
1653         in_set_state--;
1654         first_set_state = false;
1655         return 0;
1656 }
1657
1658 XMLNode&
1659 Playlist::get_state()
1660 {
1661         return state(true);
1662 }
1663
1664 XMLNode&
1665 Playlist::get_template()
1666 {
1667         return state(false);
1668 }
1669
1670 XMLNode&
1671 Playlist::state (bool full_state)
1672 {
1673         XMLNode *node = new XMLNode (X_("Playlist"));
1674         char buf[64];
1675         
1676         node->add_property (X_("name"), _name);
1677
1678         _orig_diskstream_id.print (buf, sizeof (buf));
1679         node->add_property (X_("orig_diskstream_id"), buf);
1680         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1681
1682         if (full_state) {
1683                 RegionLock rlock (this, false);
1684                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1685                         node->add_child_nocopy ((*i)->get_state());
1686                 }
1687         }
1688
1689         if (_extra_xml) {
1690                 node->add_child_copy (*_extra_xml);
1691         }
1692
1693         return *node;
1694 }
1695
1696 bool
1697 Playlist::empty() const
1698 {
1699         RegionLock rlock (const_cast<Playlist *>(this), false);
1700         return regions.empty();
1701 }
1702
1703 uint32_t
1704 Playlist::n_regions() const
1705 {
1706         RegionLock rlock (const_cast<Playlist *>(this), false);
1707         return regions.size();
1708 }
1709
1710 nframes_t
1711 Playlist::get_maximum_extent () const
1712 {
1713         RegionLock rlock (const_cast<Playlist *>(this), false);
1714         return _get_maximum_extent ();
1715 }
1716
1717 nframes_t
1718 Playlist::_get_maximum_extent () const
1719 {
1720         RegionList::const_iterator i;
1721         nframes_t max_extent = 0;
1722         nframes_t end = 0;
1723
1724         for (i = regions.begin(); i != regions.end(); ++i) {
1725                 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1726                         max_extent = end;
1727                 }
1728         }
1729
1730         return max_extent;
1731 }
1732
1733 string 
1734 Playlist::bump_name (string name, Session &session)
1735 {
1736         string newname = name;
1737
1738         do {
1739                 newname = Playlist::bump_name_once (newname);
1740         } while (session.playlist_by_name (newname)!=NULL);
1741
1742         return newname;
1743 }
1744
1745 string
1746 Playlist::bump_name_once (string name)
1747 {
1748         string::size_type period;
1749         string newname;
1750
1751         if ((period = name.find_last_of ('.')) == string::npos) {
1752                 newname  = name;
1753                 newname += ".1";
1754         } else {
1755                 int isnumber = 1;
1756                 const char *last_element = name.c_str() + period + 1;
1757                 for (size_t i = 0; i < strlen(last_element); i++) {
1758                         if (!isdigit(last_element[i])) {
1759                                 isnumber = 0;
1760                                 break;
1761                         }
1762                 }
1763
1764                 errno = 0;
1765                 long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
1766
1767                 if (isnumber == 0 || errno != 0) {
1768                         // last_element is not a number, or is too large
1769                         newname  = name;
1770                         newname += ".1";
1771                 } else {
1772                         char buf[32];
1773
1774                         snprintf (buf, sizeof(buf), "%ld", version+1);
1775                 
1776                         newname  = name.substr (0, period+1);
1777                         newname += buf;
1778                 }
1779         }
1780
1781         return newname;
1782 }
1783
1784 layer_t
1785 Playlist::top_layer() const
1786 {
1787         RegionLock rlock (const_cast<Playlist *> (this));
1788         layer_t top = 0;
1789
1790         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1791                 top = max (top, (*i)->layer());
1792         }
1793         return top;
1794 }
1795
1796 void
1797 Playlist::set_edit_mode (EditMode mode)
1798 {
1799         _edit_mode = mode;
1800 }
1801
1802 /********************
1803  * Region Layering
1804  ********************/
1805
1806 void
1807 Playlist::relayer ()
1808 {
1809         RegionList::iterator i;
1810         uint32_t layer = 0;
1811
1812         /* don't send multiple Modified notifications
1813            when multiple regions are relayered.
1814         */
1815
1816         freeze ();
1817
1818         if (Config->get_layer_model() == MoveAddHigher || 
1819             Config->get_layer_model() == AddHigher) {
1820
1821                 RegionSortByLastLayerOp cmp;
1822                 RegionList copy = regions;
1823
1824                 copy.sort (cmp);
1825
1826                 for (i = copy.begin(); i != copy.end(); ++i) {
1827                         (*i)->set_layer (layer++);
1828                 }
1829
1830         } else {
1831                 
1832                 /* Session::LaterHigher model */
1833
1834                 for (i = regions.begin(); i != regions.end(); ++i) {
1835                         (*i)->set_layer (layer++);
1836                 }
1837         }
1838
1839         /* sending Modified means that various kinds of layering
1840            models operate correctly at the GUI
1841            level. slightly inefficient, but only slightly.
1842
1843            We force a Modified signal here in case no layers actually
1844            changed.
1845         */
1846
1847         notify_modified ();
1848
1849         thaw ();
1850 }
1851
1852 /* XXX these layer functions are all deprecated */
1853
1854 void
1855 Playlist::raise_region (boost::shared_ptr<Region> region)
1856 {
1857         uint32_t rsz = regions.size();
1858         layer_t target = region->layer() + 1U;
1859
1860         if (target >= rsz) {
1861                 /* its already at the effective top */
1862                 return;
1863         }
1864
1865         move_region_to_layer (target, region, 1);
1866 }
1867
1868 void
1869 Playlist::lower_region (boost::shared_ptr<Region> region)
1870 {
1871         if (region->layer() == 0) {
1872                 /* its already at the bottom */
1873                 return;
1874         }
1875
1876         layer_t target = region->layer() - 1U;
1877
1878         move_region_to_layer (target, region, -1);
1879 }
1880
1881 void
1882 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
1883 {
1884         /* does nothing useful if layering mode is later=higher */
1885         if ((Config->get_layer_model() == MoveAddHigher) ||
1886             (Config->get_layer_model() == AddHigher)) {
1887                 timestamp_layer_op (region);
1888                 relayer ();
1889         }
1890 }
1891
1892 void
1893 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
1894 {
1895         /* does nothing useful if layering mode is later=higher */
1896         if ((Config->get_layer_model() == MoveAddHigher) ||
1897             (Config->get_layer_model() == AddHigher)) {
1898                 region->set_last_layer_op (0);
1899                 relayer ();
1900         }
1901 }
1902
1903 int
1904 Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
1905 {
1906         RegionList::iterator i;
1907         typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
1908         list<LayerInfo> layerinfo;
1909         layer_t dest;
1910
1911         {
1912                 RegionLock rlock (const_cast<Playlist *> (this));
1913                 
1914                 for (i = regions.begin(); i != regions.end(); ++i) {
1915                         
1916                         if (region == *i) {
1917                                 continue;
1918                         }
1919
1920                         if (dir > 0) {
1921
1922                                 /* region is moving up, move all regions on intermediate layers
1923                                    down 1
1924                                 */
1925                                 
1926                                 if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
1927                                         dest = (*i)->layer() - 1;
1928                                 } else {
1929                                         /* not affected */
1930                                         continue;
1931                                 }
1932                         } else {
1933
1934                                 /* region is moving down, move all regions on intermediate layers
1935                                    up 1
1936                                 */
1937
1938                                 if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
1939                                         dest = (*i)->layer() + 1;
1940                                 } else {
1941                                         /* not affected */
1942                                         continue;
1943                                 }
1944                         }
1945
1946                         LayerInfo newpair;
1947                         
1948                         newpair.first = *i;
1949                         newpair.second = dest;
1950                         
1951                         layerinfo.push_back (newpair);
1952                 } 
1953         }
1954
1955         /* now reset the layers without holding the region lock */
1956
1957         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1958                 x->first->set_layer (x->second);
1959         }
1960
1961         region->set_layer (target_layer);
1962
1963 #if 0
1964         /* now check all dependents */
1965
1966         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1967                 check_dependents (x->first, false);
1968         }
1969         
1970         check_dependents (region, false);
1971 #endif
1972         
1973         return 0;
1974 }
1975
1976 void
1977 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
1978 {
1979         RegionList::iterator i;
1980         nframes_t new_pos;
1981         bool moved = false;
1982
1983         _nudging = true;
1984
1985         {
1986                 RegionLock rlock (const_cast<Playlist *> (this));
1987                 
1988                 for (i = regions.begin(); i != regions.end(); ++i) {
1989
1990                         if ((*i)->position() >= start) {
1991
1992                                 if (forwards) {
1993
1994                                         if ((*i)->last_frame() > max_frames - distance) {
1995                                                 new_pos = max_frames - (*i)->length();
1996                                         } else {
1997                                                 new_pos = (*i)->position() + distance;
1998                                         }
1999                                         
2000                                 } else {
2001                                         
2002                                         if ((*i)->position() > distance) {
2003                                                 new_pos = (*i)->position() - distance;
2004                                         } else {
2005                                                 new_pos = 0;
2006                                         }
2007                                 }
2008
2009                                 (*i)->set_position (new_pos, this);
2010                                 moved = true;
2011                         }
2012                 }
2013         }
2014
2015         if (moved) {
2016                 _nudging = false;
2017                 notify_length_changed ();
2018         }
2019
2020 }
2021
2022 boost::shared_ptr<Region>
2023 Playlist::find_region (const ID& id) const
2024 {
2025         RegionLock rlock (const_cast<Playlist*> (this));
2026
2027         /* searches all regions currently in use by the playlist */
2028
2029         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2030                 if ((*i)->id() == id) {
2031                         return *i;
2032                 }
2033         }
2034
2035         return boost::shared_ptr<Region> ();
2036 }
2037
2038 boost::shared_ptr<Region>
2039 Playlist::region_by_id (ID id)
2040 {
2041         /* searches all regions ever added to this playlist */
2042
2043         for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
2044                 if ((*i)->id() == id) {
2045                         return *i;
2046                 }
2047         }
2048         return boost::shared_ptr<Region> ();
2049 }
2050         
2051 void
2052 Playlist::dump () const
2053 {
2054         boost::shared_ptr<Region> r;
2055
2056         cerr << "Playlist \"" << _name << "\" " << endl
2057              << regions.size() << " regions "
2058              << endl;
2059
2060         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2061                 r = *i;
2062                 cerr << "  " << r->name() << " [" 
2063                      << r->start() << "+" << r->length() 
2064                      << "] at " 
2065                      << r->position()
2066                      << " on layer "
2067                      << r->layer ()
2068                      << endl;
2069         }
2070 }
2071
2072 void
2073 Playlist::set_frozen (bool yn)
2074 {
2075         _frozen = yn;
2076 }
2077
2078 void
2079 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
2080 {
2081 //      struct timeval tv;
2082 //      gettimeofday (&tv, 0);
2083         region->set_last_layer_op (++layer_op_counter);
2084 }
2085
2086
2087 void
2088 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
2089 {
2090         bool moved = false;
2091         nframes_t new_pos;
2092
2093         if (region->locked()) {
2094                 return;
2095         }
2096
2097         _shuffling = true;
2098
2099         {
2100                 RegionLock rlock (const_cast<Playlist*> (this));
2101                 
2102                 
2103                 if (dir > 0) {
2104                         
2105                         RegionList::iterator next;
2106
2107                         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {       
2108                                 if ((*i) == region) {
2109                                         next = i;
2110                                         ++next;
2111
2112                                         if (next != regions.end()) {
2113
2114                                                 if ((*next)->locked()) {
2115                                                         break;
2116                                                 }
2117
2118                                                 if ((*next)->position() != region->last_frame() + 1) {
2119                                                         /* they didn't used to touch, so after shuffle,
2120                                                            just have them swap positions.
2121                                                         */
2122                                                         new_pos = (*next)->position();
2123                                                 } else {
2124                                                         /* they used to touch, so after shuffle,
2125                                                            make sure they still do. put the earlier
2126                                                            region where the later one will end after
2127                                                            it is moved.
2128                                                         */
2129                                                         new_pos = region->position() + (*next)->length();
2130                                                 }
2131
2132                                                 (*next)->set_position (region->position(), this);
2133                                                 region->set_position (new_pos, this);
2134
2135                                                 /* avoid a full sort */
2136
2137                                                 regions.erase (i); // removes the region from the list */
2138                                                 next++;
2139                                                 regions.insert (next, region); // adds it back after next
2140
2141                                                 moved = true;
2142                                         }
2143                                         break;
2144                                 }
2145                         }
2146                 } else {
2147                         
2148                         RegionList::iterator prev = regions.end();
2149                         
2150                         for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {     
2151                                 if ((*i) == region) {
2152
2153                                         if (prev != regions.end()) {
2154
2155                                                 if ((*prev)->locked()) {
2156                                                         break;
2157                                                 }
2158
2159                                                 if (region->position() != (*prev)->last_frame() + 1) {
2160                                                         /* they didn't used to touch, so after shuffle,
2161                                                            just have them swap positions.
2162                                                         */
2163                                                         new_pos = region->position();
2164                                                 } else {
2165                                                         /* they used to touch, so after shuffle,
2166                                                            make sure they still do. put the earlier
2167                                                            one where the later one will end after
2168                                                         */
2169                                                         new_pos = (*prev)->position() + region->length();
2170                                                 }
2171
2172                                                 region->set_position ((*prev)->position(), this);
2173                                                 (*prev)->set_position (new_pos, this);
2174                                                 
2175                                                 /* avoid a full sort */
2176
2177                                                 regions.erase (i); // remove region
2178                                                 regions.insert (prev, region); // insert region before prev
2179
2180                                                 moved = true;
2181                                         }
2182
2183                                         break;
2184                                 }
2185                         }
2186                 }
2187         }
2188
2189         _shuffling = false;
2190
2191         if (moved) {
2192
2193                 relayer ();
2194                 check_dependents (region, false);
2195                 
2196                 notify_modified();
2197         }
2198
2199 }
2200
2201 bool
2202 Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>) 
2203 {
2204         RegionLock rlock (const_cast<Playlist*> (this));
2205         
2206         if (regions.size() > 1) {
2207                 return true;
2208         }
2209
2210         return false;
2211 }