Tidy up the popup region editor and fix a few bugs with it; should close mantis 2601...
[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 #include <sstream>
25
26 #include <sigc++/bind.h>
27 #include <sigc++/class_slot.h>
28
29 #include <glibmm/thread.h>
30 #include "pbd/xml++.h"
31 #include "pbd/stacktrace.h"
32 #include "pbd/enumwriter.h"
33
34 #include "ardour/region.h"
35 #include "ardour/playlist.h"
36 #include "ardour/session.h"
37 #include "ardour/source.h"
38 #include "ardour/tempo.h"
39 #include "ardour/region_factory.h"
40 #include "ardour/filter.h"
41 #include "ardour/profile.h"
42 #include "ardour/utils.h"
43
44 #include "i18n.h"
45
46 using namespace std;
47 using namespace ARDOUR;
48 using namespace PBD;
49
50 Change Region::FadeChanged       = ARDOUR::new_change ();
51 Change Region::SyncOffsetChanged = ARDOUR::new_change ();
52 Change Region::MuteChanged       = ARDOUR::new_change ();
53 Change Region::OpacityChanged    = ARDOUR::new_change ();
54 Change Region::LockChanged       = ARDOUR::new_change ();
55 Change Region::LayerChanged      = ARDOUR::new_change ();
56 Change Region::HiddenChanged     = ARDOUR::new_change ();
57
58 sigc::signal<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
59
60 /* derived-from-derived constructor (no sources in constructor) */
61 Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
62         : SessionObject(s, name)
63         , _type(type)
64         , _flags(Flag (flags|DoNotSendPropertyChanges))
65         , _start(start)
66         , _length(length)
67         , _position(0)
68         , _last_position(0)
69         , _positional_lock_style(AudioTime)
70         , _sync_position(_start)
71         , _layer(layer)
72         , _first_edit(EditChangesNothing)
73         , _frozen(0)
74         , _ancestral_start (0)
75         , _ancestral_length (0)
76         , _stretch(1.0)
77         , _shift(1.0)
78         , _read_data_count(0)
79         , _pending_changed(Change (0))
80         , _last_layer_op(0)
81         , _pending_explicit_relayer (false)
82 {
83         /* no sources at this point */
84 }
85
86 /** Basic Region constructor (single source) */
87 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
88         : SessionObject(src->session(), name)
89         , _type(type)
90         , _flags(Flag (flags|DoNotSendPropertyChanges))
91         , _start(start)
92         , _length(length)
93         , _position(0)
94         , _last_position(0)
95         , _positional_lock_style(AudioTime)
96         , _sync_position(_start)
97         , _layer(layer)
98         , _first_edit(EditChangesNothing)
99         , _frozen(0)
100         , _ancestral_start (0)
101         , _ancestral_length (0)
102         , _stretch (1.0)
103         , _shift (1.0)
104         , _valid_transients(false)
105         , _read_data_count(0)
106         , _pending_changed(Change (0))
107         , _last_layer_op(0)
108         , _pending_explicit_relayer (false)
109 {
110         _sources.push_back (src);
111         _master_sources.push_back (src);
112
113         src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
114
115         assert(_sources.size() > 0);
116         _positional_lock_style = AudioTime;
117 }
118
119 /** Basic Region constructor (many sources) */
120 Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
121         : SessionObject(srcs.front()->session(), name)
122         , _type(type)
123         , _flags(Flag (flags|DoNotSendPropertyChanges))
124         , _start(start)
125         , _length(length)
126         , _position(0)
127         , _last_position(0)
128         , _positional_lock_style(AudioTime)
129         , _sync_position(_start)
130         , _layer(layer)
131         , _first_edit(EditChangesNothing)
132         , _frozen(0)
133         , _ancestral_start (0)
134         , _ancestral_length (0)
135         , _stretch(1.0)
136         , _shift(1.0)
137         , _read_data_count(0)
138         , _pending_changed(Change (0))
139         , _last_layer_op(0)
140         , _pending_explicit_relayer (false)
141 {
142         use_sources (srcs);
143         assert(_sources.size() > 0);
144 }
145
146 /** Create a new Region from part of an existing one */
147 Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
148         : SessionObject(other->session(), name)
149         , _type (other->data_type())
150         , _pending_explicit_relayer (false)
151
152 {
153         _start = other->_start + offset;
154         copy_stuff (other, offset, length, name, layer, flags);
155
156         _flags = Flag (_flags | DoNotSendPropertyChanges);
157
158         /* if the other region had a distinct sync point
159            set, then continue to use it as best we can.
160            otherwise, reset sync point back to start.
161         */
162
163         if (other->flags() & SyncMarked) {
164                 if (other->_sync_position < _start) {
165                         _flags = Flag (_flags & ~SyncMarked);
166                         _sync_position = _start;
167                 } else {
168                         _sync_position = other->_sync_position;
169                 }
170         } else {
171                 _flags = Flag (_flags & ~SyncMarked);
172                 _sync_position = _start;
173         }
174
175         if (Profile->get_sae()) {
176                 /* reset sync point to start if its ended up
177                    outside region bounds.
178                 */
179
180                 if (_sync_position < _start || _sync_position >= _start + _length) {
181                         _flags = Flag (_flags & ~SyncMarked);
182                         _sync_position = _start;
183                 }
184         }
185 }
186
187 Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
188         : SessionObject(other->session(), name)
189         , _type (other->data_type())
190         , _pending_explicit_relayer (false)
191 {
192         /* create a new Region exactly like another but starting at 0 in its sources */
193
194         _start = 0;
195         copy_stuff (other, 0, length, name, layer, flags);
196
197         _flags = Flag (_flags | DoNotSendPropertyChanges);
198
199         /* sync pos is relative to start of file. our start-in-file is now zero,
200            so set our sync position to whatever the the difference between
201            _start and _sync_pos was in the other region.
202
203            result is that our new sync pos points to the same point in our source(s)
204            as the sync in the other region did in its source(s).
205
206            since we start at zero in our source(s), it is not possible to use a sync point that
207            is before the start. reset it to _start if that was true in the other region.
208         */
209
210         if (other->flags() & SyncMarked) {
211                 if (other->_start < other->_sync_position) {
212                         /* sync pos was after the start point of the other region */
213                         _sync_position = other->_sync_position - other->_start;
214                 } else {
215                         /* sync pos was before the start point of the other region. not possible here. */
216                         _flags = Flag (_flags & ~SyncMarked);
217                         _sync_position = _start;
218                 }
219         } else {
220                 _flags = Flag (_flags & ~SyncMarked);
221                 _sync_position = _start;
222         }
223
224         if (Profile->get_sae()) {
225                 /* reset sync point to start if its ended up
226                    outside region bounds.
227                 */
228
229                 if (_sync_position < _start || _sync_position >= _start + _length) {
230                         _flags = Flag (_flags & ~SyncMarked);
231                         _sync_position = _start;
232                 }
233         }
234
235         /* reset a couple of things that copy_stuff() gets wrong in this particular case */
236
237         _positional_lock_style = other->_positional_lock_style;
238         _first_edit = other->_first_edit;
239 }
240
241 /** Pure copy constructor */
242 Region::Region (boost::shared_ptr<const Region> other)
243         : SessionObject(other->session(), other->name())
244         , _type(other->data_type())
245         , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
246         , _start(other->_start)
247         , _length(other->_length)
248         , _position(other->_position)
249         , _last_position(other->_last_position)
250         , _positional_lock_style(other->_positional_lock_style)
251         , _sync_position(other->_sync_position)
252         , _layer(other->_layer)
253         , _first_edit(EditChangesID)
254         , _frozen(0)
255         , _ancestral_start (other->_ancestral_start)
256         , _ancestral_length (other->_ancestral_length)
257         , _stretch (other->_stretch)
258         , _shift (other->_shift)
259         , _valid_transients(false)
260         , _read_data_count(0)
261         , _pending_changed(Change(0))
262         , _last_layer_op(other->_last_layer_op)
263         , _pending_explicit_relayer (false)
264 {
265         _flags = Flag (_flags | DoNotSendPropertyChanges);
266
267         other->_first_edit = EditChangesName;
268
269         if (other->_extra_xml) {
270                 _extra_xml = new XMLNode (*other->_extra_xml);
271         } else {
272                 _extra_xml = 0;
273         }
274
275         use_sources (other->_sources);
276         assert(_sources.size() > 0);
277 }
278
279 Region::Region (const SourceList& srcs, const XMLNode& node)
280         : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
281         , _type(DataType::NIL) // to be loaded from XML
282         , _flags(DoNotSendPropertyChanges)
283         , _start(0)
284         , _length(0)
285         , _position(0)
286         , _last_position(0)
287         , _positional_lock_style(AudioTime)
288         , _sync_position(_start)
289         , _layer(0)
290         , _first_edit(EditChangesNothing)
291         , _frozen(0)
292         , _stretch(1.0)
293         , _shift(1.0)
294         , _read_data_count(0)
295         , _pending_changed(Change(0))
296         , _last_layer_op(0)
297         , _pending_explicit_relayer (false)
298 {
299         use_sources (srcs);
300
301         if (set_state (node, Stateful::loading_state_version)) {
302                 throw failed_constructor();
303         }
304
305         assert(_type != DataType::NIL);
306         assert(_sources.size() > 0);
307 }
308
309 Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
310         : SessionObject(src->session(), X_("error: XML did not reset this"))
311         , _type(DataType::NIL)
312         , _flags(DoNotSendPropertyChanges)
313         , _start(0)
314         , _length(0)
315         , _position(0)
316         , _last_position(0)
317         , _positional_lock_style(AudioTime)
318         , _sync_position(_start)
319         , _layer(0)
320         , _first_edit(EditChangesNothing)
321         , _frozen(0)
322         , _stretch(1.0)
323         , _shift(1.0)
324         , _read_data_count(0)
325         , _pending_changed(Change(0))
326         , _last_layer_op(0)
327         , _pending_explicit_relayer (false)
328 {
329         _sources.push_back (src);
330
331         if (set_state (node, Stateful::loading_state_version)) {
332                 throw failed_constructor();
333         }
334
335         assert(_type != DataType::NIL);
336         assert(_sources.size() > 0);
337 }
338
339 Region::~Region ()
340 {
341         boost::shared_ptr<Playlist> pl (playlist());
342
343         if (pl) {
344                 for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
345                         (*i)->remove_playlist (pl);
346                 }
347                 for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
348                         (*i)->remove_playlist (pl);
349                 }
350         }
351
352         notify_callbacks ();
353         GoingAway (); /* EMIT SIGNAL */
354 }
355
356 void
357 Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t /*offset*/, nframes_t length, const string& name, layer_t layer, Flag flags)
358 {
359         _frozen = 0;
360         _pending_changed = Change (0);
361         _read_data_count = 0;
362         _valid_transients = false;
363
364         _length = length;
365         _last_length = length;
366         _sync_position = other->_sync_position;
367         _ancestral_start = other->_ancestral_start;
368         _ancestral_length = other->_ancestral_length;
369         _stretch = other->_stretch;
370         _shift = other->_shift;
371         _name = name;
372         _last_position = 0;
373         _position = 0;
374         _layer = layer;
375         _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
376         _first_edit = EditChangesNothing;
377         _last_layer_op = 0;
378         _positional_lock_style = AudioTime;
379
380         use_sources (other->_sources);
381 }
382
383 void
384 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
385 {
386         boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
387
388         boost::shared_ptr<Playlist> pl (wpl.lock());
389
390         if (old_playlist == pl) {
391                 return;
392         }
393
394         _playlist = pl;
395
396         if (pl) {
397                 if (old_playlist) {
398                         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
399                                 (*i)->remove_playlist (_playlist);
400                                 (*i)->add_playlist (pl);
401                         }
402                         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
403                                 (*i)->remove_playlist (_playlist);
404                                 (*i)->add_playlist (pl);
405                         }
406                 } else {
407                         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
408                                 (*i)->add_playlist (pl);
409                         }
410                         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
411                                 (*i)->add_playlist (pl);
412                         }
413                 }
414         } else {
415                 if (old_playlist) {
416                         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
417                                 (*i)->remove_playlist (old_playlist);
418                         }
419                         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
420                                 (*i)->remove_playlist (old_playlist);
421                         }
422                 }
423         }
424 }
425
426 bool
427 Region::set_name (const std::string& str)
428 {
429         if (_name != str) {
430                 SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
431                 assert(_name == str);
432                 send_change (ARDOUR::NameChanged);
433         }
434
435         return true;
436 }
437
438 void
439 Region::set_length (nframes_t len, void */*src*/)
440 {
441         //cerr << "Region::set_length() len = " << len << endl;
442         if (_flags & Locked) {
443                 return;
444         }
445
446         if (_length != len && len != 0) {
447
448                 /* check that the current _position wouldn't make the new
449                    length impossible.
450                 */
451
452                 if (max_frames - len < _position) {
453                         return;
454                 }
455
456                 if (!verify_length (len)) {
457                         return;
458                 }
459
460
461                 _last_length = _length;
462                 _length = len;
463
464                 _flags = Region::Flag (_flags & ~WholeFile);
465
466                 first_edit ();
467                 maybe_uncopy ();
468                 invalidate_transients ();
469
470                 if (!_frozen) {
471                         recompute_at_end ();
472                 }
473
474                 send_change (LengthChanged);
475         }
476 }
477
478 void
479 Region::maybe_uncopy ()
480 {
481 }
482
483 void
484 Region::first_edit ()
485 {
486         boost::shared_ptr<Playlist> pl (playlist());
487
488         if (_first_edit != EditChangesNothing && pl) {
489
490                 _name = pl->session().new_region_name (_name);
491                 _first_edit = EditChangesNothing;
492
493                 send_change (ARDOUR::NameChanged);
494                 RegionFactory::CheckNewRegion (shared_from_this());
495         }
496 }
497
498 bool
499 Region::at_natural_position () const
500 {
501         boost::shared_ptr<Playlist> pl (playlist());
502
503         if (!pl) {
504                 return false;
505         }
506
507         boost::shared_ptr<Region> whole_file_region = get_parent();
508
509         if (whole_file_region) {
510                 if (_position == whole_file_region->position() + _start) {
511                         return true;
512                 }
513         }
514
515         return false;
516 }
517
518 void
519 Region::move_to_natural_position (void *src)
520 {
521         boost::shared_ptr<Playlist> pl (playlist());
522
523         if (!pl) {
524                 return;
525         }
526
527         boost::shared_ptr<Region> whole_file_region = get_parent();
528
529         if (whole_file_region) {
530                 set_position (whole_file_region->position() + _start, src);
531         }
532 }
533
534 void
535 Region::special_set_position (nframes_t pos)
536 {
537         /* this is used when creating a whole file region as
538            a way to store its "natural" or "captured" position.
539         */
540
541         _position = _position;
542         _position = pos;
543 }
544
545 void
546 Region::set_position_lock_style (PositionLockStyle ps)
547 {
548         boost::shared_ptr<Playlist> pl (playlist());
549
550         if (!pl) {
551                 return;
552         }
553
554         _positional_lock_style = ps;
555
556         if (_positional_lock_style == MusicTime) {
557                 pl->session().tempo_map().bbt_time (_position, _bbt_time);
558         }
559
560 }
561
562 void
563 Region::update_position_after_tempo_map_change ()
564 {
565         boost::shared_ptr<Playlist> pl (playlist());
566
567         if (!pl || _positional_lock_style != MusicTime) {
568                 return;
569         }
570
571         TempoMap& map (pl->session().tempo_map());
572         nframes_t pos = map.frame_time (_bbt_time);
573         set_position_internal (pos, false);
574 }
575
576 void
577 Region::set_position (nframes_t pos, void */*src*/)
578 {
579         if (!can_move()) {
580                 return;
581         }
582
583         set_position_internal (pos, true);
584 }
585
586 void
587 Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
588 {
589         if (_position != pos) {
590                 _last_position = _position;
591                 _position = pos;
592
593                 /* check that the new _position wouldn't make the current
594                    length impossible - if so, change the length.
595
596                    XXX is this the right thing to do?
597                 */
598
599                 if (max_frames - _length < _position) {
600                         _last_length = _length;
601                         _length = max_frames - _position;
602                 }
603
604                 if (allow_bbt_recompute) {
605                         recompute_position_from_lock_style ();
606                 }
607
608                 invalidate_transients ();
609         }
610
611         /* do this even if the position is the same. this helps out
612            a GUI that has moved its representation already.
613         */
614
615         send_change (PositionChanged);
616 }
617
618 void
619 Region::set_position_on_top (nframes_t pos, void */*src*/)
620 {
621         if (_flags & Locked) {
622                 return;
623         }
624
625         if (_position != pos) {
626                 _last_position = _position;
627                 _position = pos;
628         }
629
630         boost::shared_ptr<Playlist> pl (playlist());
631
632         if (pl) {
633                 pl->raise_region_to_top (shared_from_this ());
634         }
635
636         /* do this even if the position is the same. this helps out
637            a GUI that has moved its representation already.
638         */
639
640         send_change (PositionChanged);
641 }
642
643 void
644 Region::recompute_position_from_lock_style ()
645 {
646         if (_positional_lock_style == MusicTime) {
647                 boost::shared_ptr<Playlist> pl (playlist());
648                 if (pl) {
649                         pl->session().tempo_map().bbt_time (_position, _bbt_time);
650                 }
651         }
652 }
653
654 void
655 Region::nudge_position (nframes64_t n, void */*src*/)
656 {
657         if (_flags & Locked) {
658                 return;
659         }
660
661         if (n == 0) {
662                 return;
663         }
664
665         _last_position = _position;
666
667         if (n > 0) {
668                 if (_position > max_frames - n) {
669                         _position = max_frames;
670                 } else {
671                         _position += n;
672                 }
673         } else {
674                 if (_position < (nframes_t) -n) {
675                         _position = 0;
676                 } else {
677                         _position += n;
678                 }
679         }
680
681         send_change (PositionChanged);
682 }
683
684 void
685 Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
686 {
687         _ancestral_length = l;
688         _ancestral_start = s;
689         _stretch = st;
690         _shift = sh;
691 }
692
693 void
694 Region::set_start (nframes_t pos, void */*src*/)
695 {
696         if (_flags & (Locked|PositionLocked)) {
697                 return;
698         }
699         /* This just sets the start, nothing else. It effectively shifts
700            the contents of the Region within the overall extent of the Source,
701            without changing the Region's position or length
702         */
703
704         if (_start != pos) {
705
706                 if (!verify_start (pos)) {
707                         return;
708                 }
709
710                 _start = pos;
711                 _flags = Region::Flag (_flags & ~WholeFile);
712                 first_edit ();
713                 invalidate_transients ();
714
715                 send_change (StartChanged);
716         }
717 }
718
719 void
720 Region::trim_start (nframes_t new_position, void */*src*/)
721 {
722         if (_flags & (Locked|PositionLocked)) {
723                 return;
724         }
725         nframes_t new_start;
726         int32_t start_shift;
727
728         if (new_position > _position) {
729                 start_shift = new_position - _position;
730         } else {
731                 start_shift = -(_position - new_position);
732         }
733
734         if (start_shift > 0) {
735
736                 if (_start > max_frames - start_shift) {
737                         new_start = max_frames;
738                 } else {
739                         new_start = _start + start_shift;
740                 }
741
742                 if (!verify_start (new_start)) {
743                         return;
744                 }
745
746         } else if (start_shift < 0) {
747
748                 if (_start < (nframes_t) -start_shift) {
749                         new_start = 0;
750                 } else {
751                         new_start = _start + start_shift;
752                 }
753         } else {
754                 return;
755         }
756
757         if (new_start == _start) {
758                 return;
759         }
760
761         _start = new_start;
762         _flags = Region::Flag (_flags & ~WholeFile);
763         first_edit ();
764
765         send_change (StartChanged);
766 }
767
768 void
769 Region::trim_front (nframes_t new_position, void *src)
770 {
771         if (_flags & Locked) {
772                 return;
773         }
774
775         nframes_t end = last_frame();
776         nframes_t source_zero;
777
778         if (_position > _start) {
779                 source_zero = _position - _start;
780         } else {
781                 source_zero = 0; // its actually negative, but this will work for us
782         }
783
784         if (new_position < end) { /* can't trim it zero or negative length */
785
786                 nframes_t newlen;
787
788                 /* can't trim it back passed where source position zero is located */
789
790                 new_position = max (new_position, source_zero);
791
792
793                 if (new_position > _position) {
794                         newlen = _length - (new_position - _position);
795                 } else {
796                         newlen = _length + (_position - new_position);
797                 }
798
799                 trim_to_internal (new_position, newlen, src);
800                 if (!_frozen) {
801                         recompute_at_start ();
802                 }
803         }
804 }
805
806 /** @param new_endpoint New region end point, such that, for example,
807  *  a region at 0 of length 10 has an endpoint of 9.
808  */
809
810 void
811 Region::trim_end (nframes_t new_endpoint, void */*src*/)
812 {
813         if (_flags & Locked) {
814                 return;
815         }
816
817         if (new_endpoint > _position) {
818                 trim_to_internal (_position, new_endpoint - _position + 1, this);
819                 if (!_frozen) {
820                         recompute_at_end ();
821                 }
822         }
823 }
824
825 void
826 Region::trim_to (nframes_t position, nframes_t length, void *src)
827 {
828         if (_flags & Locked) {
829                 return;
830         }
831
832         trim_to_internal (position, length, src);
833
834         if (!_frozen) {
835                 recompute_at_start ();
836                 recompute_at_end ();
837         }
838 }
839
840 void
841 Region::trim_to_internal (nframes_t position, nframes_t length, void */*src*/)
842 {
843         int32_t start_shift;
844         nframes_t new_start;
845
846         if (_flags & Locked) {
847                 return;
848         }
849
850         if (position > _position) {
851                 start_shift = position - _position;
852         } else {
853                 start_shift = -(_position - position);
854         }
855
856         if (start_shift > 0) {
857
858                 if (_start > max_frames - start_shift) {
859                         new_start = max_frames;
860                 } else {
861                         new_start = _start + start_shift;
862                 }
863
864
865         } else if (start_shift < 0) {
866
867                 if (_start < (nframes_t) -start_shift) {
868                         new_start = 0;
869                 } else {
870                         new_start = _start + start_shift;
871                 }
872         } else {
873                 new_start = _start;
874         }
875
876         if (!verify_start_and_length (new_start, length)) {
877                 return;
878         }
879
880         Change what_changed = Change (0);
881
882         if (_start != new_start) {
883                 _start = new_start;
884                 what_changed = Change (what_changed|StartChanged);
885         }
886         if (_length != length) {
887                 if (!_frozen) {
888                         _last_length = _length;
889                 }
890                 _length = length;
891                 what_changed = Change (what_changed|LengthChanged);
892         }
893         if (_position != position) {
894                 if (!_frozen) {
895                         _last_position = _position;
896                 }
897                 _position = position;
898                 what_changed = Change (what_changed|PositionChanged);
899         }
900
901         _flags = Region::Flag (_flags & ~WholeFile);
902
903         if (what_changed & (StartChanged|LengthChanged)) {
904                 first_edit ();
905         }
906
907         if (what_changed) {
908                 send_change (what_changed);
909         }
910 }
911
912 void
913 Region::set_hidden (bool yn)
914 {
915         if (hidden() != yn) {
916
917                 if (yn) {
918                         _flags = Flag (_flags|Hidden);
919                 } else {
920                         _flags = Flag (_flags & ~Hidden);
921                 }
922
923                 send_change (HiddenChanged);
924         }
925 }
926
927 void
928 Region::set_muted (bool yn)
929 {
930         if (muted() != yn) {
931
932                 if (yn) {
933                         _flags = Flag (_flags|Muted);
934                 } else {
935                         _flags = Flag (_flags & ~Muted);
936                 }
937
938                 send_change (MuteChanged);
939         }
940 }
941
942 void
943 Region::set_opaque (bool yn)
944 {
945         if (opaque() != yn) {
946                 if (yn) {
947                         _flags = Flag (_flags|Opaque);
948                 } else {
949                         _flags = Flag (_flags & ~Opaque);
950                 }
951                 send_change (OpacityChanged);
952         }
953 }
954
955 void
956 Region::set_locked (bool yn)
957 {
958         if (locked() != yn) {
959                 if (yn) {
960                         _flags = Flag (_flags|Locked);
961                 } else {
962                         _flags = Flag (_flags & ~Locked);
963                 }
964                 send_change (LockChanged);
965         }
966 }
967
968 void
969 Region::set_position_locked (bool yn)
970 {
971         if (position_locked() != yn) {
972                 if (yn) {
973                         _flags = Flag (_flags|PositionLocked);
974                 } else {
975                         _flags = Flag (_flags & ~PositionLocked);
976                 }
977                 send_change (LockChanged);
978         }
979 }
980
981 void
982 Region::set_sync_position (nframes_t absolute_pos)
983 {
984         nframes_t const file_pos = _start + (absolute_pos - _position);
985
986         if (file_pos != _sync_position) {
987
988                 _sync_position = file_pos;
989                 _flags = Flag (_flags|SyncMarked);
990
991                 if (!_frozen) {
992                         maybe_uncopy ();
993                 }
994                 send_change (SyncOffsetChanged);
995         }
996 }
997
998 void
999 Region::clear_sync_position ()
1000 {
1001         if (_flags & SyncMarked) {
1002                 _flags = Flag (_flags & ~SyncMarked);
1003
1004                 if (!_frozen) {
1005                         maybe_uncopy ();
1006                 }
1007                 send_change (SyncOffsetChanged);
1008         }
1009 }
1010
1011 nframes_t
1012 Region::sync_offset (int& dir) const
1013 {
1014         /* returns the sync point relative the first frame of the region */
1015
1016         if (_flags & SyncMarked) {
1017                 if (_sync_position > _start) {
1018                         dir = 1;
1019                         return _sync_position - _start;
1020                 } else {
1021                         dir = -1;
1022                         return _start - _sync_position;
1023                 }
1024         } else {
1025                 dir = 0;
1026                 return 0;
1027         }
1028 }
1029
1030 nframes_t
1031 Region::adjust_to_sync (nframes_t pos) const
1032 {
1033         int sync_dir;
1034         nframes_t offset = sync_offset (sync_dir);
1035
1036         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1037
1038         if (sync_dir > 0) {
1039                 if (pos > offset) {
1040                         pos -= offset;
1041                 } else {
1042                         pos = 0;
1043                 }
1044         } else {
1045                 if (max_frames - pos > offset) {
1046                         pos += offset;
1047                 }
1048         }
1049
1050         return pos;
1051 }
1052
1053 nframes_t
1054 Region::sync_position() const
1055 {
1056         if (_flags & SyncMarked) {
1057                 return _sync_position;
1058         } else {
1059                 return _start;
1060         }
1061 }
1062
1063 void
1064 Region::raise ()
1065 {
1066         boost::shared_ptr<Playlist> pl (playlist());
1067         if (pl) {
1068                 pl->raise_region (shared_from_this ());
1069         }
1070 }
1071
1072 void
1073 Region::lower ()
1074 {
1075         boost::shared_ptr<Playlist> pl (playlist());
1076         if (pl) {
1077                 pl->lower_region (shared_from_this ());
1078         }
1079 }
1080
1081
1082 void
1083 Region::raise_to_top ()
1084 {
1085         boost::shared_ptr<Playlist> pl (playlist());
1086         if (pl) {
1087                 pl->raise_region_to_top (shared_from_this());
1088         }
1089 }
1090
1091 void
1092 Region::lower_to_bottom ()
1093 {
1094         boost::shared_ptr<Playlist> pl (playlist());
1095         if (pl) {
1096                 pl->lower_region_to_bottom (shared_from_this());
1097         }
1098 }
1099
1100 void
1101 Region::set_layer (layer_t l)
1102 {
1103         if (_layer != l) {
1104                 _layer = l;
1105
1106                 send_change (LayerChanged);
1107         }
1108 }
1109
1110 XMLNode&
1111 Region::state (bool /*full_state*/)
1112 {
1113         XMLNode *node = new XMLNode ("Region");
1114         char buf[64];
1115         const char* fe = NULL;
1116
1117         _id.print (buf, sizeof (buf));
1118         node->add_property ("id", buf);
1119         node->add_property ("name", _name);
1120         node->add_property ("type", _type.to_string());
1121         snprintf (buf, sizeof (buf), "%u", _start);
1122         node->add_property ("start", buf);
1123         snprintf (buf, sizeof (buf), "%u", _length);
1124         node->add_property ("length", buf);
1125         snprintf (buf, sizeof (buf), "%u", _position);
1126         node->add_property ("position", buf);
1127         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
1128         node->add_property ("ancestral-start", buf);
1129         snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
1130         node->add_property ("ancestral-length", buf);
1131         snprintf (buf, sizeof (buf), "%.12g", _stretch);
1132         node->add_property ("stretch", buf);
1133         snprintf (buf, sizeof (buf), "%.12g", _shift);
1134         node->add_property ("shift", buf);
1135
1136         switch (_first_edit) {
1137         case EditChangesNothing:
1138                 fe = X_("nothing");
1139                 break;
1140         case EditChangesName:
1141                 fe = X_("name");
1142                 break;
1143         case EditChangesID:
1144                 fe = X_("id");
1145                 break;
1146         default: /* should be unreachable but makes g++ happy */
1147                 fe = X_("nothing");
1148                 break;
1149         }
1150
1151         node->add_property ("first-edit", fe);
1152
1153         /* note: flags are stored by derived classes */
1154
1155         snprintf (buf, sizeof (buf), "%d", (int) _layer);
1156         node->add_property ("layer", buf);
1157         snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
1158         node->add_property ("sync-position", buf);
1159
1160         if (_positional_lock_style != AudioTime) {
1161                 node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
1162                 stringstream str;
1163                 str << _bbt_time;
1164                 node->add_property ("bbt-position", str.str());
1165         }
1166
1167         return *node;
1168 }
1169
1170 XMLNode&
1171 Region::get_state ()
1172 {
1173         return state (true);
1174 }
1175
1176 int
1177 Region::set_live_state (const XMLNode& node, int version, Change& what_changed, bool send)
1178 {
1179         const XMLNodeList& nlist = node.children();
1180         const XMLProperty *prop;
1181         nframes_t val;
1182
1183         /* this is responsible for setting those aspects of Region state
1184            that are mutable after construction.
1185         */
1186
1187         if ((prop = node.property ("name")) == 0) {
1188                 error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
1189                 return -1;
1190         }
1191
1192         _name = prop->value();
1193
1194         if ((prop = node.property ("type")) == 0) {
1195                 _type = DataType::AUDIO;
1196         } else {
1197                 _type = DataType(prop->value());
1198         }
1199
1200         if ((prop = node.property ("start")) != 0) {
1201                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1202                 if (val != _start) {
1203                         what_changed = Change (what_changed|StartChanged);
1204                         _start = val;
1205                 }
1206         } else {
1207                 _start = 0;
1208         }
1209
1210         if ((prop = node.property ("length")) != 0) {
1211                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1212                 if (val != _length) {
1213                         what_changed = Change (what_changed|LengthChanged);
1214                         _last_length = _length;
1215                         _length = val;
1216                 }
1217         } else {
1218                 _last_length = _length;
1219                 _length = 1;
1220         }
1221
1222         if ((prop = node.property ("position")) != 0) {
1223                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1224                 if (val != _position) {
1225                         what_changed = Change (what_changed|PositionChanged);
1226                         _last_position = _position;
1227                         _position = val;
1228                 }
1229         } else {
1230                 _last_position = _position;
1231                 _position = 0;
1232         }
1233
1234         if ((prop = node.property ("layer")) != 0) {
1235                 layer_t x;
1236                 x = (layer_t) atoi (prop->value().c_str());
1237                 if (x != _layer) {
1238                         what_changed = Change (what_changed|LayerChanged);
1239                         _layer = x;
1240                 }
1241         } else {
1242                 _layer = 0;
1243         }
1244
1245         if ((prop = node.property ("sync-position")) != 0) {
1246                 sscanf (prop->value().c_str(), "%" PRIu32, &val);
1247                 if (val != _sync_position) {
1248                         what_changed = Change (what_changed|SyncOffsetChanged);
1249                         _sync_position = val;
1250                 }
1251         } else {
1252                 _sync_position = _start;
1253         }
1254
1255         if ((prop = node.property ("positional-lock-style")) != 0) {
1256                 _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
1257
1258                 if (_positional_lock_style == MusicTime) {
1259                         if ((prop = node.property ("bbt-position")) == 0) {
1260                                 /* missing BBT info, revert to audio time locking */
1261                                 _positional_lock_style = AudioTime;
1262                         } else {
1263                                 if (sscanf (prop->value().c_str(), "%d|%d|%d",
1264                                             &_bbt_time.bars,
1265                                             &_bbt_time.beats,
1266                                             &_bbt_time.ticks) != 3) {
1267                                         _positional_lock_style = AudioTime;
1268                                 }
1269                         }
1270                 }
1271
1272         } else {
1273                 _positional_lock_style = AudioTime;
1274         }
1275
1276         /* XXX FIRST EDIT !!! */
1277
1278         /* these 3 properties never change as a result of any editing */
1279
1280         if ((prop = node.property ("ancestral-start")) != 0) {
1281                 _ancestral_start = strtoll (prop->value().c_str(), 0, 10);
1282         } else {
1283                 _ancestral_start = _start;
1284         }
1285
1286         if ((prop = node.property ("ancestral-length")) != 0) {
1287                 _ancestral_length = strtoll (prop->value().c_str(), 0, 10);
1288         } else {
1289                 _ancestral_length = _length;
1290         }
1291
1292         if ((prop = node.property ("stretch")) != 0) {
1293                 _stretch = atof (prop->value());
1294
1295                 /* fix problem with old sessions corrupted by an impossible
1296                    value for _stretch
1297                 */
1298                 if (_stretch == 0.0) {
1299                         _stretch = 1.0;
1300                 }
1301         } else {
1302                 _stretch = 1.0;
1303         }
1304
1305         if ((prop = node.property ("shift")) != 0) {
1306                 _shift = atof (prop->value());
1307
1308                 /* fix problem with old sessions corrupted by an impossible
1309                    value for _shift
1310                 */
1311                 if (_shift == 0.0) {
1312                         _shift = 1.0;
1313                 }
1314         } else {
1315                 _shift = 1.0;
1316         }
1317
1318
1319         /* note: derived classes set flags */
1320
1321         delete _extra_xml;
1322         _extra_xml = 0;
1323
1324         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1325
1326                 XMLNode *child;
1327
1328                 child = (*niter);
1329
1330                 if (child->name () == "Extra") {
1331                         _extra_xml = new XMLNode (*child);
1332                         break;
1333                 }
1334         }
1335
1336         if (send) {
1337                 send_change (what_changed);
1338         }
1339
1340         return 0;
1341 }
1342
1343 int
1344 Region::set_state (const XMLNode& node, int version)
1345 {
1346         const XMLProperty *prop;
1347         Change what_changed = Change (0);
1348
1349         /* ID is not allowed to change, ever */
1350
1351         if ((prop = node.property ("id")) == 0) {
1352                 error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
1353                 return -1;
1354         }
1355
1356         _id = prop->value();
1357
1358         _first_edit = EditChangesNothing;
1359
1360         set_live_state (node, version, what_changed, true);
1361
1362         return 0;
1363 }
1364
1365 void
1366 Region::freeze ()
1367 {
1368         _frozen++;
1369         _last_length = _length;
1370         _last_position = _position;
1371 }
1372
1373 void
1374 Region::thaw (const string& /*why*/)
1375 {
1376         Change what_changed = Change (0);
1377
1378         {
1379                 Glib::Mutex::Lock lm (_lock);
1380
1381                 if (_frozen && --_frozen > 0) {
1382                         return;
1383                 }
1384
1385                 if (_pending_changed) {
1386                         what_changed = _pending_changed;
1387                         _pending_changed = Change (0);
1388                 }
1389         }
1390
1391         if (what_changed == Change (0)) {
1392                 return;
1393         }
1394
1395         if (what_changed & LengthChanged) {
1396                 if (what_changed & PositionChanged) {
1397                         recompute_at_start ();
1398                 }
1399                 recompute_at_end ();
1400         }
1401
1402         StateChanged (what_changed);
1403 }
1404
1405 void
1406 Region::send_change (Change what_changed)
1407 {
1408         {
1409                 Glib::Mutex::Lock lm (_lock);
1410                 if (_frozen) {
1411                         _pending_changed = Change (_pending_changed|what_changed);
1412                         return;
1413                 }
1414         }
1415
1416         StateChanged (what_changed);
1417
1418         if (!(_flags & DoNotSendPropertyChanges)) {
1419
1420                 /* Try and send a shared_pointer unless this is part of the constructor.
1421                    If so, do nothing.
1422                 */
1423
1424                 try {
1425                         boost::shared_ptr<Region> rptr = shared_from_this();
1426                         RegionPropertyChanged (rptr);
1427                 } catch (...) {
1428                         /* no shared_ptr available, relax; */
1429                 }
1430         }
1431
1432 }
1433
1434 void
1435 Region::set_last_layer_op (uint64_t when)
1436 {
1437         _last_layer_op = when;
1438 }
1439
1440 bool
1441 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1442 {
1443         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1444 }
1445
1446 bool
1447 Region::equivalent (boost::shared_ptr<const Region> other) const
1448 {
1449         return _start == other->_start &&
1450                 _position == other->_position &&
1451                 _length == other->_length;
1452 }
1453
1454 bool
1455 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1456 {
1457         return _start == other->_start &&
1458                 _length == other->_length;
1459 }
1460
1461 bool
1462 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1463 {
1464         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1465 }
1466
1467 void
1468 Region::source_deleted (boost::shared_ptr<Source>)
1469 {
1470         _sources.clear ();
1471         drop_references ();
1472 }
1473
1474 vector<string>
1475 Region::master_source_names ()
1476 {
1477         SourceList::iterator i;
1478
1479         vector<string> names;
1480         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1481                 names.push_back((*i)->name());
1482         }
1483
1484         return names;
1485 }
1486
1487 void
1488 Region::set_master_sources (const SourceList& srcs)
1489 {
1490         _master_sources = srcs;
1491         assert (_sources.size() == _master_sources.size());
1492 }
1493
1494 bool
1495 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1496 {
1497         if (!other)
1498                 return false;
1499
1500         SourceList::const_iterator i;
1501         SourceList::const_iterator io;
1502
1503         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1504                 if ((*i)->id() != (*io)->id()) {
1505                         return false;
1506                 }
1507         }
1508
1509         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1510                 if ((*i)->id() != (*io)->id()) {
1511                         return false;
1512                 }
1513         }
1514
1515         return true;
1516 }
1517
1518 sframes_t
1519 Region::source_length(uint32_t n) const
1520 {
1521         return _sources[n]->length(_position - _start);
1522 }
1523
1524 bool
1525 Region::verify_length (nframes_t len)
1526 {
1527         if (source() && (source()->destructive() || source()->length_mutable())) {
1528                 return true;
1529         }
1530
1531         nframes_t maxlen = 0;
1532
1533         for (uint32_t n=0; n < _sources.size(); ++n) {
1534                 maxlen = max (maxlen, (nframes_t)source_length(n) - _start);
1535         }
1536
1537         len = min (len, maxlen);
1538
1539         return true;
1540 }
1541
1542 bool
1543 Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
1544 {
1545         if (source() && (source()->destructive() || source()->length_mutable())) {
1546                 return true;
1547         }
1548
1549         nframes_t maxlen = 0;
1550
1551         for (uint32_t n=0; n < _sources.size(); ++n) {
1552                 maxlen = max (maxlen, (nframes_t)source_length(n) - new_start);
1553         }
1554
1555         new_length = min (new_length, maxlen);
1556
1557         return true;
1558 }
1559
1560 bool
1561 Region::verify_start (nframes_t pos)
1562 {
1563         if (source() && (source()->destructive() || source()->length_mutable())) {
1564                 return true;
1565         }
1566
1567         for (uint32_t n=0; n < _sources.size(); ++n) {
1568                 if (pos > source_length(n) - _length) {
1569                         return false;
1570                 }
1571         }
1572         return true;
1573 }
1574
1575 bool
1576 Region::verify_start_mutable (nframes_t& new_start)
1577 {
1578         if (source() && (source()->destructive() || source()->length_mutable())) {
1579                 return true;
1580         }
1581
1582         for (uint32_t n=0; n < _sources.size(); ++n) {
1583                 if (new_start > source_length(n) - _length) {
1584                         new_start = source_length(n) - _length;
1585                 }
1586         }
1587         return true;
1588 }
1589
1590 boost::shared_ptr<Region>
1591 Region::get_parent() const
1592 {
1593         boost::shared_ptr<Playlist> pl (playlist());
1594
1595         if (pl) {
1596                 boost::shared_ptr<Region> r;
1597                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1598
1599                 if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
1600                         return boost::static_pointer_cast<Region> (r);
1601                 }
1602         }
1603
1604         return boost::shared_ptr<Region>();
1605 }
1606
1607 int
1608 Region::apply (Filter& filter)
1609 {
1610         return filter.run (shared_from_this());
1611 }
1612
1613
1614 void
1615 Region::invalidate_transients ()
1616 {
1617         _valid_transients = false;
1618         _transients.clear ();
1619 }
1620
1621
1622 void
1623 Region::use_sources (SourceList const & s)
1624 {
1625         set<boost::shared_ptr<Source> > unique_srcs;
1626
1627         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1628                 _sources.push_back (*i);
1629                 (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), *i));
1630                 unique_srcs.insert (*i);
1631         }
1632
1633         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1634                 _master_sources.push_back (*i);
1635                 if (unique_srcs.find (*i) == unique_srcs.end()) {
1636                         (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), *i));
1637                 }
1638         }
1639 }
1640