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