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