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