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