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