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