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