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