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