fc1dd840662db9beed64c90e9493ee35b7dd72b6
[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::partition (jack_nframes_t start, jack_nframes_t end, bool just_top_level)
603 {
604         RegionList thawlist;
605
606         partition_internal (start, end, false, thawlist);
607
608         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
609                 (*i)->thaw ("separation");
610         }
611
612         maybe_save_state (_("separate"));
613 }
614
615 void
616 Playlist::partition_internal (jack_nframes_t start, jack_nframes_t end, bool cutting, RegionList& thawlist)
617 {
618         RegionLock rlock (this);
619         Region *region;
620         Region *current;
621         string new_name;
622         RegionList::iterator tmp;
623         OverlapType overlap;
624         jack_nframes_t pos1, pos2, pos3, pos4;
625         RegionList new_regions;
626
627         in_partition = true;
628
629         /* need to work from a copy, because otherwise the regions we add during the process
630            get operated on as well.
631         */
632
633         RegionList copy = regions;
634
635         for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
636                 
637                 tmp = i;
638                 ++tmp;
639
640                 current = *i;
641                 
642                 if (current->first_frame() == start && current->last_frame() == end) {
643                         if (cutting) {
644                                 remove_region_internal (current);
645                         }
646                         continue;
647                 }
648                 
649                 if ((overlap = current->coverage (start, end)) == OverlapNone) {
650                         continue;
651                 }
652                 
653                 pos1 = current->position();
654                 pos2 = start;
655                 pos3 = end;
656                 pos4 = current->last_frame();
657
658                 if (overlap == OverlapInternal) {
659                         
660                         /* split: we need 3 new regions, the front, middle and end.
661                            cut:   we need 2 regions, the front and end.
662                         */
663                         
664                         /*
665                                          start                 end
666                           ---------------*************************------------
667                                          P1  P2              P3  P4
668                           SPLIT:
669                           ---------------*****++++++++++++++++====------------
670                           CUT
671                           ---------------*****----------------====------------
672                           
673                         */
674
675                         if (!cutting) {
676                                 
677                                 /* "middle" ++++++ */
678                                 
679                                 _session.region_name (new_name, current->name(), false);
680                                 region = createRegion (*current, pos2 - pos1, pos3 - pos2, new_name,
681                                                        regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
682                                 add_region_internal (region, start, true);
683                                 new_regions.push_back (region);
684                         }
685                         
686                         /* "end" ====== */
687                         
688                         _session.region_name (new_name, current->name(), false);
689                         region = createRegion (*current, pos3 - pos1, pos4 - pos3, new_name, 
690                                                regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
691
692                         add_region_internal (region, end, true);
693                         new_regions.push_back (region);
694
695                         /* "front" ***** */
696                                 
697                         current->freeze ();
698                         thawlist.push_back (current);
699                         current->trim_end (pos2, this);
700
701                 } else if (overlap == OverlapEnd) {
702
703                         /*
704                                                               start           end
705                                     ---------------*************************------------
706                                                    P1           P2         P4   P3
707                                     SPLIT:                                                 
708                                     ---------------**************+++++++++++------------
709                                     CUT:                                                   
710                                     ---------------**************-----------------------
711
712                         */
713
714                         if (!cutting) {
715                                 
716                                 /* end +++++ */
717                                 
718                                 _session.region_name (new_name, current->name(), false);
719                                 region = createRegion (*current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
720                                                        Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
721                                 add_region_internal (region, start, true);
722                                 new_regions.push_back (region);
723                         }
724
725                         /* front ****** */
726
727                         current->freeze ();
728                         thawlist.push_back (current);
729                         current->trim_end (pos2, this);
730
731                 } else if (overlap == OverlapStart) {
732
733                         /* split: we need 2 regions: the front and the end.
734                            cut: just trim current to skip the cut area
735                         */
736                                 
737                         /*
738                                                         start           end
739                                     ---------------*************************------------
740                                        P2          P1 P3                   P4          
741
742                                     SPLIT:
743                                     ---------------****+++++++++++++++++++++------------
744                                     CUT:
745                                     -------------------*********************------------
746                                     
747                         */
748
749                         if (!cutting) {
750                                 
751                                 /* front **** */
752                                  _session.region_name (new_name, current->name(), false);
753                                  region = createRegion (*current, 0, pos3 - pos1, new_name,
754                                                         regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
755                                  add_region_internal (region, pos1, true);
756                                  new_regions.push_back (region);
757                         } 
758                         
759                         /* end */
760                         
761                         current->freeze ();
762                         thawlist.push_back (current);
763                         current->trim_front (pos3, this);
764
765                 } else if (overlap == OverlapExternal) {
766
767                         /* split: no split required.
768                            cut: remove the region.
769                         */
770                                 
771                         /*
772                                        start                                      end
773                                     ---------------*************************------------
774                                        P2          P1 P3                   P4          
775
776                                     SPLIT:
777                                     ---------------*************************------------
778                                     CUT:
779                                     ----------------------------------------------------
780                                     
781                         */
782
783                         if (cutting) {
784                                 remove_region_internal (current);
785                         }
786                         new_regions.push_back (current);
787                 }
788         }
789
790         in_partition = false;
791
792         for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
793                 check_dependents (**i, false);
794         }
795 }
796
797 Playlist*
798 Playlist::cut_copy (Playlist* (Playlist::*pmf)(jack_nframes_t, jack_nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
799 {
800         Playlist* ret;
801         Playlist* pl;
802         jack_nframes_t start;
803
804         if (ranges.empty()) {
805                 return 0;
806         }
807
808         start = ranges.front().start;
809
810
811         for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
812
813                 pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
814                 
815                 if (i == ranges.begin()) {
816                         ret = pl;
817                 } else {
818                         
819                         /* paste the next section into the nascent playlist,
820                            offset to reflect the start of the first range we
821                            chopped.
822                         */
823
824                         ret->paste (*pl, (*i).start - start, 1.0f);
825                         delete pl;
826                 }
827         }
828
829         if (ret) {
830                 /* manually notify session of new playlist here
831                    because the playlists were constructed without notifying 
832                 */
833                 PlaylistCreated (ret);
834         }
835         
836         return ret;
837 }
838
839 Playlist*
840 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
841 {
842         Playlist* (Playlist::*pmf)(jack_nframes_t,jack_nframes_t,bool) = &Playlist::cut;
843         return cut_copy (pmf, ranges, result_is_hidden);
844 }
845
846 Playlist*
847 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
848 {
849         Playlist* (Playlist::*pmf)(jack_nframes_t,jack_nframes_t,bool) = &Playlist::copy;
850         return cut_copy (pmf, ranges, result_is_hidden);
851 }
852
853 Playlist *
854 Playlist::cut (jack_nframes_t start, jack_nframes_t cnt, bool result_is_hidden)
855 {
856         Playlist *the_copy;
857         RegionList thawlist;
858         char buf[32];
859
860         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
861         string new_name = _name;
862         new_name += '.';
863         new_name += buf;
864
865         if ((the_copy = copyPlaylist (*this, start, cnt, new_name, result_is_hidden)) == 0) {
866                 return 0;
867         }
868
869         partition_internal (start, start+cnt-1, true, thawlist);
870         possibly_splice ();
871
872         for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
873                 (*i)->thaw ("playlist cut");
874         }
875
876         maybe_save_state (_("cut"));
877
878         return the_copy;
879 }
880
881 Playlist *
882 Playlist::copy (jack_nframes_t start, jack_nframes_t cnt, bool result_is_hidden)
883 {
884         char buf[32];
885         
886         snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
887         string new_name = _name;
888         new_name += '.';
889         new_name += buf;
890
891         cnt = min (_get_maximum_extent() - start, cnt);
892         return copyPlaylist (*this, start, cnt, new_name, result_is_hidden);
893 }
894
895 int
896 Playlist::paste (Playlist& other, jack_nframes_t position, float times)
897 {
898         times = fabs (times);
899         jack_nframes_t old_length;
900
901         {
902                 RegionLock rl1 (this);
903                 RegionLock rl2 (&other);
904
905                 old_length = _get_maximum_extent();
906         
907                 int itimes = (int) floor (times);
908                 jack_nframes_t pos = position;
909                 jack_nframes_t shift = other._get_maximum_extent();
910                 layer_t top_layer = regions.size();
911
912                 while (itimes--) {
913                         for (RegionList::iterator i = other.regions.begin(); i != other.regions.end(); ++i) {
914                                 Region *copy_of_region = createRegion (**i);
915
916                                 /* put these new regions on top of all existing ones, but preserve
917                                    the ordering they had in the original playlist.
918                                 */
919                                 
920                                 copy_of_region->set_layer (copy_of_region->layer() + top_layer);
921                                 add_region_internal (copy_of_region, copy_of_region->position() + pos);
922                         }
923                         pos += shift;
924                 }
925
926                 possibly_splice_unlocked ();
927
928                 /* XXX shall we handle fractional cases at some point? */
929
930                 if (old_length != _get_maximum_extent()) {
931                         notify_length_changed ();
932                 }
933
934                 
935         }
936
937         maybe_save_state (_("paste"));
938
939         return 0;
940 }
941
942
943 void
944 Playlist::duplicate (Region& region, jack_nframes_t position, float times)
945 {
946         times = fabs (times);
947
948         RegionLock rl (this);
949         int itimes = (int) floor (times);
950         jack_nframes_t pos = position;
951
952         while (itimes--) {
953                 Region *copy = createRegion (region);
954                 add_region_internal (copy, pos, true);
955                 pos += region.length();
956         }
957
958         if (floor (times) != times) {
959                 jack_nframes_t length = (jack_nframes_t) floor (region.length() * (times - floor (times)));
960                 string name;
961                 _session.region_name (name, region.name(), false);
962                 Region *sub = createRegion (region, 0, length, name, region.layer(), region.flags());
963                 add_region_internal (sub, pos, true);
964         }
965
966         maybe_save_state (_("duplicate"));
967 }
968
969 void
970 Playlist::split_region (Region& region, jack_nframes_t playlist_position)
971 {
972         RegionLock rl (this);
973
974         if (!region.covers (playlist_position)) {
975                 return;
976         }
977
978         if (region.position() == playlist_position ||
979             region.last_frame() == playlist_position) {
980                 return;
981         }
982
983         Region *left;
984         Region *right;
985         jack_nframes_t before;
986         jack_nframes_t after;
987         string before_name;
988         string after_name;
989
990         before = playlist_position - region.position();
991         after = region.length() - before;
992         
993         
994         _session.region_name (before_name, region.name(), false);
995         left = createRegion (region, 0, before, before_name, region.layer(), Region::Flag (region.flags()|Region::LeftOfSplit));
996
997         _session.region_name (after_name, region.name(), false);
998         right = createRegion (region, before, after, after_name, region.layer(), Region::Flag (region.flags()|Region::RightOfSplit));
999
1000         add_region_internal (left, region.position(), true);
1001         add_region_internal (right, region.position() + before);
1002         
1003         uint64_t orig_layer_op = region.last_layer_op();
1004         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1005                 if ((*i)->last_layer_op() > orig_layer_op) {
1006                         (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
1007                 }
1008         }
1009         
1010         left->set_last_layer_op ( orig_layer_op );
1011         right->set_last_layer_op ( orig_layer_op + 1);
1012
1013         layer_op_counter++;
1014
1015         finalize_split_region (&region, left, right);
1016         
1017         if (remove_region_internal (&region, true)) {
1018                 return;
1019         }
1020
1021         maybe_save_state (_("split"));
1022 }
1023
1024 void
1025 Playlist::possibly_splice ()
1026 {
1027         if (_edit_mode == Splice) {
1028                 splice_locked ();
1029         }
1030 }
1031
1032 void
1033 Playlist::possibly_splice_unlocked ()
1034 {
1035         if (_edit_mode == Splice) {
1036                 splice_unlocked ();
1037         }
1038 }
1039
1040 void
1041 Playlist::splice_locked ()
1042 {
1043         {
1044                 RegionLock rl (this);
1045                 core_splice ();
1046         }
1047
1048         notify_length_changed ();
1049 }
1050
1051 void
1052 Playlist::splice_unlocked ()
1053 {
1054         core_splice ();
1055         notify_length_changed ();
1056 }
1057
1058 void
1059 Playlist::core_splice ()
1060 {
1061         _splicing = true;
1062         
1063         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1064                 
1065                 RegionList::iterator next;
1066                 
1067                 next = i;
1068                 ++next;
1069                 
1070                 if (next == regions.end()) {
1071                         break;
1072                 }
1073                 
1074                 (*next)->set_position ((*i)->last_frame() + 1, this);
1075         }
1076         
1077         _splicing = false;
1078 }
1079
1080 void
1081 Playlist::region_bounds_changed (Change what_changed, Region *region)
1082 {
1083         if (in_set_state || _splicing || _nudging) {
1084                 return;
1085         }
1086
1087         if (what_changed & ARDOUR::PositionChanged) {
1088
1089                 /* remove it from the list then add it back in
1090                    the right place again.
1091                 */
1092                 
1093                 RegionSortByPosition cmp;
1094
1095                 RegionList::iterator i = find (regions.begin(), regions.end(), region);
1096                 
1097                 if (i == regions.end()) {
1098                         warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
1099                                             _name, region->name())
1100                                 << endmsg;
1101                         return;
1102                 }
1103
1104                 regions.erase (i);
1105                 regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp),
1106                                 region);
1107
1108         }
1109
1110         if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
1111         
1112                 if (holding_state ()) {
1113                         pending_bounds.push_back (region);
1114                 } else {
1115                         if (_session.get_layer_model() == Session::MoveAddHigher) {
1116                                 /* it moved or changed length, so change the timestamp */
1117                                 timestamp_layer_op (*region);
1118                         }
1119                         
1120                         possibly_splice ();
1121                         check_dependents (*region, false);
1122                         notify_length_changed ();
1123                         relayer ();
1124                 }
1125         }
1126 }
1127
1128 void
1129 Playlist::region_changed_proxy (Change what_changed, Region* region)
1130 {
1131         /* this makes a virtual call to the right kind of playlist ... */
1132
1133         region_changed (what_changed, region);
1134 }
1135
1136 bool
1137 Playlist::region_changed (Change what_changed, Region* region)
1138 {
1139         Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
1140         bool save = false;
1141
1142         if (in_set_state || in_flush) {
1143                 return false;
1144         }
1145
1146         {
1147                 if (what_changed & BoundsChanged) {
1148                         region_bounds_changed (what_changed, region);
1149                         save = !(_splicing || _nudging);
1150                 }
1151                 
1152                 if ((what_changed & Region::MuteChanged) && 
1153                     !(what_changed &  Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
1154                         check_dependents (*region, false);
1155                 }
1156                 
1157                 if (what_changed & our_interests) {
1158                         save = true;
1159                 }
1160         }
1161
1162         return save;
1163 }
1164
1165 void
1166 Playlist::clear (bool with_delete, bool with_save)
1167 {
1168         RegionList::iterator i;
1169         RegionList tmp;
1170
1171         { 
1172                 RegionLock rl (this);
1173                 tmp = regions;
1174                 regions.clear ();
1175         }
1176         
1177         for (i = tmp.begin(); i != tmp.end(); ++i) {
1178                 notify_region_removed (*i);
1179                 if (with_delete) {
1180                         delete *i;
1181                 }
1182         }
1183
1184         if (with_save) {
1185                 maybe_save_state (_("clear"));
1186         }
1187 }
1188
1189 /***********************************************************************
1190  FINDING THINGS
1191  **********************************************************************/
1192
1193 Playlist::RegionList *
1194 Playlist::regions_at (jack_nframes_t frame)
1195
1196 {
1197         RegionLock rlock (this);
1198         return find_regions_at (frame);
1199 }       
1200
1201 Region *
1202 Playlist::top_region_at (jack_nframes_t frame)
1203
1204 {
1205         RegionLock rlock (this);
1206         RegionList *rlist = find_regions_at (frame);
1207         Region *region = 0;
1208
1209         if (rlist->size()) {
1210                 RegionSortByLayer cmp;
1211                 rlist->sort (cmp);
1212                 region = rlist->back();
1213         } 
1214
1215         delete rlist;
1216         return region;
1217 }       
1218
1219 Playlist::RegionList *
1220 Playlist::find_regions_at (jack_nframes_t frame)
1221 {
1222         RegionList *rlist = new RegionList;
1223
1224         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1225                 if ((*i)->covers (frame)) {
1226                         rlist->push_back (*i);
1227                 }
1228         }
1229
1230         return rlist;
1231 }
1232
1233 Playlist::RegionList *
1234 Playlist::regions_touched (jack_nframes_t start, jack_nframes_t end)
1235 {
1236         RegionLock rlock (this);
1237         RegionList *rlist = new RegionList;
1238
1239         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1240                 if ((*i)->coverage (start, end) != OverlapNone) {
1241                         rlist->push_back (*i);
1242                 }
1243         }
1244
1245         return rlist;
1246 }
1247
1248
1249 Region*
1250
1251 Playlist::find_next_region (jack_nframes_t frame, RegionPoint point, int dir)
1252 {
1253         RegionLock rlock (this);
1254         Region* ret = 0;
1255         jack_nframes_t closest = max_frames;
1256
1257         for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1258
1259                 jack_nframes_t distance;
1260                 Region* r = (*i);
1261                 jack_nframes_t pos = 0;
1262
1263                 switch (point) {
1264                 case Start:
1265                         pos = r->first_frame ();
1266                         break;
1267                 case End:
1268                         pos = r->last_frame ();
1269                         break;
1270                 case SyncPoint:
1271                         pos = r->adjust_to_sync (r->first_frame());
1272                         break;
1273                 }
1274
1275                 switch (dir) {
1276                 case 1: /* forwards */
1277
1278                         if (pos > frame) {
1279                                 if ((distance = pos - frame) < closest) {
1280                                         closest = distance;
1281                                         ret = r;
1282                                 }
1283                         }
1284
1285                         break;
1286
1287                 default: /* backwards */
1288
1289                         if (pos < frame) {
1290                                 if ((distance = frame - pos) < closest) {
1291                                         closest = distance;
1292                                         ret = r;
1293                                 }
1294                         }
1295                         break;
1296                 }
1297         }
1298
1299         return ret;
1300 }
1301
1302 /***********************************************************************/
1303
1304
1305
1306 void
1307 Playlist::mark_session_dirty ()
1308 {
1309         if (!in_set_state && !holding_state ()) {
1310                 _session.set_dirty();
1311         }
1312 }
1313
1314 int
1315 Playlist::set_state (const XMLNode& node)
1316 {
1317         in_set_state = true;
1318
1319         XMLNode *child;
1320         XMLNodeList nlist;
1321         XMLNodeConstIterator niter;
1322         XMLPropertyList plist;
1323         XMLPropertyConstIterator piter;
1324         XMLProperty *prop;
1325         Region *region;
1326         string region_name;
1327
1328         clear (false, false);
1329
1330         if (node.name() != "Playlist") {
1331                 in_set_state = false;
1332                 return -1;
1333         }
1334
1335         plist = node.properties();
1336
1337         for (piter = plist.begin(); piter != plist.end(); ++piter) {
1338
1339                 prop = *piter;
1340                 
1341                 if (prop->name() == X_("name")) {
1342                         _name = prop->value();
1343                 } else if (prop->name() == X_("orig_diskstream_id")) {
1344                         _orig_diskstream_id = prop->value ();
1345                 } else if (prop->name() == X_("frozen")) {
1346                         _frozen = (prop->value() == X_("yes"));
1347                 }
1348         }
1349
1350         nlist = node.children();
1351
1352         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1353
1354                 child = *niter;
1355                 
1356                 if (child->name() == "Region") {
1357
1358                         if ((region = createRegion (_session, *child, true)) == 0) {
1359                                 error << _("Playlist: cannot create region from state file") << endmsg;
1360                                 continue;
1361                         }
1362
1363                         add_region (*region, region->position(), 1.0, false);
1364
1365                         // So that layer_op ordering doesn't get screwed up
1366                         region->set_last_layer_op( region->layer());
1367
1368                 }                       
1369         }
1370
1371         
1372         /* update dependents, which was not done during add_region_internal 
1373            due to in_set_state being true 
1374         */
1375
1376         for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
1377                 check_dependents (**r, false);
1378         }
1379         
1380         in_set_state = false;
1381
1382         return 0;
1383 }
1384
1385 XMLNode&
1386 Playlist::get_state()
1387 {
1388         return state(true);
1389 }
1390
1391 XMLNode&
1392 Playlist::get_template()
1393 {
1394         return state(false);
1395 }
1396
1397 XMLNode&
1398 Playlist::state (bool full_state)
1399 {
1400         XMLNode *node = new XMLNode (X_("Playlist"));
1401         char buf[64];
1402         
1403         node->add_property (X_("name"), _name);
1404
1405         _orig_diskstream_id.print (buf);
1406         node->add_property (X_("orig_diskstream_id"), buf);
1407         node->add_property (X_("frozen"), _frozen ? "yes" : "no");
1408
1409         if (full_state) {
1410                 RegionLock rlock (this, false);
1411
1412                 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
1413                         node->add_child_nocopy ((*i)->get_state());
1414                 }
1415         }
1416
1417         if (_extra_xml) {
1418                 node->add_child_copy (*_extra_xml);
1419         }
1420
1421         return *node;
1422 }
1423
1424 bool
1425 Playlist::empty() const
1426 {
1427         return regions.empty();
1428 }
1429
1430 jack_nframes_t
1431 Playlist::get_maximum_extent () const
1432 {
1433         RegionLock rlock (const_cast<Playlist *>(this));
1434         return _get_maximum_extent ();
1435 }
1436
1437 jack_nframes_t
1438 Playlist::_get_maximum_extent () const
1439 {
1440         RegionList::const_iterator i;
1441         jack_nframes_t max_extent = 0;
1442         jack_nframes_t end = 0;
1443
1444         for (i = regions.begin(); i != regions.end(); ++i) {
1445                 if ((end = (*i)->position() + (*i)->length()) > max_extent) {
1446                         max_extent = end;
1447                 }
1448         }
1449
1450         return max_extent;
1451 }
1452
1453 string 
1454 Playlist::bump_name (string name, Session &session)
1455 {
1456         string newname = name;
1457
1458         do {
1459                 newname = Playlist::bump_name_once (newname);
1460         } while (session.playlist_by_name(newname)!=NULL);
1461
1462         return newname;
1463 }
1464
1465 string
1466 Playlist::bump_name_once (string name)
1467 {
1468         string::size_type period;
1469         string newname;
1470
1471         if ((period = name.find_last_of ('.')) == string::npos) {
1472                 newname = name;
1473                 newname += ".1";
1474         } else {
1475                 char buf[32];
1476                 int version;
1477                 
1478                 sscanf (name.substr (period+1).c_str(), "%d", &version);
1479                 snprintf (buf, sizeof(buf), "%d", version+1);
1480                 
1481                 newname = name.substr (0, period+1);
1482                 newname += buf;
1483         }
1484
1485         return newname;
1486 }
1487
1488 layer_t
1489 Playlist::top_layer() const
1490 {
1491         RegionLock rlock (const_cast<Playlist *> (this));
1492         layer_t top = 0;
1493
1494         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1495                 top = max (top, (*i)->layer());
1496         }
1497         return top;
1498 }
1499
1500 void
1501 Playlist::set_edit_mode (EditMode mode)
1502 {
1503         _edit_mode = mode;
1504 }
1505
1506 /********************
1507  * Region Layering
1508  ********************/
1509
1510 void
1511 Playlist::relayer ()
1512 {
1513         RegionList::iterator i;
1514         uint32_t layer = 0;
1515
1516         /* don't send multiple Modified notifications
1517            when multiple regions are relayered.
1518         */
1519
1520         freeze ();
1521
1522         if (_session.get_layer_model() == Session::MoveAddHigher || 
1523             _session.get_layer_model() == Session::AddHigher) {
1524
1525                 RegionSortByLastLayerOp cmp;
1526                 RegionList copy = regions;
1527
1528                 copy.sort (cmp);
1529
1530                 for (i = copy.begin(); i != copy.end(); ++i) {
1531                         (*i)->set_layer (layer++);
1532                 }
1533
1534         } else {
1535                 
1536                 /* Session::LaterHigher model */
1537
1538                 for (i = regions.begin(); i != regions.end(); ++i) {
1539                         (*i)->set_layer (layer++);
1540                 }
1541         }
1542
1543         /* sending Modified means that various kinds of layering
1544            models operate correctly at the GUI
1545            level. slightly inefficient, but only slightly.
1546
1547            We force a Modified signal here in case no layers actually
1548            changed.
1549         */
1550
1551         notify_modified ();
1552
1553         thaw ();
1554 }
1555
1556 /* XXX these layer functions are all deprecated */
1557
1558 void
1559 Playlist::raise_region (Region& region)
1560 {
1561         uint32_t rsz = regions.size();
1562         layer_t target = region.layer() + 1U;
1563
1564         if (target >= rsz) {
1565                 /* its already at the effective top */
1566                 return;
1567         }
1568
1569         move_region_to_layer (target, region, 1);
1570 }
1571
1572 void
1573 Playlist::lower_region (Region& region)
1574 {
1575         if (region.layer() == 0) {
1576                 /* its already at the bottom */
1577                 return;
1578         }
1579
1580         layer_t target = region.layer() - 1U;
1581
1582         move_region_to_layer (target, region, -1);
1583 }
1584
1585 void
1586 Playlist::raise_region_to_top (Region& region)
1587 {
1588         /* does nothing useful if layering mode is later=higher */
1589         if ((_session.get_layer_model() == Session::MoveAddHigher) ||
1590             (_session.get_layer_model() == Session::AddHigher)) {
1591                 timestamp_layer_op (region);
1592                 relayer ();
1593         }
1594 }
1595
1596 void
1597 Playlist::lower_region_to_bottom (Region& region)
1598 {
1599         /* does nothing useful if layering mode is later=higher */
1600         if ((_session.get_layer_model() == Session::MoveAddHigher) ||
1601             (_session.get_layer_model() == Session::AddHigher)) {
1602                 region.set_last_layer_op (0);
1603                 relayer ();
1604         }
1605 }
1606
1607 int
1608 Playlist::move_region_to_layer (layer_t target_layer, Region& region, int dir)
1609 {
1610         RegionList::iterator i;
1611         typedef pair<Region*,layer_t> LayerInfo;
1612         list<LayerInfo> layerinfo;
1613         layer_t dest;
1614
1615         {
1616                 RegionLock rlock (const_cast<Playlist *> (this));
1617                 
1618                 for (i = regions.begin(); i != regions.end(); ++i) {
1619                         
1620                         if (&region == *i) {
1621                                 continue;
1622                         }
1623
1624                         if (dir > 0) {
1625
1626                                 /* region is moving up, move all regions on intermediate layers
1627                                    down 1
1628                                 */
1629                                 
1630                                 if ((*i)->layer() > region.layer() && (*i)->layer() <= target_layer) {
1631                                         dest = (*i)->layer() - 1;
1632                                 } else {
1633                                         /* not affected */
1634                                         continue;
1635                                 }
1636                         } else {
1637
1638                                 /* region is moving down, move all regions on intermediate layers
1639                                    up 1
1640                                 */
1641
1642                                 if ((*i)->layer() < region.layer() && (*i)->layer() >= target_layer) {
1643                                         dest = (*i)->layer() + 1;
1644                                 } else {
1645                                         /* not affected */
1646                                         continue;
1647                                 }
1648                         }
1649
1650                         LayerInfo newpair;
1651                         
1652                         newpair.first = *i;
1653                         newpair.second = dest;
1654                         
1655                         layerinfo.push_back (newpair);
1656                 } 
1657         }
1658
1659         /* now reset the layers without holding the region lock */
1660
1661         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1662                 x->first->set_layer (x->second);
1663         }
1664
1665         region.set_layer (target_layer);
1666
1667         /* now check all dependents */
1668
1669         for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
1670                 check_dependents (*(x->first), false);
1671         }
1672         
1673         check_dependents (region, false);
1674         
1675         return 0;
1676 }
1677
1678 void
1679 Playlist::nudge_after (jack_nframes_t start, jack_nframes_t distance, bool forwards)
1680 {
1681         RegionList::iterator i;
1682         jack_nframes_t new_pos;
1683         bool moved = false;
1684
1685         _nudging = true;
1686
1687         {
1688                 RegionLock rlock (const_cast<Playlist *> (this));
1689                 
1690                 for (i = regions.begin(); i != regions.end(); ++i) {
1691
1692                         if ((*i)->position() >= start) {
1693
1694                                 if (forwards) {
1695
1696                                         if ((*i)->last_frame() > max_frames - distance) {
1697                                                 new_pos = max_frames - (*i)->length();
1698                                         } else {
1699                                                 new_pos = (*i)->position() + distance;
1700                                         }
1701                                         
1702                                 } else {
1703                                         
1704                                         if ((*i)->position() > distance) {
1705                                                 new_pos = (*i)->position() - distance;
1706                                         } else {
1707                                                 new_pos = 0;
1708                                         }
1709                                 }
1710
1711                                 (*i)->set_position (new_pos, this);
1712                                 moved = true;
1713                         }
1714                 }
1715         }
1716
1717         if (moved) {
1718                 _nudging = false;
1719                 maybe_save_state (_("nudged"));
1720                 notify_length_changed ();
1721         }
1722
1723 }
1724
1725 Region*
1726 Playlist::find_region (const ID& id) const
1727 {
1728         RegionLock rlock (const_cast<Playlist*> (this));
1729         RegionList::const_iterator i;
1730         
1731         for (i = regions.begin(); i != regions.end(); ++i) {
1732                 if ((*i)->id() == id) {
1733                         return (*i);
1734                 }
1735         }
1736
1737         return 0;
1738 }
1739         
1740 void
1741 Playlist::save_state (std::string why)
1742 {
1743         if (!in_set_state) {
1744                 StateManager::save_state (why);
1745         }
1746 }
1747
1748 void
1749 Playlist::dump () const
1750 {
1751         Region *r;
1752
1753         cerr << "Playlist \"" << _name << "\" " << endl
1754              << regions.size() << " regions "
1755              << endl;
1756
1757         for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
1758                 r = *i;
1759                 cerr << "  " << r->name() << " [" 
1760                      << r->start() << "+" << r->length() 
1761                      << "] at " 
1762                      << r->position()
1763                      << " on layer "
1764                      << r->layer ()
1765                      << endl;
1766         }
1767 }
1768
1769 void
1770 Playlist::set_frozen (bool yn)
1771 {
1772         _frozen = yn;
1773 }
1774
1775 void
1776 Playlist::timestamp_layer_op (Region& region)
1777 {
1778 //      struct timeval tv;
1779 //      gettimeofday (&tv, 0);
1780         region.set_last_layer_op (++layer_op_counter);
1781 }
1782
1783 void
1784 Playlist::maybe_save_state (string why)
1785 {
1786         if (holding_state ()) {
1787                 save_on_thaw = true;
1788                 last_save_reason = why;
1789         } else {
1790                 save_state (why);
1791         }
1792 }