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