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