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