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