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