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