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