Merged with trunk R1705.
[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, jack_nframes_t start, jack_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, jack_nframes_t start, jack_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|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))
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 (_flags & Locked) {
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) {
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) {
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_sync_position (nframes_t absolute_pos)
761 {
762         nframes_t file_pos;
763
764         file_pos = _start + (absolute_pos - _position);
765
766         if (file_pos != _sync_position) {
767                 
768                 _sync_position = file_pos;
769                 _flags = Flag (_flags|SyncMarked);
770
771                 if (!_frozen) {
772                         maybe_uncopy ();
773                 }
774                 send_change (SyncOffsetChanged);
775         }
776 }
777
778 void
779 Region::clear_sync_position ()
780 {
781         if (_flags & SyncMarked) {
782                 _flags = Flag (_flags & ~SyncMarked);
783
784                 if (!_frozen) {
785                         maybe_uncopy ();
786                 }
787                 send_change (SyncOffsetChanged);
788         }
789 }
790
791 nframes_t
792 Region::sync_offset (int& dir) const
793 {
794         /* returns the sync point relative the first frame of the region */
795
796         if (_flags & SyncMarked) {
797                 if (_sync_position > _start) {
798                         dir = 1;
799                         return _sync_position - _start; 
800                 } else {
801                         dir = -1;
802                         return _start - _sync_position;
803                 }
804         } else {
805                 dir = 0;
806                 return 0;
807         }
808 }
809
810 nframes_t 
811 Region::adjust_to_sync (nframes_t pos)
812 {
813         int sync_dir;
814         nframes_t offset = sync_offset (sync_dir);
815         
816         if (sync_dir > 0) {
817                 if (max_frames - pos > offset) {
818                         pos += offset;
819                 }
820         } else {
821                 if (pos > offset) {
822                         pos -= offset;
823                 } else {
824                         pos = 0;
825                 }
826         }
827
828         return pos;
829 }
830
831 nframes_t
832 Region::sync_position() const
833 {
834         if (_flags & SyncMarked) {
835                 return _sync_position; 
836         } else {
837                 return _start;
838         }
839 }
840
841
842 void
843 Region::raise ()
844 {
845         boost::shared_ptr<Playlist> pl (playlist());
846         if (pl) {
847                 pl->raise_region (shared_from_this ());
848         }
849 }
850
851 void
852 Region::lower ()
853 {
854         boost::shared_ptr<Playlist> pl (playlist());
855         if (pl) {
856                 pl->lower_region (shared_from_this ());
857         }
858 }
859
860 void
861 Region::raise_to_top ()
862 {
863         boost::shared_ptr<Playlist> pl (playlist());
864         if (pl) {
865                 pl->raise_region_to_top (shared_from_this());
866         }
867 }
868
869 void
870 Region::lower_to_bottom ()
871 {
872         boost::shared_ptr<Playlist> pl (playlist());
873         if (pl) {
874                 pl->lower_region_to_bottom (shared_from_this());
875         }
876 }
877
878 void
879 Region::set_layer (layer_t l)
880 {
881         if (_layer != l) {
882                 _layer = l;
883                 
884                 send_change (LayerChanged);
885         }
886 }
887
888 XMLNode&
889 Region::state (bool full_state)
890 {
891         XMLNode *node = new XMLNode ("Region");
892         char buf[64];
893         char* fe = NULL;
894
895         _id.print (buf, sizeof (buf));
896         node->add_property ("id", buf);
897         node->add_property ("name", _name);
898         node->add_property ("type", _type.to_string());
899         snprintf (buf, sizeof (buf), "%u", _start);
900         node->add_property ("start", buf);
901         snprintf (buf, sizeof (buf), "%u", _length);
902         node->add_property ("length", buf);
903         snprintf (buf, sizeof (buf), "%u", _position);
904         node->add_property ("position", buf);
905         
906         switch (_first_edit) {
907         case EditChangesNothing:
908                 fe = X_("nothing");
909                 break;
910         case EditChangesName:
911                 fe = X_("name");
912                 break;
913         case EditChangesID:
914                 fe = X_("id");
915                 break;
916         default: /* should be unreachable but makes g++ happy */
917                 cerr << "Odd region property found\n";
918                 fe = X_("nothing");
919                 break;
920         }
921
922         node->add_property ("first_edit", fe);
923
924         /* note: flags are stored by derived classes */
925
926         snprintf (buf, sizeof (buf), "%d", (int) _layer);
927         node->add_property ("layer", buf);
928         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
929         node->add_property ("sync-position", buf);
930
931         return *node;
932 }
933
934 XMLNode&
935 Region::get_state ()
936 {
937         return state (true);
938 }
939
940 int
941 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
942 {
943         const XMLNodeList& nlist = node.children();
944         const XMLProperty *prop;
945         nframes_t val;
946
947         /* this is responsible for setting those aspects of Region state 
948            that are mutable after construction.
949         */
950
951         if ((prop = node.property ("name")) == 0) {
952                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
953                 return -1;
954         }
955
956         _name = prop->value();
957         
958         if ((prop = node.property ("type")) == 0) {
959                 _type = DataType::AUDIO;
960         } else {
961                 _type = DataType(prop->value());
962         }
963
964         if ((prop = node.property ("start")) != 0) {
965                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
966                 if (val != _start) {
967                         what_changed = Change (what_changed|StartChanged);      
968                         _start = val;
969                 }
970         } else {
971                 _start = 0;
972         }
973
974         if ((prop = node.property ("length")) != 0) {
975                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
976                 if (val != _length) {
977                         what_changed = Change (what_changed|LengthChanged);
978                         _length = val;
979                 }
980         } else {
981                 _length = 1;
982         }
983
984         if ((prop = node.property ("position")) != 0) {
985                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
986                 if (val != _position) {
987                         what_changed = Change (what_changed|PositionChanged);
988                         _position = val;
989                 }
990         } else {
991                 _position = 0;
992         }
993
994         if ((prop = node.property ("layer")) != 0) {
995                 layer_t x;
996                 x = (layer_t) atoi (prop->value().c_str());
997                 if (x != _layer) {
998                         what_changed = Change (what_changed|LayerChanged);
999                         _layer = x;
1000                 }
1001         } else {
1002                 _layer = 0;
1003         }
1004
1005         if ((prop = node.property ("sync-position")) != 0) {
1006                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1007                 if (val != _sync_position) {
1008                         what_changed = Change (what_changed|SyncOffsetChanged);
1009                         _sync_position = val;
1010                 }
1011         } else {
1012                 _sync_position = _start;
1013         }
1014
1015         /* XXX FIRST EDIT !!! */
1016         
1017         /* note: derived classes set flags */
1018
1019         if (_extra_xml) {
1020                 delete _extra_xml;
1021                 _extra_xml = 0;
1022         }
1023
1024         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1025                 
1026                 XMLNode *child;
1027                 
1028                 child = (*niter);
1029                 
1030                 if (child->name () == "extra") {
1031                         _extra_xml = new XMLNode (*child);
1032                         break;
1033                 }
1034         }
1035
1036         if (send) {
1037                 send_change (what_changed);
1038         }
1039
1040         return 0;
1041 }
1042
1043 int
1044 Region::set_state (const XMLNode& node)
1045 {
1046         const XMLProperty *prop;
1047         Change what_changed = Change (0);
1048
1049         /* ID is not allowed to change, ever */
1050
1051         if ((prop = node.property ("id")) == 0) {
1052                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1053                 return -1;
1054         }
1055
1056         _id = prop->value();
1057         
1058         _first_edit = EditChangesNothing;
1059         
1060         set_live_state (node, what_changed, true);
1061
1062         return 0;
1063 }
1064
1065 void
1066 Region::freeze ()
1067 {
1068         _frozen++;
1069 }
1070
1071 void
1072 Region::thaw (const string& why)
1073 {
1074         Change what_changed = Change (0);
1075
1076         {
1077                 Glib::Mutex::Lock lm (_lock);
1078
1079                 if (_frozen && --_frozen > 0) {
1080                         return;
1081                 }
1082
1083                 if (_pending_changed) {
1084                         what_changed = _pending_changed;
1085                         _pending_changed = Change (0);
1086                 }
1087         }
1088
1089         if (what_changed == Change (0)) {
1090                 return;
1091         }
1092
1093         if (what_changed & LengthChanged) {
1094                 if (what_changed & PositionChanged) {
1095                         recompute_at_start ();
1096                 } 
1097                 recompute_at_end ();
1098         }
1099                 
1100         StateChanged (what_changed);
1101 }
1102
1103 void
1104 Region::send_change (Change what_changed)
1105 {
1106         {
1107                 Glib::Mutex::Lock lm (_lock);
1108                 if (_frozen) {
1109                         _pending_changed = Change (_pending_changed|what_changed);
1110                         return;
1111                 } 
1112         }
1113
1114         StateChanged (what_changed);
1115 }
1116
1117 void
1118 Region::set_last_layer_op (uint64_t when)
1119 {
1120         _last_layer_op = when;
1121 }
1122
1123 bool
1124 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1125 {
1126         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1127 }
1128
1129 bool
1130 Region::equivalent (boost::shared_ptr<const Region> other) const
1131 {
1132         return _start == other->_start &&
1133                 _position == other->_position &&
1134                 _length == other->_length;
1135 }
1136
1137 bool
1138 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1139 {
1140         return _start == other->_start &&
1141                 _length == other->_length;
1142 }
1143
1144 bool
1145 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1146 {
1147         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1148 }
1149
1150 void
1151 Region::source_deleted (boost::shared_ptr<Source>)
1152 {
1153         delete this;
1154 }
1155
1156 vector<string>
1157 Region::master_source_names ()
1158 {
1159         SourceList::iterator i;
1160
1161         vector<string> names;
1162         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1163                 names.push_back((*i)->name());
1164         }
1165
1166         return names;
1167 }
1168
1169 bool
1170 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1171 {
1172         if (!other)
1173                 return false;
1174
1175         SourceList::const_iterator i;
1176         SourceList::const_iterator io;
1177
1178         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1179                 if ((*i)->id() != (*io)->id()) {
1180                         return false;
1181                 }
1182         }
1183
1184         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1185                 if ((*i)->id() != (*io)->id()) {
1186                         return false;
1187                 }
1188         }
1189
1190         return true;
1191 }
1192
1193 bool
1194 Region::verify_length (jack_nframes_t len)
1195 {
1196         for (uint32_t n=0; n < _sources.size(); ++n) {
1197                 if (_start > _sources[n]->length() - len) {
1198                         return false;
1199                 }
1200         }
1201         return true;
1202 }
1203
1204 bool
1205 Region::verify_start_and_length (jack_nframes_t new_start, jack_nframes_t new_length)
1206 {
1207         for (uint32_t n=0; n < _sources.size(); ++n) {
1208                 if (new_length > _sources[n]->length() - new_start) {
1209                         return false;
1210                 }
1211         }
1212         return true;
1213 }
1214 bool
1215 Region::verify_start (jack_nframes_t pos)
1216 {
1217         for (uint32_t n=0; n < _sources.size(); ++n) {
1218                 if (pos > _sources[n]->length() - _length) {
1219                         return false;
1220                 }
1221         }
1222         return true;
1223 }
1224
1225 bool
1226 Region::verify_start_mutable (jack_nframes_t& new_start)
1227 {
1228         for (uint32_t n=0; n < _sources.size(); ++n) {
1229                 if (new_start > _sources[n]->length() - _length) {
1230                         new_start = _sources[n]->length() - _length;
1231                 }
1232         }
1233         return true;
1234 }
1235
1236 boost::shared_ptr<Region>
1237 Region::get_parent() const
1238 {
1239         boost::shared_ptr<Playlist> pl (playlist());
1240
1241         if (pl) {
1242                 boost::shared_ptr<Region> r;
1243                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1244                 
1245                 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1246                         return boost::static_pointer_cast<Region> (r);
1247                 }
1248         }
1249         
1250         return boost::shared_ptr<Region>();
1251 }
1252