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