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