allow region list selection of whole file regions to select every instance of a regio...
[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         add_properties (*node);
1130
1131         id().print (buf, sizeof (buf));
1132         node->add_property ("id", buf);
1133         node->add_property ("type", _type.to_string());
1134
1135         switch (_first_edit) {
1136         case EditChangesNothing:
1137                 fe = X_("nothing");
1138                 break;
1139         case EditChangesName:
1140                 fe = X_("name");
1141                 break;
1142         case EditChangesID:
1143                 fe = X_("id");
1144                 break;
1145         default: /* should be unreachable but makes g++ happy */
1146                 fe = X_("nothing");
1147                 break;
1148         }
1149
1150         node->add_property ("first-edit", fe);
1151
1152         /* note: flags are stored by derived classes */
1153
1154         if (_position_lock_style != AudioTime) {
1155                 stringstream str;
1156                 str << _bbt_time;
1157                 node->add_property ("bbt-position", str.str());
1158         }
1159
1160         for (uint32_t n=0; n < _sources.size(); ++n) {
1161                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1162                 _sources[n]->id().print (buf, sizeof(buf));
1163                 node->add_property (buf2, buf);
1164         }
1165
1166         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1167                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1168                 _master_sources[n]->id().print (buf, sizeof (buf));
1169                 node->add_property (buf2, buf);
1170         }
1171
1172         /* Only store nested sources for the whole-file region that acts
1173            as the parent/root of all regions using it.
1174         */
1175
1176         if (_whole_file && max_source_level() > 0) {
1177
1178                 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1179
1180                 /* region is compound - get its playlist and
1181                    store that before we list the region that
1182                    needs it ...
1183                 */
1184
1185                 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1186                         nested_node->add_child_nocopy ((*s)->get_state ());
1187                 }
1188
1189                 if (nested_node) {
1190                         node->add_child_nocopy (*nested_node);
1191                 }
1192         }
1193
1194         if (_extra_xml) {
1195                 node->add_child_copy (*_extra_xml);
1196         }
1197
1198         return *node;
1199 }
1200
1201 XMLNode&
1202 Region::get_state ()
1203 {
1204         return state ();
1205 }
1206
1207 int
1208 Region::set_state (const XMLNode& node, int version)
1209 {
1210         PropertyChange what_changed;
1211         return _set_state (node, version, what_changed, true);
1212 }
1213
1214 int
1215 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1216 {
1217         const XMLProperty* prop;
1218
1219         Stateful::save_extra_xml (node);
1220
1221         what_changed = set_values (node);
1222
1223         set_id (node);
1224
1225         if (_position_lock_style == MusicTime) {
1226                 if ((prop = node.property ("bbt-position")) == 0) {
1227                         /* missing BBT info, revert to audio time locking */
1228                         _position_lock_style = AudioTime;
1229                 } else {
1230                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1231                                     &_bbt_time.bars,
1232                                     &_bbt_time.beats,
1233                                     &_bbt_time.ticks) != 3) {
1234                                 _position_lock_style = AudioTime;
1235                         }
1236                 }
1237         }
1238
1239         /* fix problems with old sessions corrupted by impossible
1240            values for _stretch or _shift
1241         */
1242         if (_stretch == 0.0f) {
1243                 _stretch = 1.0f;
1244         }
1245
1246         if (_shift == 0.0f) {
1247                 _shift = 1.0f;
1248         }
1249
1250         if (send) {
1251                 send_change (what_changed);
1252         }
1253
1254         /* Quick fix for 2.x sessions when region is muted */
1255         if ((prop = node.property (X_("flags")))) {
1256                 if (string::npos != prop->value().find("Muted")){
1257                         set_muted (true);
1258                 }
1259         }
1260
1261
1262         return 0;
1263 }
1264
1265 void
1266 Region::suspend_property_changes ()
1267 {
1268         Stateful::suspend_property_changes ();
1269         _last_length = _length;
1270         _last_position = _position;
1271 }
1272
1273 void
1274 Region::mid_thaw (const PropertyChange& what_changed)
1275 {
1276         if (what_changed.contains (Properties::length)) {
1277                 if (what_changed.contains (Properties::position)) {
1278                         recompute_at_start ();
1279                 }
1280                 recompute_at_end ();
1281         }
1282 }
1283
1284 void
1285 Region::send_change (const PropertyChange& what_changed)
1286 {
1287         if (what_changed.empty()) {
1288                 return;
1289         }
1290
1291         Stateful::send_change (what_changed);
1292
1293         if (!Stateful::property_changes_suspended()) {
1294
1295                 /* Try and send a shared_pointer unless this is part of the constructor.
1296                    If so, do nothing.
1297                 */
1298
1299                 try {
1300                         boost::shared_ptr<Region> rptr = shared_from_this();
1301                         RegionPropertyChanged (rptr, what_changed);
1302                 } catch (...) {
1303                         /* no shared_ptr available, relax; */
1304                 }
1305         }
1306 }
1307
1308 bool
1309 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1310 {
1311         return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1312 }
1313
1314 bool
1315 Region::equivalent (boost::shared_ptr<const Region> other) const
1316 {
1317         return _start == other->_start &&
1318                 _position == other->_position &&
1319                 _length == other->_length;
1320 }
1321
1322 bool
1323 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1324 {
1325         return _start == other->_start &&
1326                 _length == other->_length;
1327 }
1328
1329 bool
1330 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1331 {
1332         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1333 }
1334
1335 void
1336 Region::source_deleted (boost::weak_ptr<Source>)
1337 {
1338         drop_sources ();
1339
1340         if (!_session.deletion_in_progress()) {
1341                 /* this is a very special case: at least one of the region's
1342                    sources has bee deleted, so invalidate all references to
1343                    ourselves. Do NOT do this during session deletion, because
1344                    then we run the risk that this will actually result
1345                    in this object being deleted (as refcnt goes to zero)
1346                    while emitting DropReferences.
1347                 */
1348
1349                 drop_references ();
1350         }
1351 }
1352
1353 vector<string>
1354 Region::master_source_names ()
1355 {
1356         SourceList::iterator i;
1357
1358         vector<string> names;
1359         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1360                 names.push_back((*i)->name());
1361         }
1362
1363         return names;
1364 }
1365
1366 void
1367 Region::set_master_sources (const SourceList& srcs)
1368 {
1369         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1370                 (*i)->dec_use_count ();
1371         }
1372
1373         _master_sources = srcs;
1374         assert (_sources.size() == _master_sources.size());
1375
1376         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1377                 (*i)->inc_use_count ();
1378         }
1379 }
1380
1381 bool
1382 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1383 {
1384         if (!other)
1385                 return false;
1386
1387         if ((_sources.size() != other->_sources.size()) ||
1388             (_master_sources.size() != other->_master_sources.size())) {
1389                 return false;
1390         }
1391
1392         SourceList::const_iterator i;
1393         SourceList::const_iterator io;
1394
1395         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1396                 if ((*i)->id() != (*io)->id()) {
1397                         return false;
1398                 }
1399         }
1400
1401         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1402                 if ((*i)->id() != (*io)->id()) {
1403                         return false;
1404                 }
1405         }
1406
1407         return true;
1408 }
1409
1410 bool
1411 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1412 {
1413         if (!other) {
1414                 return false;
1415         }
1416
1417         SourceList::const_iterator i;
1418         SourceList::const_iterator io;
1419
1420         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1421                 if ((*i)->id() == (*io)->id()) {
1422                         return true;
1423                 }
1424         }
1425
1426         return false;
1427 }
1428
1429 std::string
1430 Region::source_string () const
1431 {
1432         //string res = itos(_sources.size());
1433
1434         stringstream res;
1435         res << _sources.size() << ":";
1436
1437         SourceList::const_iterator i;
1438
1439         for (i = _sources.begin(); i != _sources.end(); ++i) {
1440                 res << (*i)->id() << ":";
1441         }
1442
1443         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1444                 res << (*i)->id() << ":";
1445         }
1446
1447         return res.str();
1448 }
1449
1450 bool
1451 Region::uses_source (boost::shared_ptr<const Source> source) const
1452 {
1453         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1454                 if (*i == source) {
1455                         return true;
1456                 }
1457
1458                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1459
1460                 if (ps) {
1461                         if (ps->playlist()->uses_source (source)) {
1462                                 return true;
1463                         }
1464                 }
1465         }
1466
1467         return false;
1468 }
1469
1470 framecnt_t
1471 Region::source_length(uint32_t n) const
1472 {
1473         assert (n < _sources.size());
1474         return _sources[n]->length (_position - _start);
1475 }
1476
1477 bool
1478 Region::verify_length (framecnt_t len)
1479 {
1480         if (source() && (source()->destructive() || source()->length_mutable())) {
1481                 return true;
1482         }
1483
1484         framecnt_t maxlen = 0;
1485
1486         for (uint32_t n = 0; n < _sources.size(); ++n) {
1487                 maxlen = max (maxlen, source_length(n) - _start);
1488         }
1489
1490         len = min (len, maxlen);
1491
1492         return true;
1493 }
1494
1495 bool
1496 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1497 {
1498         if (source() && (source()->destructive() || source()->length_mutable())) {
1499                 return true;
1500         }
1501
1502         framecnt_t maxlen = 0;
1503
1504         for (uint32_t n = 0; n < _sources.size(); ++n) {
1505                 maxlen = max (maxlen, source_length(n) - new_start);
1506         }
1507
1508         new_length = min (new_length, maxlen);
1509
1510         return true;
1511 }
1512
1513 bool
1514 Region::verify_start (framepos_t pos)
1515 {
1516         if (source() && (source()->destructive() || source()->length_mutable())) {
1517                 return true;
1518         }
1519
1520         for (uint32_t n = 0; n < _sources.size(); ++n) {
1521                 if (pos > source_length(n) - _length) {
1522                         return false;
1523                 }
1524         }
1525         return true;
1526 }
1527
1528 bool
1529 Region::verify_start_mutable (framepos_t& new_start)
1530 {
1531         if (source() && (source()->destructive() || source()->length_mutable())) {
1532                 return true;
1533         }
1534
1535         for (uint32_t n = 0; n < _sources.size(); ++n) {
1536                 if (new_start > source_length(n) - _length) {
1537                         new_start = source_length(n) - _length;
1538                 }
1539         }
1540         return true;
1541 }
1542
1543 boost::shared_ptr<Region>
1544 Region::get_parent() const
1545 {
1546         boost::shared_ptr<Playlist> pl (playlist());
1547
1548         if (pl) {
1549                 boost::shared_ptr<Region> r;
1550                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1551
1552                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1553                         return boost::static_pointer_cast<Region> (r);
1554                 }
1555         }
1556
1557         return boost::shared_ptr<Region>();
1558 }
1559
1560 int
1561 Region::apply (Filter& filter, Progress* progress)
1562 {
1563         return filter.run (shared_from_this(), progress);
1564 }
1565
1566
1567 void
1568 Region::invalidate_transients ()
1569 {
1570         _valid_transients = false;
1571         _transients.clear ();
1572
1573         send_change (PropertyChange (Properties::valid_transients));
1574 }
1575
1576 void
1577 Region::drop_sources ()
1578 {
1579         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1580                 (*i)->dec_use_count ();
1581         }
1582
1583         _sources.clear ();
1584
1585         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1586                 (*i)->dec_use_count ();
1587         }
1588
1589         _master_sources.clear ();
1590 }
1591
1592 void
1593 Region::use_sources (SourceList const & s)
1594 {
1595         set<boost::shared_ptr<Source> > unique_srcs;
1596
1597         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1598
1599                 _sources.push_back (*i);
1600                 (*i)->inc_use_count ();
1601                 _master_sources.push_back (*i);
1602                 (*i)->inc_use_count ();
1603
1604                 /* connect only once to DropReferences, even if sources are replicated
1605                  */
1606
1607                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1608                         unique_srcs.insert (*i);
1609                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1610                 }
1611         }
1612 }
1613
1614 Trimmable::CanTrim
1615 Region::can_trim () const
1616 {
1617         CanTrim ct = CanTrim (0);
1618
1619         if (locked()) {
1620                 return ct;
1621         }
1622
1623         /* if not locked, we can always move the front later, and the end earlier
1624          */
1625
1626         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1627
1628         if (start() != 0 || can_trim_start_before_source_start ()) {
1629                 ct = CanTrim (ct | FrontTrimEarlier);
1630         }
1631
1632         if (!_sources.empty()) {
1633                 if ((start() + length()) < _sources.front()->length (0)) {
1634                         ct = CanTrim (ct | EndTrimLater);
1635                 }
1636         }
1637
1638         return ct;
1639 }
1640
1641 uint32_t
1642 Region::max_source_level () const
1643 {
1644         uint32_t lvl = 0;
1645
1646         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1647                 lvl = max (lvl, (*i)->level());
1648         }
1649
1650         return lvl;
1651 }
1652
1653 bool
1654 Region::is_compound () const
1655 {
1656         return max_source_level() > 0;
1657 }
1658
1659 void
1660 Region::post_set (const PropertyChange& pc)
1661 {
1662         if (pc.contains (Properties::position)) {
1663                 recompute_position_from_lock_style ();
1664         }
1665 }
1666
1667 void
1668 Region::set_start_internal (framecnt_t s)
1669 {
1670         _start = s;
1671 }
1672
1673 framepos_t
1674 Region::earliest_possible_position () const
1675 {
1676         if (_start > _position) {
1677                 return 0;
1678         } else {
1679                 return _position - _start;
1680         }
1681 }
1682
1683 framecnt_t
1684 Region::latest_possible_frame () const
1685 {
1686         framecnt_t minlen = max_framecnt;
1687
1688         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1689                 /* non-audio regions have a length that may vary based on their
1690                  * position, so we have to pass it in the call.
1691                  */
1692                 minlen = min (minlen, (*i)->length (_position));
1693         }
1694
1695         /* the latest possible last frame is determined by the current
1696          * position, plus the shortest source extent past _start.
1697          */
1698
1699         return _position + (minlen - _start) - 1;
1700 }
1701