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