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