move session file storage of nested sources from playlist into region, and reload...
[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, void */*src*/)
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 (void *src)
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, src);
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, void* /*src*/)
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, void* /*src*/)
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, void* /*src*/)
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, void* /*src*/)
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, void */*src*/)
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, void *src)
775 {
776         modify_front (new_position, false, src);
777 }
778
779 void
780 Region::cut_front (framepos_t new_position, void *src)
781 {
782         modify_front (new_position, true, src);
783 }
784
785 void
786 Region::cut_end (framepos_t new_endpoint, void *src)
787 {
788         modify_end (new_endpoint, true, src);
789 }
790
791 void
792 Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
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, src);
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, void* /*src*/)
843 {
844         if (locked()) {
845                 return;
846         }
847
848         if (new_endpoint > _position) {
849                 trim_to_internal (_position, new_endpoint - _position +1, this);
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, void* src)
865 {
866         modify_end (new_endpoint, false, src);
867 }
868
869 void
870 Region::trim_to (framepos_t position, framecnt_t length, void *src)
871 {
872         if (locked()) {
873                 return;
874         }
875
876         trim_to_internal (position, length, src);
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, void */*src*/)
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         const XMLNodeList& nlist = node.children();
1253
1254         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1255
1256                 XMLNode *child = (*niter);
1257
1258                 if (child->name () == "Extra") {
1259                         delete _extra_xml;
1260                         _extra_xml = new XMLNode (*child);
1261                 }
1262         }
1263
1264         what_changed = set_values (node);
1265
1266         if ((prop = node.property (X_("id")))) {
1267                 _id = prop->value();
1268         }
1269
1270         if (_position_lock_style == MusicTime) {
1271                 if ((prop = node.property ("bbt-position")) == 0) {
1272                         /* missing BBT info, revert to audio time locking */
1273                         _position_lock_style = AudioTime;
1274                 } else {
1275                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1276                                     &_bbt_time.bars,
1277                                     &_bbt_time.beats,
1278                                     &_bbt_time.ticks) != 3) {
1279                                 _position_lock_style = AudioTime;
1280                         }
1281                 }
1282         }
1283
1284         /* fix problems with old sessions corrupted by impossible
1285            values for _stretch or _shift
1286         */
1287         if (_stretch == 0.0f) {
1288                 _stretch = 1.0f;
1289         }
1290         
1291         if (_shift == 0.0f) {
1292                 _shift = 1.0f;
1293         }
1294
1295         if (send) {
1296                 send_change (what_changed);
1297         }
1298         
1299         /* Quick fix for 2.x sessions when region is muted */
1300         if ((prop = node.property (X_("flags")))) {
1301                 if (string::npos != prop->value().find("Muted")){
1302                         set_muted (true);
1303                 }
1304         }
1305         
1306
1307         return 0;
1308 }
1309
1310 void
1311 Region::suspend_property_changes ()
1312 {
1313         Stateful::suspend_property_changes ();
1314         _last_length = _length;
1315         _last_position = _position;
1316 }
1317
1318 void
1319 Region::mid_thaw (const PropertyChange& what_changed)
1320 {
1321         if (what_changed.contains (Properties::length)) {
1322                 if (what_changed.contains (Properties::position)) {
1323                         recompute_at_start ();
1324                 }
1325                 recompute_at_end ();
1326         }
1327 }
1328
1329 void
1330 Region::send_change (const PropertyChange& what_changed)
1331 {
1332         if (what_changed.empty()) {
1333                 return;
1334         }
1335
1336         Stateful::send_change (what_changed);
1337
1338         if (!Stateful::frozen()) {
1339                 
1340                 /* Try and send a shared_pointer unless this is part of the constructor.
1341                    If so, do nothing.
1342                 */
1343
1344                 try {
1345                         boost::shared_ptr<Region> rptr = shared_from_this();
1346                         RegionPropertyChanged (rptr, what_changed);
1347                 } catch (...) {
1348                         /* no shared_ptr available, relax; */
1349                 }
1350         }
1351 }
1352
1353 void
1354 Region::set_last_layer_op (uint64_t when)
1355 {
1356         _last_layer_op = when;
1357 }
1358
1359 bool
1360 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1361 {
1362         return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
1363 }
1364
1365 bool
1366 Region::equivalent (boost::shared_ptr<const Region> other) const
1367 {
1368         return _start == other->_start &&
1369                 _position == other->_position &&
1370                 _length == other->_length;
1371 }
1372
1373 bool
1374 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1375 {
1376         return _start == other->_start &&
1377                 _length == other->_length;
1378 }
1379
1380 bool
1381 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1382 {
1383         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1384 }
1385
1386 void
1387 Region::source_deleted (boost::weak_ptr<Source>)
1388 {
1389         drop_sources ();
1390
1391         if (!_session.deletion_in_progress()) {
1392                 /* this is a very special case: at least one of the region's
1393                    sources has bee deleted, so invalidate all references to
1394                    ourselves. Do NOT do this during session deletion, because
1395                    then we run the risk that this will actually result
1396                    in this object being deleted (as refcnt goes to zero)
1397                    while emitting DropReferences.
1398                 */
1399
1400                 drop_references ();
1401         }
1402 }
1403
1404 vector<string>
1405 Region::master_source_names ()
1406 {
1407         SourceList::iterator i;
1408
1409         vector<string> names;
1410         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1411                 names.push_back((*i)->name());
1412         }
1413
1414         return names;
1415 }
1416
1417 void
1418 Region::set_master_sources (const SourceList& srcs)
1419 {
1420         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1421                 (*i)->dec_use_count ();
1422         }
1423
1424         _master_sources = srcs;
1425         assert (_sources.size() == _master_sources.size());
1426
1427         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1428                 (*i)->inc_use_count ();
1429         }
1430 }
1431
1432 bool
1433 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1434 {
1435         if (!other)
1436                 return false;
1437         
1438         if ((_sources.size() != other->_sources.size()) ||
1439             (_master_sources.size() != other->_master_sources.size())) {
1440                 return false;
1441         }
1442
1443         SourceList::const_iterator i;
1444         SourceList::const_iterator io;
1445
1446         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1447                 if ((*i)->id() != (*io)->id()) {
1448                         return false;
1449                 }
1450         }
1451
1452         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1453                 if ((*i)->id() != (*io)->id()) {
1454                         return false;
1455                 }
1456         }
1457
1458         return true;
1459 }
1460
1461 std::string
1462 Region::source_string () const
1463 {
1464         //string res = itos(_sources.size());
1465
1466         stringstream res;
1467         res << _sources.size() << ":";
1468         
1469         SourceList::const_iterator i;
1470
1471         for (i = _sources.begin(); i != _sources.end(); ++i) {
1472                 res << (*i)->id() << ":";
1473         }
1474
1475         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1476                 res << (*i)->id() << ":";
1477         }
1478
1479         return res.str();
1480 }
1481
1482 bool
1483 Region::uses_source (boost::shared_ptr<const Source> source) const
1484 {
1485         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1486                 if (*i == source) {
1487                         return true;
1488                 }
1489         }
1490         return false;
1491 }
1492
1493 bool
1494 Region::uses_source_path (const std::string& path) const
1495 {
1496         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1497                 boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
1498                 if (fs) {
1499                         if (fs->path() == path) {
1500                                 return true;
1501                         }
1502                 }
1503         }
1504         return false;
1505 }
1506
1507 framecnt_t
1508 Region::source_length(uint32_t n) const
1509 {
1510         assert (n < _sources.size());
1511         return _sources[n]->length (_position - _start);
1512 }
1513
1514 bool
1515 Region::verify_length (framecnt_t len)
1516 {
1517         if (source() && (source()->destructive() || source()->length_mutable())) {
1518                 return true;
1519         }
1520
1521         framecnt_t maxlen = 0;
1522
1523         for (uint32_t n = 0; n < _sources.size(); ++n) {
1524                 maxlen = max (maxlen, source_length(n) - _start);
1525         }
1526
1527         len = min (len, maxlen);
1528
1529         return true;
1530 }
1531
1532 bool
1533 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1534 {
1535         if (source() && (source()->destructive() || source()->length_mutable())) {
1536                 return true;
1537         }
1538
1539         framecnt_t maxlen = 0;
1540
1541         for (uint32_t n = 0; n < _sources.size(); ++n) {
1542                 maxlen = max (maxlen, source_length(n) - new_start);
1543         }
1544
1545         new_length = min (new_length, maxlen);
1546
1547         return true;
1548 }
1549
1550 bool
1551 Region::verify_start (framepos_t pos)
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 (pos > source_length(n) - _length) {
1559                         return false;
1560                 }
1561         }
1562         return true;
1563 }
1564
1565 bool
1566 Region::verify_start_mutable (framepos_t& new_start)
1567 {
1568         if (source() && (source()->destructive() || source()->length_mutable())) {
1569                 return true;
1570         }
1571
1572         for (uint32_t n = 0; n < _sources.size(); ++n) {
1573                 if (new_start > source_length(n) - _length) {
1574                         new_start = source_length(n) - _length;
1575                 }
1576         }
1577         return true;
1578 }
1579
1580 boost::shared_ptr<Region>
1581 Region::get_parent() const
1582 {
1583         boost::shared_ptr<Playlist> pl (playlist());
1584
1585         if (pl) {
1586                 boost::shared_ptr<Region> r;
1587                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1588
1589                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1590                         return boost::static_pointer_cast<Region> (r);
1591                 }
1592         }
1593
1594         return boost::shared_ptr<Region>();
1595 }
1596
1597 int
1598 Region::apply (Filter& filter, Progress* progress)
1599 {
1600         return filter.run (shared_from_this(), progress);
1601 }
1602
1603
1604 void
1605 Region::invalidate_transients ()
1606 {
1607         _valid_transients = false;
1608         _transients.clear ();
1609         
1610         send_change (PropertyChange (Properties::valid_transients));
1611 }
1612
1613 void
1614 Region::drop_sources ()
1615 {
1616         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1617                 (*i)->dec_use_count ();
1618         }
1619
1620         _sources.clear ();
1621
1622         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1623                 (*i)->dec_use_count ();
1624         }
1625
1626         _master_sources.clear ();
1627 }
1628
1629 void
1630 Region::use_sources (SourceList const & s)
1631 {
1632         set<boost::shared_ptr<Source> > unique_srcs;
1633
1634         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1635
1636                 _sources.push_back (*i);
1637                 (*i)->inc_use_count ();
1638                 _master_sources.push_back (*i);
1639                 (*i)->inc_use_count ();
1640
1641                 /* connect only once to DropReferences, even if sources are replicated
1642                  */
1643
1644                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1645                         unique_srcs.insert (*i);
1646                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1647                 }
1648         }
1649 }
1650
1651 Trimmable::CanTrim
1652 Region::can_trim () const
1653 {
1654         CanTrim ct = CanTrim (0);
1655
1656         if (locked()) {
1657                 return ct;
1658         }
1659
1660         /* if not locked, we can always move the front later, and the end earlier 
1661          */
1662
1663         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1664
1665         if (start() != 0 || can_trim_start_before_source_start ()) {
1666                 ct = CanTrim (ct | FrontTrimEarlier);
1667         }
1668
1669         if (!_sources.empty()) {
1670                 if ((start() + length()) < _sources.front()->length (0)) {
1671                         ct = CanTrim (ct | EndTrimLater);
1672                 }
1673         }
1674
1675         return ct;
1676 }
1677                       
1678 uint32_t
1679 Region::max_source_level () const
1680 {
1681         uint32_t lvl = 0;
1682
1683         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1684                 lvl = max (lvl, (*i)->level());
1685         }
1686
1687         return lvl;
1688 }
1689
1690 bool
1691 Region::is_compound () const
1692 {
1693         return max_source_level() > 0;
1694 }