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