Make import GUI report if you are importing a file of a name that
[ardour.git] / libs / ardour / region.cc
1 /*
2     Copyright (C) 2000-2003 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include <cmath>
22 #include <climits>
23 #include <algorithm>
24 #include <sstream>
25
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
28
29 #include <glibmm/thread.h>
30 #include <pbd/xml++.h>
31 #include <pbd/stacktrace.h>
32 #include <pbd/enumwriter.h>
33
34 #include <ardour/region.h>
35 #include <ardour/playlist.h>
36 #include <ardour/session.h>
37 #include <ardour/tempo.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 Change Region::FadeChanged       = ARDOUR::new_change ();
47 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
48 Change Region::MuteChanged       = ARDOUR::new_change ();
49 Change Region::OpacityChanged    = ARDOUR::new_change ();
50 Change Region::LockChanged       = ARDOUR::new_change ();
51 Change Region::LayerChanged      = ARDOUR::new_change ();
52 Change Region::HiddenChanged     = ARDOUR::new_change ();
53
54 Region::Region (nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags)
55 {
56         /* basic Region constructor */
57
58         _flags = flags;
59         _read_data_count = 0;
60         _frozen = 0;
61         pending_changed = Change (0);
62         valid_transients = false;
63         _name = name;
64         _start = start; 
65         _sync_position = _start;
66         _length = length; 
67         _last_length = length; 
68         _ancestral_start = start; 
69         _ancestral_length = length; 
70         _stretch = 1.0;
71         _shift = 1.0;
72         _last_position = 0; 
73         _position = 0; 
74         _layer = layer;
75         _read_data_count = 0;
76         _first_edit = EditChangesNothing;
77         _last_layer_op = 0;
78         _positional_lock_style = AudioTime;
79 }
80
81 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
82 {
83         /* create a new Region from part of an existing one */
84
85         _frozen = 0;
86         pending_changed = Change (0);
87         _read_data_count = 0;
88         valid_transients = false;
89
90         _start = other->_start + offset; 
91         if (other->_sync_position < offset) {
92                 _sync_position = other->_sync_position;
93         } else {
94                 _sync_position = _start;
95         }
96         _length = length; 
97         _last_length = length; 
98         _ancestral_start = other->_ancestral_start + offset;
99         _ancestral_length = length; 
100         _stretch = 1.0;
101         _shift = 1.0;
102         _name = name;
103         _last_position = 0; 
104         _position = 0; 
105         _layer = layer; 
106         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
107         _first_edit = EditChangesNothing;
108         _last_layer_op = 0;
109         _positional_lock_style = AudioTime;
110 }
111
112 Region::Region (boost::shared_ptr<const Region> other)
113 {
114         /* Pure copy constructor */
115
116         _frozen = 0;
117         pending_changed = Change (0);
118         _read_data_count = 0;
119         valid_transients = false;
120
121         _first_edit = EditChangesID;
122         other->_first_edit = EditChangesName;
123
124         if (other->_extra_xml) {
125                 _extra_xml = new XMLNode (*other->_extra_xml);
126         } else {
127                 _extra_xml = 0;
128         }
129
130         _start = other->_start;
131         _sync_position = other->_sync_position;
132         _length = other->_length; 
133         _last_length = other->_length; 
134         _ancestral_start = _start; 
135         _ancestral_length = _length; 
136         _stretch = 1.0;
137         _shift = 1.0;
138         _name = other->_name;
139         _last_position = other->_position; 
140         _position = other->_position; 
141         _layer = other->_layer; 
142         _flags = Flag (other->_flags & ~Locked);
143         _last_layer_op = other->_last_layer_op;
144         _positional_lock_style = AudioTime;
145 }
146
147 Region::Region (const XMLNode& node)
148 {
149         _frozen = 0;
150         pending_changed = Change (0);
151         valid_transients = false;
152         _read_data_count = 0;
153         _start = 0; 
154         _sync_position = _start;
155         _length = 0;
156         _last_length = 0;
157         _name = X_("error: XML did not reset this");
158         _last_position = 0; 
159         _position = 0; 
160         _layer = 0;
161         _flags = Flag (0);
162         _first_edit = EditChangesNothing;
163         _positional_lock_style = AudioTime;
164
165         if (set_state (node)) {
166                 throw failed_constructor();
167         }
168 }
169
170 Region::~Region ()
171 {
172         /* derived classes must call notify_callbacks() and then emit GoingAway */
173 }
174
175 void
176 Region::set_playlist (boost::weak_ptr<Playlist> pl)
177 {
178         _playlist = pl;
179 }
180
181 void
182 Region::set_name (string str)
183 {
184         if (_name != str) {
185                 _name = str; 
186                 send_change (NameChanged);
187         }
188 }
189
190 void
191 Region::set_length (nframes_t len, void *src)
192 {
193         if (_flags & Locked) {
194                 return;
195         }
196
197         if (_length != len && len != 0) {
198
199                 /* check that the current _position wouldn't make the new 
200                    length impossible.
201                 */
202
203                 if (max_frames - len < _position) {
204                         return;
205                 }
206
207                 if (!verify_length (len)) {
208                         return;
209                 }
210                 
211
212                 _last_length = _length;
213                 _length = len;
214
215                 _flags = Region::Flag (_flags & ~WholeFile);
216
217                 first_edit ();
218                 maybe_uncopy ();
219                 invalidate_transients ();
220
221                 if (!_frozen) {
222                         recompute_at_end ();
223                 }
224
225                 send_change (LengthChanged);
226         }
227 }
228
229 void
230 Region::maybe_uncopy ()
231 {
232 }
233
234 void
235 Region::first_edit ()
236 {
237         boost::shared_ptr<Playlist> pl (playlist());
238
239         if (_first_edit != EditChangesNothing && pl) {
240
241                 _name = pl->session().new_region_name (_name);
242                 _first_edit = EditChangesNothing;
243
244                 send_change (NameChanged);
245                 RegionFactory::CheckNewRegion (shared_from_this());
246         }
247 }
248
249 bool
250 Region::at_natural_position () const
251 {
252         boost::shared_ptr<Playlist> pl (playlist());
253
254         if (!pl) {
255                 return false;
256         }
257         
258         boost::shared_ptr<Region> whole_file_region = get_parent();
259
260         if (whole_file_region) {
261                 if (_position == whole_file_region->position() + _start) {
262                         return true;
263                 }
264         }
265
266         return false;
267 }
268
269 void
270 Region::move_to_natural_position (void *src)
271 {
272         boost::shared_ptr<Playlist> pl (playlist());
273
274         if (!pl) {
275                 return;
276         }
277         
278         boost::shared_ptr<Region> whole_file_region = get_parent();
279
280         if (whole_file_region) {
281                 set_position (whole_file_region->position() + _start, src);
282         }
283 }
284         
285 void
286 Region::special_set_position (nframes_t pos)
287 {
288         /* this is used when creating a whole file region as 
289            a way to store its "natural" or "captured" position.
290         */
291
292         _position = _position;
293         _position = pos;
294 }
295
296 void
297 Region::set_position_lock_style (PositionLockStyle ps)
298 {
299         boost::shared_ptr<Playlist> pl (playlist());
300
301         if (!pl) {
302                 return;
303         }
304
305         _positional_lock_style = ps;
306
307         if (_positional_lock_style == MusicTime) {
308                 pl->session().tempo_map().bbt_time (_position, _bbt_time);
309         }
310         
311 }
312
313 void
314 Region::update_position_after_tempo_map_change ()
315 {
316         boost::shared_ptr<Playlist> pl (playlist());
317         
318         if (!pl || _positional_lock_style != MusicTime) {
319                 return;
320         }
321
322         TempoMap& map (pl->session().tempo_map());
323         nframes_t pos = map.frame_time (_bbt_time);
324         set_position_internal (pos, false);
325 }
326
327 void
328 Region::set_position (nframes_t pos, void *src)
329 {
330         if (_flags & Locked) {
331                 return;
332         }
333
334         set_position_internal (pos, true);
335 }
336
337 void
338 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
339 {
340         if (_position != pos) {
341                 _last_position = _position;
342                 _position = pos;
343
344                 /* check that the new _position wouldn't make the current
345                    length impossible - if so, change the length. 
346
347                    XXX is this the right thing to do?
348                 */
349
350                 if (max_frames - _length < _position) {
351                         _last_length = _length;
352                         _length = max_frames - _position;
353                 }
354
355                 if (allow_bbt_recompute && _positional_lock_style == MusicTime) {
356                         boost::shared_ptr<Playlist> pl (playlist());
357                         if (pl) {
358                                 pl->session().tempo_map().bbt_time (_position, _bbt_time);
359                         }
360                 }
361
362                 invalidate_transients ();
363         }
364
365         /* do this even if the position is the same. this helps out
366            a GUI that has moved its representation already.
367         */
368
369         send_change (PositionChanged);
370 }
371
372 void
373 Region::set_position_on_top (nframes_t pos, void *src)
374 {
375         if (_flags & Locked) {
376                 return;
377         }
378
379         if (_position != pos) {
380                 _last_position = _position;
381                 _position = pos;
382         }
383
384         boost::shared_ptr<Playlist> pl (playlist());
385
386         if (pl) {
387                 pl->raise_region_to_top (shared_from_this ());
388         }
389
390         /* do this even if the position is the same. this helps out
391            a GUI that has moved its representation already.
392         */
393         
394         send_change (PositionChanged);
395 }
396
397 void
398 Region::nudge_position (nframes64_t n, void *src)
399 {
400         if (_flags & Locked) {
401                 return;
402         }
403
404         if (n == 0) {
405                 return;
406         }
407         
408         _last_position = _position;
409
410         if (n > 0) {
411                 if (_position > max_frames - n) {
412                         _position = max_frames;
413                 } else {
414                         _position += n;
415                 }
416         } else {
417                 if (_position < (nframes_t) -n) {
418                         _position = 0;
419                 } else {
420                         _position += n;
421                 }
422         }
423
424         send_change (PositionChanged);
425 }
426
427 void
428 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
429 {
430         _ancestral_length = l;
431         _ancestral_start = s;
432         _stretch = st;
433         _shift = sh;
434 }
435
436 void
437 Region::set_start (nframes_t pos, void *src)
438 {
439         if (_flags & Locked) {
440                 return;
441         }
442         /* This just sets the start, nothing else. It effectively shifts
443            the contents of the Region within the overall extent of the Source,
444            without changing the Region's position or length
445         */
446
447         if (_start != pos) {
448
449                 if (!verify_start (pos)) {
450                         return;
451                 }
452
453                 _start = pos;
454                 _flags = Region::Flag (_flags & ~WholeFile);
455                 first_edit ();
456                 invalidate_transients ();
457
458                 send_change (StartChanged);
459         }
460 }
461
462 void
463 Region::trim_start (nframes_t new_position, void *src)
464 {
465         if (_flags & Locked) {
466                 return;
467         }
468         nframes_t new_start;
469         int32_t start_shift;
470         
471         if (new_position > _position) {
472                 start_shift = new_position - _position;
473         } else {
474                 start_shift = -(_position - new_position);
475         }
476
477         if (start_shift > 0) {
478
479                 if (_start > max_frames - start_shift) {
480                         new_start = max_frames;
481                 } else {
482                         new_start = _start + start_shift;
483                 }
484
485                 if (!verify_start (new_start)) {
486                         return;
487                 }
488
489         } else if (start_shift < 0) {
490
491                 if (_start < (nframes_t) -start_shift) {
492                         new_start = 0;
493                 } else {
494                         new_start = _start + start_shift;
495                 }
496         } else {
497                 return;
498         }
499
500         if (new_start == _start) {
501                 return;
502         }
503         
504         _start = new_start;
505         _flags = Region::Flag (_flags & ~WholeFile);
506         first_edit ();
507
508         send_change (StartChanged);
509 }
510
511 void
512 Region::trim_front (nframes_t new_position, void *src)
513 {
514         if (_flags & Locked) {
515                 return;
516         }
517
518         nframes_t end = last_frame();
519         nframes_t source_zero;
520
521         if (_position > _start) {
522                 source_zero = _position - _start;
523         } else {
524                 source_zero = 0; // its actually negative, but this will work for us
525         }
526
527         if (new_position < end) { /* can't trim it zero or negative length */
528                 
529                 nframes_t newlen;
530
531                 /* can't trim it back passed where source position zero is located */
532                 
533                 new_position = max (new_position, source_zero);
534                 
535                 
536                 if (new_position > _position) {
537                         newlen = _length - (new_position - _position);
538                 } else {
539                         newlen = _length + (_position - new_position);
540                 }
541                 
542                 trim_to_internal (new_position, newlen, src);
543                 if (!_frozen) {
544                         recompute_at_start ();
545                 }
546         }
547 }
548
549 void
550 Region::trim_end (nframes_t new_endpoint, void *src)
551 {
552         if (_flags & Locked) {
553                 return;
554         }
555
556         if (new_endpoint > _position) {
557                 trim_to_internal (_position, new_endpoint - _position, this);
558                 if (!_frozen) {
559                         recompute_at_end ();
560                 }
561         }
562 }
563
564 void
565 Region::trim_to (nframes_t position, nframes_t length, void *src)
566 {
567         if (_flags & Locked) {
568                 return;
569         }
570
571         trim_to_internal (position, length, src);
572
573         if (!_frozen) {
574                 recompute_at_start ();
575                 recompute_at_end ();
576         }
577 }
578
579 void
580 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
581 {
582         int32_t start_shift;
583         nframes_t new_start;
584
585         if (_flags & Locked) {
586                 return;
587         }
588
589         if (position > _position) {
590                 start_shift = position - _position;
591         } else {
592                 start_shift = -(_position - position);
593         }
594
595         if (start_shift > 0) {
596
597                 if (_start > max_frames - start_shift) {
598                         new_start = max_frames;
599                 } else {
600                         new_start = _start + start_shift;
601                 }
602
603
604         } else if (start_shift < 0) {
605
606                 if (_start < (nframes_t) -start_shift) {
607                         new_start = 0;
608                 } else {
609                         new_start = _start + start_shift;
610                 }
611         } else {
612                 new_start = _start;
613         }
614
615         if (!verify_start_and_length (new_start, length)) {
616                 return;
617         }
618
619         Change what_changed = Change (0);
620
621         if (_start != new_start) {
622                 _start = new_start;
623                 what_changed = Change (what_changed|StartChanged);
624         }
625         if (_length != length) {
626                 if (!_frozen) {
627                         _last_length = _length;
628                 }
629                 _length = length;
630                 what_changed = Change (what_changed|LengthChanged);
631         }
632         if (_position != position) {
633                 if (!_frozen) {
634                         _last_position = _position;
635                 }
636                 _position = position;
637                 what_changed = Change (what_changed|PositionChanged);
638         }
639         
640         _flags = Region::Flag (_flags & ~WholeFile);
641
642         if (what_changed & (StartChanged|LengthChanged)) {
643                 first_edit ();
644         } 
645
646         if (what_changed) {
647                 send_change (what_changed);
648         }
649 }       
650
651 void
652 Region::set_hidden (bool yn)
653 {
654         if (hidden() != yn) {
655
656                 if (yn) {
657                         _flags = Flag (_flags|Hidden);
658                 } else {
659                         _flags = Flag (_flags & ~Hidden);
660                 }
661
662                 send_change (HiddenChanged);
663         }
664 }
665
666 void
667 Region::set_muted (bool yn)
668 {
669         if (muted() != yn) {
670
671                 if (yn) {
672                         _flags = Flag (_flags|Muted);
673                 } else {
674                         _flags = Flag (_flags & ~Muted);
675                 }
676
677                 send_change (MuteChanged);
678         }
679 }
680
681 void
682 Region::set_opaque (bool yn)
683 {
684         if (opaque() != yn) {
685                 if (yn) {
686                         _flags = Flag (_flags|Opaque);
687                 } else {
688                         _flags = Flag (_flags & ~Opaque);
689                 }
690                         send_change (OpacityChanged);
691         }
692 }
693
694 void
695 Region::set_locked (bool yn)
696 {
697         if (locked() != yn) {
698                 if (yn) {
699                         _flags = Flag (_flags|Locked);
700                 } else {
701                         _flags = Flag (_flags & ~Locked);
702                 }
703                 send_change (LockChanged);
704         }
705 }
706
707 void
708 Region::set_sync_position (nframes_t absolute_pos)
709 {
710         nframes_t file_pos;
711
712         file_pos = _start + (absolute_pos - _position);
713
714         if (file_pos != _sync_position) {
715                 
716                 _sync_position = file_pos;
717                 _flags = Flag (_flags|SyncMarked);
718
719                 if (!_frozen) {
720                         maybe_uncopy ();
721                 }
722                 send_change (SyncOffsetChanged);
723         }
724 }
725
726 void
727 Region::clear_sync_position ()
728 {
729         if (_flags & SyncMarked) {
730                 _flags = Flag (_flags & ~SyncMarked);
731
732                 if (!_frozen) {
733                         maybe_uncopy ();
734                 }
735                 send_change (SyncOffsetChanged);
736         }
737 }
738
739 nframes_t
740 Region::sync_offset (int& dir) const
741 {
742         /* returns the sync point relative the first frame of the region */
743
744         if (_flags & SyncMarked) {
745                 if (_sync_position > _start) {
746                         dir = 1;
747                         return _sync_position - _start; 
748                 } else {
749                         dir = -1;
750                         return _start - _sync_position;
751                 }
752         } else {
753                 dir = 0;
754                 return 0;
755         }
756 }
757
758 nframes_t 
759 Region::adjust_to_sync (nframes_t pos)
760 {
761         int sync_dir;
762         nframes_t offset = sync_offset (sync_dir);
763
764         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
765         
766         if (sync_dir > 0) {
767                 if (pos > offset) {
768                         pos -= offset;
769                 } else {
770                         pos = 0;
771                 }
772         } else {
773                 if (max_frames - pos > offset) {
774                         pos += offset;
775                 }
776         }
777
778         return pos;
779 }
780
781 nframes_t
782 Region::sync_position() const
783 {
784         if (_flags & SyncMarked) {
785                 return _sync_position; 
786         } else {
787                 return _start;
788         }
789 }
790
791
792 void
793 Region::raise ()
794 {
795         boost::shared_ptr<Playlist> pl (playlist());
796         if (pl) {
797                 pl->raise_region (shared_from_this ());
798         }
799 }
800
801 void
802 Region::lower ()
803 {
804         boost::shared_ptr<Playlist> pl (playlist());
805         if (pl) {
806                 pl->lower_region (shared_from_this ());
807         }
808 }
809
810 void
811 Region::raise_to_top ()
812 {
813         boost::shared_ptr<Playlist> pl (playlist());
814         if (pl) {
815                 pl->raise_region_to_top (shared_from_this());
816         }
817 }
818
819 void
820 Region::lower_to_bottom ()
821 {
822         boost::shared_ptr<Playlist> pl (playlist());
823         if (pl) {
824                 pl->lower_region_to_bottom (shared_from_this());
825         }
826 }
827
828 void
829 Region::set_layer (layer_t l)
830 {
831         if (_layer != l) {
832                 _layer = l;
833                 
834                 send_change (LayerChanged);
835         }
836 }
837
838 XMLNode&
839 Region::state (bool full_state)
840 {
841         XMLNode *node = new XMLNode ("Region");
842         char buf[64];
843         const char* fe = NULL;
844
845         _id.print (buf, sizeof (buf));
846         node->add_property ("id", buf);
847         node->add_property ("name", _name);
848         snprintf (buf, sizeof (buf), "%u", _start);
849         node->add_property ("start", buf);
850         snprintf (buf, sizeof (buf), "%u", _length);
851         node->add_property ("length", buf);
852         snprintf (buf, sizeof (buf), "%u", _position);
853         node->add_property ("position", buf);
854         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
855         node->add_property ("ancestral-start", buf);
856         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
857         node->add_property ("ancestral-length", buf);
858         snprintf (buf, sizeof (buf), "%.12g", _stretch);
859         node->add_property ("stretch", buf);
860         snprintf (buf, sizeof (buf), "%.12g", _shift);
861         node->add_property ("shift", buf);
862         
863         switch (_first_edit) {
864         case EditChangesNothing:
865                 fe = X_("nothing");
866                 break;
867         case EditChangesName:
868                 fe = X_("name");
869                 break;
870         case EditChangesID:
871                 fe = X_("id");
872                 break;
873         default: /* should be unreachable but makes g++ happy */
874                 fe = X_("nothing");
875                 break;
876         }
877
878         node->add_property ("first_edit", fe);
879
880         /* note: flags are stored by derived classes */
881
882         snprintf (buf, sizeof (buf), "%d", (int) _layer);
883         node->add_property ("layer", buf);
884         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
885         node->add_property ("sync-position", buf);
886
887         if (_positional_lock_style != AudioTime) {
888                 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
889                 stringstream str;
890                 str << _bbt_time;
891                 node->add_property ("bbt-position", str.str());
892         }
893
894         return *node;
895 }
896
897 XMLNode&
898 Region::get_state ()
899 {
900         return state (true);
901 }
902
903 int
904 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
905 {
906         const XMLNodeList& nlist = node.children();
907         const XMLProperty *prop;
908         nframes_t val;
909
910         /* this is responsible for setting those aspects of Region state 
911            that are mutable after construction.
912         */
913
914         if ((prop = node.property ("name")) == 0) {
915                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
916                 return -1;
917         }
918
919         _name = prop->value();
920
921         if ((prop = node.property ("start")) != 0) {
922                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
923                 if (val != _start) {
924                         what_changed = Change (what_changed|StartChanged);      
925                         _start = val;
926                 }
927         } else {
928                 _start = 0;
929         }
930
931         if ((prop = node.property ("length")) != 0) {
932                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
933                 if (val != _length) {
934                         what_changed = Change (what_changed|LengthChanged);
935                         _last_length = _length;
936                         _length = val;
937                 }
938         } else {
939                 _last_length = _length;
940                 _length = 1;
941         }
942
943         if ((prop = node.property ("position")) != 0) {
944                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
945                 if (val != _position) {
946                         what_changed = Change (what_changed|PositionChanged);
947                         _last_position = _position;
948                         _position = val;
949                 }
950         } else {
951                 _last_position = _position;
952                 _position = 0;
953         }
954
955         if ((prop = node.property ("layer")) != 0) {
956                 layer_t x;
957                 x = (layer_t) atoi (prop->value().c_str());
958                 if (x != _layer) {
959                         what_changed = Change (what_changed|LayerChanged);
960                         _layer = x;
961                 }
962         } else {
963                 _layer = 0;
964         }
965
966         if ((prop = node.property ("sync-position")) != 0) {
967                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
968                 if (val != _sync_position) {
969                         what_changed = Change (what_changed|SyncOffsetChanged);
970                         _sync_position = val;
971                 }
972         } else {
973                 _sync_position = _start;
974         }
975
976         if ((prop = node.property ("positional-lock-style")) != 0) {
977                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
978
979                 if (_positional_lock_style == MusicTime) {
980                         if ((prop = node.property ("bbt-position")) == 0) {
981                                 /* missing BBT info, revert to audio time locking */
982                                 _positional_lock_style = AudioTime;
983                         } else {
984                                 if (sscanf (prop->value().c_str(), "%d|%d|%d", 
985                                             &_bbt_time.bars,
986                                             &_bbt_time.beats,
987                                             &_bbt_time.ticks) != 3) {
988                                         _positional_lock_style = AudioTime;
989                                 }
990                         }
991                 }
992                         
993         } else {
994                 _positional_lock_style = AudioTime;
995         }
996
997         /* XXX FIRST EDIT !!! */
998         
999         /* these 3 properties never change as a result of any editing */
1000
1001         if ((prop = node.property ("ancestral-start")) != 0) {
1002                 _ancestral_start = atoi (prop->value());
1003         } else {
1004                 _ancestral_start = _start;
1005         }
1006
1007         if ((prop = node.property ("ancestral-length")) != 0) {
1008                 _ancestral_length = atoi (prop->value());
1009         } else {
1010                 _ancestral_length = _length;
1011         }
1012
1013         if ((prop = node.property ("stretch")) != 0) {
1014                 _stretch = atof (prop->value());
1015         } else {
1016                 _stretch = 1.0;
1017         }
1018
1019         if ((prop = node.property ("shift")) != 0) {
1020                 _shift = atof (prop->value());
1021         } else {
1022                 _shift = 1.0;
1023         }
1024
1025         /* note: derived classes set flags */
1026
1027         if (_extra_xml) {
1028                 delete _extra_xml;
1029                 _extra_xml = 0;
1030         }
1031
1032         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1033                 
1034                 XMLNode *child;
1035                 
1036                 child = (*niter);
1037                 
1038                 if (child->name () == "extra") {
1039                         _extra_xml = new XMLNode (*child);
1040                         break;
1041                 }
1042         }
1043
1044         if (send) {
1045                 send_change (what_changed);
1046         }
1047
1048         return 0;
1049 }
1050
1051 int
1052 Region::set_state (const XMLNode& node)
1053 {
1054         const XMLProperty *prop;
1055         Change what_changed = Change (0);
1056
1057         /* ID is not allowed to change, ever */
1058
1059         if ((prop = node.property ("id")) == 0) {
1060                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1061                 return -1;
1062         }
1063
1064         _id = prop->value();
1065         
1066         _first_edit = EditChangesNothing;
1067         
1068         set_live_state (node, what_changed, true);
1069
1070         return 0;
1071 }
1072
1073 void
1074 Region::freeze ()
1075 {
1076         _frozen++;
1077         _last_length = _length;
1078         _last_position = _position;
1079 }
1080
1081 void
1082 Region::thaw (const string& why)
1083 {
1084         Change what_changed = Change (0);
1085
1086         {
1087                 Glib::Mutex::Lock lm (lock);
1088
1089                 if (_frozen && --_frozen > 0) {
1090                         return;
1091                 }
1092
1093                 if (pending_changed) {
1094                         what_changed = pending_changed;
1095                         pending_changed = Change (0);
1096                 }
1097         }
1098
1099         if (what_changed == Change (0)) {
1100                 return;
1101         }
1102
1103         if (what_changed & LengthChanged) {
1104                 if (what_changed & PositionChanged) {
1105                         recompute_at_start ();
1106                 } 
1107                 recompute_at_end ();
1108         }
1109                 
1110         StateChanged (what_changed);
1111 }
1112
1113 void
1114 Region::send_change (Change what_changed)
1115 {
1116         {
1117                 Glib::Mutex::Lock lm (lock);
1118                 if (_frozen) {
1119                         pending_changed = Change (pending_changed|what_changed);
1120                         return;
1121                 } 
1122         }
1123
1124         StateChanged (what_changed);
1125 }
1126
1127 void
1128 Region::set_last_layer_op (uint64_t when)
1129 {
1130         _last_layer_op = when;
1131 }
1132
1133 bool
1134 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1135 {
1136         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1137 }
1138
1139 bool
1140 Region::equivalent (boost::shared_ptr<const Region> other) const
1141 {
1142         return _start == other->_start &&
1143                 _position == other->_position &&
1144                 _length == other->_length;
1145 }
1146
1147 bool
1148 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1149 {
1150         return _start == other->_start &&
1151                 _length == other->_length;
1152 }
1153
1154 bool
1155 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1156 {
1157         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1158 }
1159
1160 void
1161 Region::invalidate_transients ()
1162 {
1163         valid_transients = false;
1164         _transients.clear ();
1165 }
1166