add control port to ardour.rc.in and fix crash bug caused by bad shared_ptr<Region...
[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 (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         _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, nframes_t offset, 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 (nframes_t len, void *src)
234 {
235         if (_flags & Locked) {
236                 return;
237         }
238
239         if (_length != len && len != 0) {
240
241                 /* check that the current _position wouldn't make the new 
242                    length impossible.
243                 */
244
245                 if (max_frames - len < _position) {
246                         return;
247                 }
248
249                 if (!verify_length (len)) {
250                         return;
251                 }
252                 
253                 _length = len;
254
255                 _flags = Region::Flag (_flags & ~WholeFile);
256
257                 first_edit ();
258                 maybe_uncopy ();
259
260                 if (!_frozen) {
261                         recompute_at_end ();
262
263                         char buf[64];
264                         snprintf (buf, sizeof (buf), "length set to %u", len);
265                         save_state (buf);
266                 }
267
268                 send_change (LengthChanged);
269         }
270 }
271
272 void
273 Region::maybe_uncopy ()
274 {
275 }
276
277 void
278 Region::first_edit ()
279 {
280         if (_first_edit != EditChangesNothing && _playlist) {
281
282                 _name = _playlist->session().new_region_name (_name);
283                 _first_edit = EditChangesNothing;
284
285                 send_change (NameChanged);
286                 RegionFactory::CheckNewRegion (shared_from_this());
287         }
288 }
289
290 void
291 Region::move_to_natural_position (void *src)
292 {
293         if (!_playlist) {
294                 return;
295         }
296         
297         boost::shared_ptr<Region> whole_file_region = get_parent();
298
299         if (whole_file_region) {
300                 set_position (whole_file_region->position() + _start, src);
301         }
302 }
303         
304 void
305 Region::special_set_position (nframes_t pos)
306 {
307         /* this is used when creating a whole file region as 
308            a way to store its "natural" or "captured" position.
309         */
310
311         _position = pos;
312 }
313
314 void
315 Region::set_position (nframes_t pos, void *src)
316 {
317         if (_flags & Locked) {
318                 return;
319         }
320
321         if (_position != pos) {
322                 _position = pos;
323
324                 /* check that the new _position wouldn't make the current
325                    length impossible - if so, change the length. 
326
327                    XXX is this the right thing to do?
328                 */
329
330                 if (max_frames - _length < _position) {
331                         _length = max_frames - _position;
332                 }
333
334                 if (!_frozen) {
335                         char buf[64];
336                         snprintf (buf, sizeof (buf), "position set to %u", pos);
337                         save_state (buf);
338                 }
339         }
340
341         /* do this even if the position is the same. this helps out
342            a GUI that has moved its representation already.
343         */
344
345         send_change (PositionChanged);
346 }
347
348 void
349 Region::set_position_on_top (nframes_t pos, void *src)
350 {
351         if (_flags & Locked) {
352                 return;
353         }
354
355         if (_position != pos) {
356                 _position = pos;
357
358                 if (!_frozen) {
359                         char buf[64];
360                         snprintf (buf, sizeof (buf), "position set to %u", pos);
361                         save_state (buf);
362                 }
363         }
364
365         _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
366
367         /* do this even if the position is the same. this helps out
368            a GUI that has moved its representation already.
369         */
370         
371         send_change (PositionChanged);
372 }
373
374 void
375 Region::nudge_position (long n, void *src)
376 {
377         if (_flags & Locked) {
378                 return;
379         }
380
381         if (n == 0) {
382                 return;
383         }
384         
385         if (n > 0) {
386                 if (_position > max_frames - n) {
387                         _position = max_frames;
388                 } else {
389                         _position += n;
390                 }
391         } else {
392                 if (_position < (nframes_t) -n) {
393                         _position = 0;
394                 } else {
395                         _position += n;
396                 }
397         }
398
399         if (!_frozen) {
400                 char buf[64];
401                 snprintf (buf, sizeof (buf), "position set to %u", _position);
402                 save_state (buf);
403         }
404
405         send_change (PositionChanged);
406 }
407
408 void
409 Region::set_start (nframes_t pos, void *src)
410 {
411         if (_flags & Locked) {
412                 return;
413         }
414         /* This just sets the start, nothing else. It effectively shifts
415            the contents of the Region within the overall extent of the Source,
416            without changing the Region's position or length
417         */
418
419         if (_start != pos) {
420
421                 if (!verify_start (pos)) {
422                         return;
423                 }
424
425                 _start = pos;
426                 _flags = Region::Flag (_flags & ~WholeFile);
427                 first_edit ();
428
429                 if (!_frozen) {
430                         char buf[64];
431                         snprintf (buf, sizeof (buf), "start set to %u", pos);
432                         save_state (buf);
433                 }
434
435                 send_change (StartChanged);
436         }
437 }
438
439 void
440 Region::trim_start (nframes_t new_position, void *src)
441 {
442         if (_flags & Locked) {
443                 return;
444         }
445         nframes_t new_start;
446         int32_t start_shift;
447         
448         if (new_position > _position) {
449                 start_shift = new_position - _position;
450         } else {
451                 start_shift = -(_position - new_position);
452         }
453
454         if (start_shift > 0) {
455
456                 if (_start > max_frames - start_shift) {
457                         new_start = max_frames;
458                 } else {
459                         new_start = _start + start_shift;
460                 }
461
462                 if (!verify_start (new_start)) {
463                         return;
464                 }
465
466         } else if (start_shift < 0) {
467
468                 if (_start < (nframes_t) -start_shift) {
469                         new_start = 0;
470                 } else {
471                         new_start = _start + start_shift;
472                 }
473         } else {
474                 return;
475         }
476
477         if (new_start == _start) {
478                 return;
479         }
480         
481         _start = new_start;
482         _flags = Region::Flag (_flags & ~WholeFile);
483         first_edit ();
484
485         if (!_frozen) {
486                 char buf[64];
487                 snprintf (buf, sizeof (buf), "slipped start to %u", _start);
488                 save_state (buf);
489         }
490
491         send_change (StartChanged);
492 }
493
494 void
495 Region::trim_front (nframes_t new_position, void *src)
496 {
497         if (_flags & Locked) {
498                 return;
499         }
500
501         nframes_t end = last_frame();
502         nframes_t source_zero;
503
504         if (_position > _start) {
505                 source_zero = _position - _start;
506         } else {
507                 source_zero = 0; // its actually negative, but this will work for us
508         }
509
510         if (new_position < end) { /* can't trim it zero or negative length */
511                 
512                 nframes_t newlen;
513
514                 /* can't trim it back passed where source position zero is located */
515                 
516                 new_position = max (new_position, source_zero);
517                 
518                 
519                 if (new_position > _position) {
520                         newlen = _length - (new_position - _position);
521                 } else {
522                         newlen = _length + (_position - new_position);
523                 }
524                 
525                 trim_to_internal (new_position, newlen, src);
526                 if (!_frozen) {
527                         recompute_at_start ();
528                 }
529         }
530 }
531
532 void
533 Region::trim_end (nframes_t new_endpoint, void *src)
534 {
535         if (_flags & Locked) {
536                 return;
537         }
538
539         if (new_endpoint > _position) {
540                 trim_to_internal (_position, new_endpoint - _position, this);
541                 if (!_frozen) {
542                         recompute_at_end ();
543                 }
544         }
545 }
546
547 void
548 Region::trim_to (nframes_t position, nframes_t length, void *src)
549 {
550         if (_flags & Locked) {
551                 return;
552         }
553
554         trim_to_internal (position, length, src);
555
556         if (!_frozen) {
557                 recompute_at_start ();
558                 recompute_at_end ();
559         }
560 }
561
562 void
563 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
564 {
565         int32_t start_shift;
566         nframes_t new_start;
567
568         if (_flags & Locked) {
569                 return;
570         }
571
572         if (position > _position) {
573                 start_shift = position - _position;
574         } else {
575                 start_shift = -(_position - position);
576         }
577
578         if (start_shift > 0) {
579
580                 if (_start > max_frames - start_shift) {
581                         new_start = max_frames;
582                 } else {
583                         new_start = _start + start_shift;
584                 }
585
586
587         } else if (start_shift < 0) {
588
589                 if (_start < (nframes_t) -start_shift) {
590                         new_start = 0;
591                 } else {
592                         new_start = _start + start_shift;
593                 }
594         } else {
595                 new_start = _start;
596         }
597
598         if (!verify_start_and_length (new_start, length)) {
599                 return;
600         }
601
602         Change what_changed = Change (0);
603
604         if (_start != new_start) {
605                 _start = new_start;
606                 what_changed = Change (what_changed|StartChanged);
607         }
608         if (_length != length) {
609                 _length = length;
610                 what_changed = Change (what_changed|LengthChanged);
611         }
612         if (_position != position) {
613                 _position = position;
614                 what_changed = Change (what_changed|PositionChanged);
615         }
616         
617         _flags = Region::Flag (_flags & ~WholeFile);
618
619         if (what_changed & (StartChanged|LengthChanged)) {
620                 first_edit ();
621         } 
622
623         if (what_changed) {
624                 
625                 if (!_frozen) {
626                         char buf[64];
627                         snprintf (buf, sizeof (buf), "trimmed to %u-%u", _position, _position+_length-1);
628                         save_state (buf);
629                 }
630
631                 send_change (what_changed);
632         }
633 }       
634
635 void
636 Region::set_hidden (bool yn)
637 {
638         if (hidden() != yn) {
639
640                 if (yn) {
641                         _flags = Flag (_flags|Hidden);
642                 } else {
643                         _flags = Flag (_flags & ~Hidden);
644                 }
645
646                 send_change (HiddenChanged);
647         }
648 }
649
650 void
651 Region::set_muted (bool yn)
652 {
653         if (muted() != yn) {
654
655                 if (yn) {
656                         _flags = Flag (_flags|Muted);
657                 } else {
658                         _flags = Flag (_flags & ~Muted);
659                 }
660
661                 if (!_frozen) {
662                         char buf[64];
663                         if (yn) {
664                                 snprintf (buf, sizeof (buf), "muted");
665                         } else {
666                                 snprintf (buf, sizeof (buf), "unmuted");
667                         }
668                         save_state (buf);
669                 }
670
671                 send_change (MuteChanged);
672         }
673 }
674
675 void
676 Region::set_opaque (bool yn)
677 {
678         if (opaque() != yn) {
679                 if (!_frozen) {
680                         char buf[64];
681                         if (yn) {
682                                 snprintf (buf, sizeof (buf), "opaque");
683                                 _flags = Flag (_flags|Opaque);
684                         } else {
685                                 snprintf (buf, sizeof (buf), "translucent");
686                                 _flags = Flag (_flags & ~Opaque);
687                         }
688                         save_state (buf);
689                 }
690                 send_change (OpacityChanged);
691         }
692 }
693
694 void
695 Region::set_locked (bool yn)
696 {
697         if (locked() != yn) {
698                 if (!_frozen) {
699                         char buf[64];
700                         if (yn) {
701                                 snprintf (buf, sizeof (buf), "locked");
702                                 _flags = Flag (_flags|Locked);
703                         } else {
704                                 snprintf (buf, sizeof (buf), "unlocked");
705                                 _flags = Flag (_flags & ~Locked);
706                         }
707                         save_state (buf);
708                 }
709                 send_change (LockChanged);
710         }
711 }
712
713 void
714 Region::set_sync_position (nframes_t absolute_pos)
715 {
716         nframes_t file_pos;
717
718         file_pos = _start + (absolute_pos - _position);
719
720         if (file_pos != _sync_position) {
721                 
722                 _sync_position = file_pos;
723                 _flags = Flag (_flags|SyncMarked);
724
725                 if (!_frozen) {
726                         char buf[64];
727                         maybe_uncopy ();
728                         snprintf (buf, sizeof (buf), "sync point set to %u", _sync_position);
729                         save_state (buf);
730                 }
731                 send_change (SyncOffsetChanged);
732         }
733 }
734
735 void
736 Region::clear_sync_position ()
737 {
738         if (_flags & SyncMarked) {
739                 _flags = Flag (_flags & ~SyncMarked);
740
741                 if (!_frozen) {
742                         maybe_uncopy ();
743                         save_state ("sync point removed");
744                 }
745                 send_change (SyncOffsetChanged);
746         }
747 }
748
749 nframes_t
750 Region::sync_offset (int& dir) const
751 {
752         /* returns the sync point relative the first frame of the region */
753
754         if (_flags & SyncMarked) {
755                 if (_sync_position > _start) {
756                         dir = 1;
757                         return _sync_position - _start; 
758                 } else {
759                         dir = -1;
760                         return _start - _sync_position;
761                 }
762         } else {
763                 dir = 0;
764                 return 0;
765         }
766 }
767
768 nframes_t 
769 Region::adjust_to_sync (nframes_t pos)
770 {
771         int sync_dir;
772         nframes_t offset = sync_offset (sync_dir);
773         
774         if (sync_dir > 0) {
775                 if (max_frames - pos > offset) {
776                         pos += offset;
777                 }
778         } else {
779                 if (pos > offset) {
780                         pos -= offset;
781                 } else {
782                         pos = 0;
783                 }
784         }
785
786         return pos;
787 }
788
789 nframes_t
790 Region::sync_position() const
791 {
792         if (_flags & SyncMarked) {
793                 return _sync_position; 
794         } else {
795                 return _start;
796         }
797 }
798
799
800 void
801 Region::raise ()
802 {
803         if (_playlist == 0) {
804                 return;
805         }
806
807         _playlist->raise_region (boost::shared_ptr<Region>(this));
808 }
809
810 void
811 Region::lower ()
812 {
813         if (_playlist == 0) {
814                 return;
815         }
816
817         _playlist->lower_region (boost::shared_ptr<Region>(this));
818 }
819
820 void
821 Region::raise_to_top ()
822 {
823
824         if (_playlist == 0) {
825                 return;
826         }
827
828         _playlist->raise_region_to_top (boost::shared_ptr<Region>(this));
829 }
830
831 void
832 Region::lower_to_bottom ()
833 {
834         if (_playlist == 0) {
835                 return;
836         }
837
838         _playlist->lower_region_to_bottom (boost::shared_ptr<Region>(this));
839 }
840
841 void
842 Region::set_layer (layer_t l)
843 {
844         if (_layer != l) {
845                 _layer = l;
846                 
847                 if (!_frozen) {
848                         char buf[64];
849                         snprintf (buf, sizeof (buf), "layer set to %" PRIu32, _layer);
850                         save_state (buf);
851                 }
852                 
853                 send_change (LayerChanged);
854         }
855 }
856
857 XMLNode&
858 Region::state (bool full_state)
859 {
860         XMLNode *node = new XMLNode ("Region");
861         char buf[64];
862         
863         _id.print (buf, sizeof (buf));
864         node->add_property ("id", buf);
865         node->add_property ("name", _name);
866         snprintf (buf, sizeof (buf), "%u", _start);
867         node->add_property ("start", buf);
868         snprintf (buf, sizeof (buf), "%u", _length);
869         node->add_property ("length", buf);
870         snprintf (buf, sizeof (buf), "%u", _position);
871         node->add_property ("position", buf);
872
873         /* note: flags are stored by derived classes */
874
875         snprintf (buf, sizeof (buf), "%d", (int) _layer);
876         node->add_property ("layer", buf);
877         snprintf (buf, sizeof (buf), "%u", _sync_position);
878         node->add_property ("sync-position", buf);
879
880         return *node;
881 }
882
883 XMLNode&
884 Region::get_state ()
885 {
886         return state (true);
887 }
888
889 int
890 Region::set_state (const XMLNode& node)
891 {
892         const XMLNodeList& nlist = node.children();
893         const XMLProperty *prop;
894
895         if (_extra_xml) {
896                 delete _extra_xml;
897                 _extra_xml = 0;
898         }
899
900         if ((prop = node.property ("id")) == 0) {
901                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
902                 return -1;
903         }
904
905         _id = prop->value();
906
907         if ((prop = node.property ("name")) == 0) {
908                 error << _("Session: XMLNode describing a Region is incomplete (no name)") << endmsg;
909                 return -1;
910         }
911
912         _name = prop->value();
913
914         if ((prop = node.property ("start")) != 0) {
915                 sscanf (prop->value().c_str(), "%" PRIu32, &_start);
916         }
917
918         if ((prop = node.property ("length")) != 0) {
919                 sscanf (prop->value().c_str(), "%" PRIu32, &_length);
920         }
921
922         if ((prop = node.property ("position")) != 0) {
923                 sscanf (prop->value().c_str(), "%" PRIu32, &_position);
924         }
925
926         if ((prop = node.property ("layer")) != 0) {
927                 _layer = (layer_t) atoi (prop->value().c_str());
928         }
929
930         /* note: derived classes set flags */
931
932         if ((prop = node.property ("sync-position")) != 0) {
933                 sscanf (prop->value().c_str(), "%" PRIu32, &_sync_position);
934         } else {
935                 _sync_position = _start;
936         }
937         
938         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
939                 
940                 XMLNode *child;
941                 
942                 child = (*niter);
943                 
944                 if (child->name () == "extra") {
945                         _extra_xml = new XMLNode (*child);
946                         break;
947                 }
948         }
949
950         _first_edit = EditChangesNothing;
951
952         return 0;
953 }
954
955 void
956 Region::freeze ()
957 {
958         _frozen++;
959 }
960
961 void
962 Region::thaw (const string& why)
963 {
964         Change what_changed = Change (0);
965
966         {
967                 Glib::Mutex::Lock lm (lock);
968
969                 if (_frozen && --_frozen > 0) {
970                         return;
971                 }
972
973                 if (pending_changed) {
974                         what_changed = pending_changed;
975                         pending_changed = Change (0);
976                 }
977         }
978
979         if (what_changed == Change (0)) {
980                 return;
981         }
982
983         if (what_changed & LengthChanged) {
984                 if (what_changed & PositionChanged) {
985                         recompute_at_start ();
986                 } 
987                 recompute_at_end ();
988         }
989                 
990         save_state (why);
991         StateChanged (what_changed);
992 }
993
994 void
995 Region::send_change (Change what_changed)
996 {
997         {
998                 Glib::Mutex::Lock lm (lock);
999                 if (_frozen) {
1000                         pending_changed = Change (pending_changed|what_changed);
1001                         return;
1002                 } 
1003         }
1004
1005         StateManager::send_state_changed (what_changed);
1006 }
1007
1008 void
1009 Region::set_last_layer_op (uint64_t when)
1010 {
1011         _last_layer_op = when;
1012 }
1013
1014 bool
1015 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1016 {
1017         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1018 }
1019
1020 bool
1021 Region::equivalent (boost::shared_ptr<const Region> other) const
1022 {
1023         return _start == other->_start &&
1024                 _position == other->_position &&
1025                 _length == other->_length;
1026 }
1027
1028 bool
1029 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1030 {
1031         return _start == other->_start &&
1032                 _length == other->_length;
1033 }
1034
1035 bool
1036 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1037 {
1038         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1039 }