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