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