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