Use sys::path and SessionDirectory in Session::create_session_file_from_template...
[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 (nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
56         : _name(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         : _name(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         : _name(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         : _name(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         : _name(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         : _name(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         : _name(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 void
327 Region::set_name (string str)
328 {
329         if (_name != str) {
330                 _name = str; 
331                 send_change (NameChanged);
332         }
333 }
334
335 void
336 Region::set_length (nframes_t len, void *src)
337 {
338         if (_flags & Locked) {
339                 return;
340         }
341
342         if (_length != len && len != 0) {
343
344                 /* check that the current _position wouldn't make the new 
345                    length impossible.
346                 */
347
348                 if (max_frames - len < _position) {
349                         return;
350                 }
351
352                 if (!verify_length (len)) {
353                         return;
354                 }
355                 
356                 _length = len;
357
358                 _flags = Region::Flag (_flags & ~WholeFile);
359
360                 first_edit ();
361                 maybe_uncopy ();
362
363                 if (!_frozen) {
364                         recompute_at_end ();
365                 }
366
367                 send_change (LengthChanged);
368         }
369 }
370
371 void
372 Region::maybe_uncopy ()
373 {
374 }
375
376 void
377 Region::first_edit ()
378 {
379         boost::shared_ptr<Playlist> pl (playlist());
380
381         if (_first_edit != EditChangesNothing && pl) {
382
383                 _name = pl->session().new_region_name (_name);
384                 _first_edit = EditChangesNothing;
385
386                 send_change (NameChanged);
387                 RegionFactory::CheckNewRegion (shared_from_this());
388         }
389 }
390
391 bool
392 Region::at_natural_position () const
393 {
394         boost::shared_ptr<Playlist> pl (playlist());
395
396         if (!pl) {
397                 return false;
398         }
399         
400         boost::shared_ptr<Region> whole_file_region = get_parent();
401
402         if (whole_file_region) {
403                 if (_position == whole_file_region->position() + _start) {
404                         return true;
405                 }
406         }
407
408         return false;
409 }
410
411 void
412 Region::move_to_natural_position (void *src)
413 {
414         boost::shared_ptr<Playlist> pl (playlist());
415
416         if (!pl) {
417                 return;
418         }
419         
420         boost::shared_ptr<Region> whole_file_region = get_parent();
421
422         if (whole_file_region) {
423                 set_position (whole_file_region->position() + _start, src);
424         }
425 }
426         
427 void
428 Region::special_set_position (nframes_t pos)
429 {
430         /* this is used when creating a whole file region as 
431            a way to store its "natural" or "captured" position.
432         */
433
434         _position = pos;
435 }
436
437 void
438 Region::set_position (nframes_t pos, void *src)
439 {
440         if (!can_move()) {
441                 return;
442         }
443
444         if (_position != pos) {
445                 _position = pos;
446
447                 /* check that the new _position wouldn't make the current
448                    length impossible - if so, change the length. 
449
450                    XXX is this the right thing to do?
451                 */
452
453                 if (max_frames - _length < _position) {
454                         _length = max_frames - _position;
455                 }
456         }
457
458         /* do this even if the position is the same. this helps out
459            a GUI that has moved its representation already.
460         */
461
462         send_change (PositionChanged);
463 }
464
465 void
466 Region::set_position_on_top (nframes_t pos, void *src)
467 {
468         if (_flags & Locked) {
469                 return;
470         }
471
472         if (_position != pos) {
473                 _position = pos;
474         }
475
476         boost::shared_ptr<Playlist> pl (playlist());
477
478         if (pl) {
479                 pl->raise_region_to_top (shared_from_this ());
480         }
481
482         /* do this even if the position is the same. this helps out
483            a GUI that has moved its representation already.
484         */
485         
486         send_change (PositionChanged);
487 }
488
489 void
490 Region::nudge_position (long n, void *src)
491 {
492         if (_flags & Locked) {
493                 return;
494         }
495
496         if (n == 0) {
497                 return;
498         }
499         
500         if (n > 0) {
501                 if (_position > max_frames - n) {
502                         _position = max_frames;
503                 } else {
504                         _position += n;
505                 }
506         } else {
507                 if (_position < (nframes_t) -n) {
508                         _position = 0;
509                 } else {
510                         _position += n;
511                 }
512         }
513
514         send_change (PositionChanged);
515 }
516
517 void
518 Region::set_start (nframes_t pos, void *src)
519 {
520         if (_flags & (Locked|PositionLocked)) {
521                 return;
522         }
523         /* This just sets the start, nothing else. It effectively shifts
524            the contents of the Region within the overall extent of the Source,
525            without changing the Region's position or length
526         */
527
528         if (_start != pos) {
529
530                 if (!verify_start (pos)) {
531                         return;
532                 }
533
534                 _start = pos;
535                 _flags = Region::Flag (_flags & ~WholeFile);
536                 first_edit ();
537
538                 send_change (StartChanged);
539         }
540 }
541
542 void
543 Region::trim_start (nframes_t new_position, void *src)
544 {
545         if (_flags & (Locked|PositionLocked)) {
546                 return;
547         }
548         nframes_t new_start;
549         int32_t start_shift;
550         
551         if (new_position > _position) {
552                 start_shift = new_position - _position;
553         } else {
554                 start_shift = -(_position - new_position);
555         }
556
557         if (start_shift > 0) {
558
559                 if (_start > max_frames - start_shift) {
560                         new_start = max_frames;
561                 } else {
562                         new_start = _start + start_shift;
563                 }
564
565                 if (!verify_start (new_start)) {
566                         return;
567                 }
568
569         } else if (start_shift < 0) {
570
571                 if (_start < (nframes_t) -start_shift) {
572                         new_start = 0;
573                 } else {
574                         new_start = _start + start_shift;
575                 }
576         } else {
577                 return;
578         }
579
580         if (new_start == _start) {
581                 return;
582         }
583         
584         _start = new_start;
585         _flags = Region::Flag (_flags & ~WholeFile);
586         first_edit ();
587
588         send_change (StartChanged);
589 }
590
591 void
592 Region::trim_front (nframes_t new_position, void *src)
593 {
594         if (_flags & Locked) {
595                 return;
596         }
597
598         nframes_t end = last_frame();
599         nframes_t source_zero;
600
601         if (_position > _start) {
602                 source_zero = _position - _start;
603         } else {
604                 source_zero = 0; // its actually negative, but this will work for us
605         }
606
607         if (new_position < end) { /* can't trim it zero or negative length */
608                 
609                 nframes_t newlen;
610
611                 /* can't trim it back passed where source position zero is located */
612                 
613                 new_position = max (new_position, source_zero);
614                 
615                 
616                 if (new_position > _position) {
617                         newlen = _length - (new_position - _position);
618                 } else {
619                         newlen = _length + (_position - new_position);
620                 }
621                 
622                 trim_to_internal (new_position, newlen, src);
623                 if (!_frozen) {
624                         recompute_at_start ();
625                 }
626         }
627 }
628
629 void
630 Region::trim_end (nframes_t new_endpoint, void *src)
631 {
632         if (_flags & Locked) {
633                 return;
634         }
635
636         if (new_endpoint > _position) {
637                 trim_to_internal (_position, new_endpoint - _position, this);
638                 if (!_frozen) {
639                         recompute_at_end ();
640                 }
641         }
642 }
643
644 void
645 Region::trim_to (nframes_t position, nframes_t length, void *src)
646 {
647         if (_flags & Locked) {
648                 return;
649         }
650
651         trim_to_internal (position, length, src);
652
653         if (!_frozen) {
654                 recompute_at_start ();
655                 recompute_at_end ();
656         }
657 }
658
659 void
660 Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
661 {
662         int32_t start_shift;
663         nframes_t new_start;
664
665         if (_flags & Locked) {
666                 return;
667         }
668
669         if (position > _position) {
670                 start_shift = position - _position;
671         } else {
672                 start_shift = -(_position - position);
673         }
674
675         if (start_shift > 0) {
676
677                 if (_start > max_frames - start_shift) {
678                         new_start = max_frames;
679                 } else {
680                         new_start = _start + start_shift;
681                 }
682
683
684         } else if (start_shift < 0) {
685
686                 if (_start < (nframes_t) -start_shift) {
687                         new_start = 0;
688                 } else {
689                         new_start = _start + start_shift;
690                 }
691         } else {
692                 new_start = _start;
693         }
694
695         if (!verify_start_and_length (new_start, length)) {
696                 return;
697         }
698
699         Change what_changed = Change (0);
700
701         if (_start != new_start) {
702                 _start = new_start;
703                 what_changed = Change (what_changed|StartChanged);
704         }
705         if (_length != length) {
706                 _length = length;
707                 what_changed = Change (what_changed|LengthChanged);
708         }
709         if (_position != position) {
710                 _position = position;
711                 what_changed = Change (what_changed|PositionChanged);
712         }
713         
714         _flags = Region::Flag (_flags & ~WholeFile);
715
716         if (what_changed & (StartChanged|LengthChanged)) {
717                 first_edit ();
718         } 
719
720         if (what_changed) {
721                 send_change (what_changed);
722         }
723 }       
724
725 void
726 Region::set_hidden (bool yn)
727 {
728         if (hidden() != yn) {
729
730                 if (yn) {
731                         _flags = Flag (_flags|Hidden);
732                 } else {
733                         _flags = Flag (_flags & ~Hidden);
734                 }
735
736                 send_change (HiddenChanged);
737         }
738 }
739
740 void
741 Region::set_muted (bool yn)
742 {
743         if (muted() != yn) {
744
745                 if (yn) {
746                         _flags = Flag (_flags|Muted);
747                 } else {
748                         _flags = Flag (_flags & ~Muted);
749                 }
750
751                 send_change (MuteChanged);
752         }
753 }
754
755 void
756 Region::set_opaque (bool yn)
757 {
758         if (opaque() != yn) {
759                 if (yn) {
760                         _flags = Flag (_flags|Opaque);
761                 } else {
762                         _flags = Flag (_flags & ~Opaque);
763                 }
764                 send_change (OpacityChanged);
765         }
766 }
767
768 void
769 Region::set_locked (bool yn)
770 {
771         if (locked() != yn) {
772                 if (yn) {
773                         _flags = Flag (_flags|Locked);
774                 } else {
775                         _flags = Flag (_flags & ~Locked);
776                 }
777                 send_change (LockChanged);
778         }
779 }
780
781 void
782 Region::set_position_locked (bool yn)
783 {
784         if (position_locked() != yn) {
785                 if (yn) {
786                         _flags = Flag (_flags|PositionLocked);
787                 } else {
788                         _flags = Flag (_flags & ~PositionLocked);
789                 }
790                 send_change (LockChanged);
791         }
792 }
793
794 void
795 Region::set_sync_position (nframes_t absolute_pos)
796 {
797         nframes_t file_pos;
798
799         file_pos = _start + (absolute_pos - _position);
800
801         if (file_pos != _sync_position) {
802                 
803                 _sync_position = file_pos;
804                 _flags = Flag (_flags|SyncMarked);
805
806                 if (!_frozen) {
807                         maybe_uncopy ();
808                 }
809                 send_change (SyncOffsetChanged);
810         }
811 }
812
813 void
814 Region::clear_sync_position ()
815 {
816         if (_flags & SyncMarked) {
817                 _flags = Flag (_flags & ~SyncMarked);
818
819                 if (!_frozen) {
820                         maybe_uncopy ();
821                 }
822                 send_change (SyncOffsetChanged);
823         }
824 }
825
826 nframes_t
827 Region::sync_offset (int& dir) const
828 {
829         /* returns the sync point relative the first frame of the region */
830
831         if (_flags & SyncMarked) {
832                 if (_sync_position > _start) {
833                         dir = 1;
834                         return _sync_position - _start; 
835                 } else {
836                         dir = -1;
837                         return _start - _sync_position;
838                 }
839         } else {
840                 dir = 0;
841                 return 0;
842         }
843 }
844
845 nframes_t 
846 Region::adjust_to_sync (nframes_t pos)
847 {
848         int sync_dir;
849         nframes_t offset = sync_offset (sync_dir);
850         
851         if (sync_dir > 0) {
852                 if (max_frames - pos > offset) {
853                         pos += offset;
854                 }
855         } else {
856                 if (pos > offset) {
857                         pos -= offset;
858                 } else {
859                         pos = 0;
860                 }
861         }
862
863         return pos;
864 }
865
866 nframes_t
867 Region::sync_position() const
868 {
869         if (_flags & SyncMarked) {
870                 return _sync_position; 
871         } else {
872                 return _start;
873         }
874 }
875
876
877 void
878 Region::raise_to_top ()
879 {
880         boost::shared_ptr<Playlist> pl (playlist());
881         if (pl) {
882                 pl->raise_region_to_top (shared_from_this());
883         }
884 }
885
886 void
887 Region::lower_to_bottom ()
888 {
889         boost::shared_ptr<Playlist> pl (playlist());
890         if (pl) {
891                 pl->lower_region_to_bottom (shared_from_this());
892         }
893 }
894
895 void
896 Region::set_layer (layer_t l)
897 {
898         if (_layer != l) {
899                 _layer = l;
900                 
901                 send_change (LayerChanged);
902         }
903 }
904
905 XMLNode&
906 Region::state (bool full_state)
907 {
908         XMLNode *node = new XMLNode ("Region");
909         char buf[64];
910         char* fe = NULL;
911
912         _id.print (buf, sizeof (buf));
913         node->add_property ("id", buf);
914         node->add_property ("name", _name);
915         node->add_property ("type", _type.to_string());
916         snprintf (buf, sizeof (buf), "%u", _start);
917         node->add_property ("start", buf);
918         snprintf (buf, sizeof (buf), "%u", _length);
919         node->add_property ("length", buf);
920         snprintf (buf, sizeof (buf), "%u", _position);
921         node->add_property ("position", buf);
922         
923         switch (_first_edit) {
924         case EditChangesNothing:
925                 fe = X_("nothing");
926                 break;
927         case EditChangesName:
928                 fe = X_("name");
929                 break;
930         case EditChangesID:
931                 fe = X_("id");
932                 break;
933         default: /* should be unreachable but makes g++ happy */
934                 fe = X_("nothing");
935                 break;
936         }
937
938         node->add_property ("first_edit", fe);
939
940         /* note: flags are stored by derived classes */
941
942         snprintf (buf, sizeof (buf), "%d", (int) _layer);
943         node->add_property ("layer", buf);
944         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
945         node->add_property ("sync-position", buf);
946
947         return *node;
948 }
949
950 XMLNode&
951 Region::get_state ()
952 {
953         return state (true);
954 }
955
956 int
957 Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
958 {
959         const XMLNodeList& nlist = node.children();
960         const XMLProperty *prop;
961         nframes_t val;
962
963         /* this is responsible for setting those aspects of Region state 
964            that are mutable after construction.
965         */
966
967         if ((prop = node.property ("name")) == 0) {
968                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
969                 return -1;
970         }
971
972         _name = prop->value();
973         
974         if ((prop = node.property ("type")) == 0) {
975                 _type = DataType::AUDIO;
976         } else {
977                 _type = DataType(prop->value());
978         }
979
980         if ((prop = node.property ("start")) != 0) {
981                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
982                 if (val != _start) {
983                         what_changed = Change (what_changed|StartChanged);      
984                         _start = val;
985                 }
986         } else {
987                 _start = 0;
988         }
989
990         if ((prop = node.property ("length")) != 0) {
991                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
992                 if (val != _length) {
993                         what_changed = Change (what_changed|LengthChanged);
994                         _length = val;
995                 }
996         } else {
997                 _length = 1;
998         }
999
1000         if ((prop = node.property ("position")) != 0) {
1001                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1002                 if (val != _position) {
1003                         what_changed = Change (what_changed|PositionChanged);
1004                         _position = val;
1005                 }
1006         } else {
1007                 _position = 0;
1008         }
1009
1010         if ((prop = node.property ("layer")) != 0) {
1011                 layer_t x;
1012                 x = (layer_t) atoi (prop->value().c_str());
1013                 if (x != _layer) {
1014                         what_changed = Change (what_changed|LayerChanged);
1015                         _layer = x;
1016                 }
1017         } else {
1018                 _layer = 0;
1019         }
1020
1021         if ((prop = node.property ("sync-position")) != 0) {
1022                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1023                 if (val != _sync_position) {
1024                         what_changed = Change (what_changed|SyncOffsetChanged);
1025                         _sync_position = val;
1026                 }
1027         } else {
1028                 _sync_position = _start;
1029         }
1030
1031         /* XXX FIRST EDIT !!! */
1032         
1033         /* note: derived classes set flags */
1034
1035         if (_extra_xml) {
1036                 delete _extra_xml;
1037                 _extra_xml = 0;
1038         }
1039
1040         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1041                 
1042                 XMLNode *child;
1043                 
1044                 child = (*niter);
1045                 
1046                 if (child->name () == "extra") {
1047                         _extra_xml = new XMLNode (*child);
1048                         break;
1049                 }
1050         }
1051
1052         if (send) {
1053                 send_change (what_changed);
1054         }
1055
1056         return 0;
1057 }
1058
1059 int
1060 Region::set_state (const XMLNode& node)
1061 {
1062         const XMLProperty *prop;
1063         Change what_changed = Change (0);
1064
1065         /* ID is not allowed to change, ever */
1066
1067         if ((prop = node.property ("id")) == 0) {
1068                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1069                 return -1;
1070         }
1071
1072         _id = prop->value();
1073         
1074         _first_edit = EditChangesNothing;
1075         
1076         set_live_state (node, what_changed, true);
1077
1078         return 0;
1079 }
1080
1081 void
1082 Region::freeze ()
1083 {
1084         _frozen++;
1085 }
1086
1087 void
1088 Region::thaw (const string& why)
1089 {
1090         Change what_changed = Change (0);
1091
1092         {
1093                 Glib::Mutex::Lock lm (_lock);
1094
1095                 if (_frozen && --_frozen > 0) {
1096                         return;
1097                 }
1098
1099                 if (_pending_changed) {
1100                         what_changed = _pending_changed;
1101                         _pending_changed = Change (0);
1102                 }
1103         }
1104
1105         if (what_changed == Change (0)) {
1106                 return;
1107         }
1108
1109         if (what_changed & LengthChanged) {
1110                 if (what_changed & PositionChanged) {
1111                         recompute_at_start ();
1112                 } 
1113                 recompute_at_end ();
1114         }
1115                 
1116         StateChanged (what_changed);
1117 }
1118
1119 void
1120 Region::send_change (Change what_changed)
1121 {
1122         {
1123                 Glib::Mutex::Lock lm (_lock);
1124                 if (_frozen) {
1125                         _pending_changed = Change (_pending_changed|what_changed);
1126                         return;
1127                 } 
1128         }
1129
1130         StateChanged (what_changed);
1131 }
1132
1133 void
1134 Region::set_last_layer_op (uint64_t when)
1135 {
1136         _last_layer_op = when;
1137 }
1138
1139 bool
1140 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1141 {
1142         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1143 }
1144
1145 bool
1146 Region::equivalent (boost::shared_ptr<const Region> other) const
1147 {
1148         return _start == other->_start &&
1149                 _position == other->_position &&
1150                 _length == other->_length;
1151 }
1152
1153 bool
1154 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1155 {
1156         return _start == other->_start &&
1157                 _length == other->_length;
1158 }
1159
1160 bool
1161 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1162 {
1163         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1164 }
1165
1166 void
1167 Region::source_deleted (boost::shared_ptr<Source>)
1168 {
1169         delete this;
1170 }
1171
1172 vector<string>
1173 Region::master_source_names ()
1174 {
1175         SourceList::iterator i;
1176
1177         vector<string> names;
1178         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1179                 names.push_back((*i)->name());
1180         }
1181
1182         return names;
1183 }
1184
1185 bool
1186 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1187 {
1188         if (!other)
1189                 return false;
1190
1191         SourceList::const_iterator i;
1192         SourceList::const_iterator io;
1193
1194         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1195                 if ((*i)->id() != (*io)->id()) {
1196                         return false;
1197                 }
1198         }
1199
1200         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1201                 if ((*i)->id() != (*io)->id()) {
1202                         return false;
1203                 }
1204         }
1205
1206         return true;
1207 }
1208
1209 bool
1210 Region::verify_length (nframes_t len)
1211 {
1212         for (uint32_t n=0; n < _sources.size(); ++n) {
1213                 if (_start > _sources[n]->length() - len) {
1214                         return false;
1215                 }
1216         }
1217         return true;
1218 }
1219
1220 bool
1221 Region::verify_start_and_length (nframes_t new_start, nframes_t new_length)
1222 {
1223         for (uint32_t n=0; n < _sources.size(); ++n) {
1224                 if (new_length > _sources[n]->length() - new_start) {
1225                         return false;
1226                 }
1227         }
1228         return true;
1229 }
1230 bool
1231 Region::verify_start (nframes_t pos)
1232 {
1233         for (uint32_t n=0; n < _sources.size(); ++n) {
1234                 if (pos > _sources[n]->length() - _length) {
1235                         return false;
1236                 }
1237         }
1238         return true;
1239 }
1240
1241 bool
1242 Region::verify_start_mutable (nframes_t& new_start)
1243 {
1244         for (uint32_t n=0; n < _sources.size(); ++n) {
1245                 if (new_start > _sources[n]->length() - _length) {
1246                         new_start = _sources[n]->length() - _length;
1247                 }
1248         }
1249         return true;
1250 }
1251
1252 boost::shared_ptr<Region>
1253 Region::get_parent() const
1254 {
1255         boost::shared_ptr<Playlist> pl (playlist());
1256
1257         if (pl) {
1258                 boost::shared_ptr<Region> r;
1259                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1260                 
1261                 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1262                         return boost::static_pointer_cast<Region> (r);
1263                 }
1264         }
1265         
1266         return boost::shared_ptr<Region>();
1267 }
1268
1269 int
1270 Region::apply (Filter& filter)
1271 {
1272         return filter.run (shared_from_this());
1273 }
1274
1275