fix region "valid-transient" property
[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 void
595 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
596 {
597         /* We emit a change of Properties::position even if the position hasn't changed
598            (see Region::set_position), so we must always set this up so that
599            e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
600         */
601         _last_position = _position;
602
603         if (_position != pos) {
604                 _position = pos;
605
606                 /* check that the new _position wouldn't make the current
607                    length impossible - if so, change the length.
608
609                    XXX is this the right thing to do?
610                 */
611
612                 if (max_framepos - _length < _position) {
613                         _last_length = _length;
614                         _length = max_framepos - _position;
615                 }
616
617                 if (allow_bbt_recompute) {
618                         recompute_position_from_lock_style ();
619                 }
620
621                 //invalidate_transients ();
622         }
623 }
624
625 void
626 Region::recompute_position_from_lock_style ()
627 {
628         if (_position_lock_style == MusicTime) {
629                 _session.bbt_time (_position, _bbt_time);
630         }
631 }
632
633 void
634 Region::nudge_position (frameoffset_t n)
635 {
636         if (locked() || video_locked()) {
637                 return;
638         }
639
640         if (n == 0) {
641                 return;
642         }
643
644         framepos_t new_position = _position;
645
646         if (n > 0) {
647                 if (_position > max_framepos - n) {
648                         new_position = max_framepos;
649                 } else {
650                         new_position += n;
651                 }
652         } else {
653                 if (_position < -n) {
654                         new_position = 0;
655                 } else {
656                         new_position += n;
657                 }
658         }
659
660         set_position_internal (new_position, true);
661
662         send_change (Properties::position);
663 }
664
665 void
666 Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
667 {
668         _ancestral_length = l;
669         _ancestral_start = s;
670         _stretch = st;
671         _shift = sh;
672 }
673
674 void
675 Region::set_start (framepos_t pos)
676 {
677         if (locked() || position_locked() || video_locked()) {
678                 return;
679         }
680         /* This just sets the start, nothing else. It effectively shifts
681            the contents of the Region within the overall extent of the Source,
682            without changing the Region's position or length
683         */
684
685         if (_start != pos) {
686
687                 if (!verify_start (pos)) {
688                         return;
689                 }
690
691                 set_start_internal (pos);
692                 _whole_file = false;
693                 first_edit ();
694                 invalidate_transients ();
695
696                 send_change (Properties::start);
697         }
698 }
699
700 void
701 Region::move_start (frameoffset_t distance)
702 {
703         if (locked() || position_locked() || video_locked()) {
704                 return;
705         }
706
707         framepos_t new_start;
708
709         if (distance > 0) {
710
711                 if (_start > max_framepos - distance) {
712                         new_start = max_framepos; // makes no sense
713                 } else {
714                         new_start = _start + distance;
715                 }
716
717                 if (!verify_start (new_start)) {
718                         return;
719                 }
720
721         } else if (distance < 0) {
722
723                 if (_start < -distance) {
724                         new_start = 0;
725                 } else {
726                         new_start = _start + distance;
727                 }
728
729         } else {
730                 return;
731         }
732
733         if (new_start == _start) {
734                 return;
735         }
736
737         set_start_internal (new_start);
738
739         _whole_file = false;
740         first_edit ();
741
742         send_change (Properties::start);
743 }
744
745 void
746 Region::trim_front (framepos_t new_position)
747 {
748         modify_front (new_position, false);
749 }
750
751 void
752 Region::cut_front (framepos_t new_position)
753 {
754         modify_front (new_position, true);
755 }
756
757 void
758 Region::cut_end (framepos_t new_endpoint)
759 {
760         modify_end (new_endpoint, true);
761 }
762
763 void
764 Region::modify_front (framepos_t new_position, bool reset_fade)
765 {
766         if (locked()) {
767                 return;
768         }
769
770         framepos_t end = last_frame();
771         framepos_t source_zero;
772
773         if (_position > _start) {
774                 source_zero = _position - _start;
775         } else {
776                 source_zero = 0; // its actually negative, but this will work for us
777         }
778
779         if (new_position < end) { /* can't trim it zero or negative length */
780
781                 framecnt_t newlen = 0;
782                 framepos_t delta = 0;
783
784                 if (!can_trim_start_before_source_start ()) {
785                         /* can't trim it back past where source position zero is located */
786                         new_position = max (new_position, source_zero);
787                 }
788
789                 if (new_position > _position) {
790                         newlen = _length - (new_position - _position);
791                         delta = -1 * (new_position - _position);
792                 } else {
793                         newlen = _length + (_position - new_position);
794                         delta = _position - new_position;
795                 }
796
797                 trim_to_internal (new_position, newlen);
798
799                 if (reset_fade) {
800                         _right_of_split = true;
801                 }
802
803                 if (!property_changes_suspended()) {
804                         recompute_at_start ();
805                 }
806
807                 if (_transients.size() > 0){
808                         adjust_transients(delta);
809                 }
810         }
811 }
812
813 void
814 Region::modify_end (framepos_t new_endpoint, bool reset_fade)
815 {
816         if (locked()) {
817                 return;
818         }
819
820         if (new_endpoint > _position) {
821                 trim_to_internal (_position, new_endpoint - _position);
822                 if (reset_fade) {
823                         _left_of_split = true;
824                 }
825                 if (!property_changes_suspended()) {
826                         recompute_at_end ();
827                 }
828         }
829 }
830
831 /** @param new_endpoint New region end point, such that, for example,
832  *  a region at 0 of length 10 has an endpoint of 9.
833  */
834
835 void
836 Region::trim_end (framepos_t new_endpoint)
837 {
838         modify_end (new_endpoint, false);
839 }
840
841 void
842 Region::trim_to (framepos_t position, framecnt_t length)
843 {
844         if (locked()) {
845                 return;
846         }
847
848         trim_to_internal (position, length);
849
850         if (!property_changes_suspended()) {
851                 recompute_at_start ();
852                 recompute_at_end ();
853         }
854 }
855
856 void
857 Region::trim_to_internal (framepos_t position, framecnt_t length)
858 {
859         framepos_t new_start;
860
861         if (locked()) {
862                 return;
863         }
864
865         frameoffset_t const start_shift = position - _position;
866
867         if (start_shift > 0) {
868
869                 if (_start > max_framepos - start_shift) {
870                         new_start = max_framepos;
871                 } else {
872                         new_start = _start + start_shift;
873                 }
874
875         } else if (start_shift < 0) {
876
877                 if (_start < -start_shift && !can_trim_start_before_source_start ()) {
878                         new_start = 0;
879                 } else {
880                         new_start = _start + start_shift;
881                 }
882
883         } else {
884                 new_start = _start;
885         }
886
887         if (!verify_start_and_length (new_start, length)) {
888                 return;
889         }
890
891         PropertyChange what_changed;
892
893         if (_start != new_start) {
894                 set_start_internal (new_start);
895                 what_changed.add (Properties::start);
896         }
897
898         /* Set position before length, otherwise for MIDI regions this bad thing happens:
899          * 1. we call set_length_internal; length in beats is computed using the region's current
900          *    (soon-to-be old) position
901          * 2. we call set_position_internal; position is set and length in frames re-computed using
902          *    length in beats from (1) but at the new position, which is wrong if the region
903          *    straddles a tempo/meter change.
904          */
905
906         if (_position != position) {
907                 if (!property_changes_suspended()) {
908                         _last_position = _position;
909                 }
910                 set_position_internal (position, true);
911                 what_changed.add (Properties::position);
912         }
913
914         if (_length != length) {
915                 if (!property_changes_suspended()) {
916                         _last_length = _length;
917                 }
918                 set_length_internal (length);
919                 what_changed.add (Properties::length);
920         }
921
922         _whole_file = false;
923
924         PropertyChange start_and_length;
925
926         start_and_length.add (Properties::start);
927         start_and_length.add (Properties::length);
928
929         if (what_changed.contains (start_and_length)) {
930                 first_edit ();
931         }
932
933         if (!what_changed.empty()) {
934                 send_change (what_changed);
935         }
936 }
937
938 void
939 Region::set_hidden (bool yn)
940 {
941         if (hidden() != yn) {
942                 _hidden = yn;
943                 send_change (Properties::hidden);
944         }
945 }
946
947 void
948 Region::set_whole_file (bool yn)
949 {
950         _whole_file = yn;
951         /* no change signal */
952 }
953
954 void
955 Region::set_automatic (bool yn)
956 {
957         _automatic = yn;
958         /* no change signal */
959 }
960
961 void
962 Region::set_muted (bool yn)
963 {
964         if (muted() != yn) {
965                 _muted = yn;
966                 send_change (Properties::muted);
967         }
968 }
969
970 void
971 Region::set_opaque (bool yn)
972 {
973         if (opaque() != yn) {
974                 _opaque = yn;
975                 send_change (Properties::opaque);
976         }
977 }
978
979 void
980 Region::set_locked (bool yn)
981 {
982         if (locked() != yn) {
983                 _locked = yn;
984                 send_change (Properties::locked);
985         }
986 }
987
988 void
989 Region::set_video_locked (bool yn)
990 {
991         if (video_locked() != yn) {
992                 _video_locked = yn;
993                 send_change (Properties::video_locked);
994         }
995 }
996
997 void
998 Region::set_position_locked (bool yn)
999 {
1000         if (position_locked() != yn) {
1001                 _position_locked = yn;
1002                 send_change (Properties::locked);
1003         }
1004 }
1005
1006 /** Set the region's sync point.
1007  *  @param absolute_pos Session time.
1008  */
1009 void
1010 Region::set_sync_position (framepos_t absolute_pos)
1011 {
1012         /* position within our file */
1013         framepos_t const file_pos = _start + (absolute_pos - _position);
1014
1015         if (file_pos != _sync_position) {
1016                 _sync_marked = true;
1017                 _sync_position = file_pos;
1018                 if (!property_changes_suspended()) {
1019                         maybe_uncopy ();
1020                 }
1021
1022                 send_change (Properties::sync_position);
1023         }
1024 }
1025
1026 void
1027 Region::clear_sync_position ()
1028 {
1029         if (sync_marked()) {
1030                 _sync_marked = false;
1031                 if (!property_changes_suspended()) {
1032                         maybe_uncopy ();
1033                 }
1034
1035                 send_change (Properties::sync_position);
1036         }
1037 }
1038
1039 /* @return the sync point relative the first frame of the region */
1040 frameoffset_t
1041 Region::sync_offset (int& dir) const
1042 {
1043         if (sync_marked()) {
1044                 if (_sync_position > _start) {
1045                         dir = 1;
1046                         return _sync_position - _start;
1047                 } else {
1048                         dir = -1;
1049                         return _start - _sync_position;
1050                 }
1051         } else {
1052                 dir = 0;
1053                 return 0;
1054         }
1055 }
1056
1057 framepos_t
1058 Region::adjust_to_sync (framepos_t pos) const
1059 {
1060         int sync_dir;
1061         frameoffset_t offset = sync_offset (sync_dir);
1062
1063         // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
1064
1065         if (sync_dir > 0) {
1066                 if (pos > offset) {
1067                         pos -= offset;
1068                 } else {
1069                         pos = 0;
1070                 }
1071         } else {
1072                 if (max_framepos - pos > offset) {
1073                         pos += offset;
1074                 }
1075         }
1076
1077         return pos;
1078 }
1079
1080 /** @return Sync position in session time */
1081 framepos_t
1082 Region::sync_position() const
1083 {
1084         if (sync_marked()) {
1085                 return _position - _start + _sync_position;
1086         } else {
1087                 /* if sync has not been marked, use the start of the region */
1088                 return _position;
1089         }
1090 }
1091
1092 void
1093 Region::raise ()
1094 {
1095         boost::shared_ptr<Playlist> pl (playlist());
1096         if (pl) {
1097                 pl->raise_region (shared_from_this ());
1098         }
1099 }
1100
1101 void
1102 Region::lower ()
1103 {
1104         boost::shared_ptr<Playlist> pl (playlist());
1105         if (pl) {
1106                 pl->lower_region (shared_from_this ());
1107         }
1108 }
1109
1110
1111 void
1112 Region::raise_to_top ()
1113 {
1114         boost::shared_ptr<Playlist> pl (playlist());
1115         if (pl) {
1116                 pl->raise_region_to_top (shared_from_this());
1117         }
1118 }
1119
1120 void
1121 Region::lower_to_bottom ()
1122 {
1123         boost::shared_ptr<Playlist> pl (playlist());
1124         if (pl) {
1125                 pl->lower_region_to_bottom (shared_from_this());
1126         }
1127 }
1128
1129 void
1130 Region::set_layer (layer_t l)
1131 {
1132         _layer = l;
1133 }
1134
1135 XMLNode&
1136 Region::state ()
1137 {
1138         XMLNode *node = new XMLNode ("Region");
1139         char buf[64];
1140         char buf2[64];
1141         LocaleGuard lg (X_("C"));
1142         const char* fe = NULL;
1143
1144         /* custom version of 'add_properties (*node);'
1145          * skip values that have have dedicated save functions
1146          * in AudioRegion::state()
1147          */
1148         for (OwnedPropertyList::iterator i = _properties->begin(); i != _properties->end(); ++i) {
1149                 if (!strcmp(i->second->property_name(), (const char*)"Envelope")) continue;
1150                 if (!strcmp(i->second->property_name(), (const char*)"FadeIn")) continue;
1151                 if (!strcmp(i->second->property_name(), (const char*)"FadeOut")) continue;
1152                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeIn")) continue;
1153                 if (!strcmp(i->second->property_name(), (const char*)"InverseFadeOut")) continue;
1154                 i->second->get_value (*node);
1155         }
1156
1157         id().print (buf, sizeof (buf));
1158         node->add_property ("id", buf);
1159         node->add_property ("type", _type.to_string());
1160
1161         switch (_first_edit) {
1162         case EditChangesNothing:
1163                 fe = X_("nothing");
1164                 break;
1165         case EditChangesName:
1166                 fe = X_("name");
1167                 break;
1168         case EditChangesID:
1169                 fe = X_("id");
1170                 break;
1171         default: /* should be unreachable but makes g++ happy */
1172                 fe = X_("nothing");
1173                 break;
1174         }
1175
1176         node->add_property ("first-edit", fe);
1177
1178         /* note: flags are stored by derived classes */
1179
1180         if (_position_lock_style != AudioTime) {
1181                 stringstream str;
1182                 str << _bbt_time;
1183                 node->add_property ("bbt-position", str.str());
1184         }
1185
1186         for (uint32_t n=0; n < _sources.size(); ++n) {
1187                 snprintf (buf2, sizeof(buf2), "source-%d", n);
1188                 _sources[n]->id().print (buf, sizeof(buf));
1189                 node->add_property (buf2, buf);
1190         }
1191
1192         for (uint32_t n=0; n < _master_sources.size(); ++n) {
1193                 snprintf (buf2, sizeof(buf2), "master-source-%d", n);
1194                 _master_sources[n]->id().print (buf, sizeof (buf));
1195                 node->add_property (buf2, buf);
1196         }
1197
1198         /* Only store nested sources for the whole-file region that acts
1199            as the parent/root of all regions using it.
1200         */
1201
1202         if (_whole_file && max_source_level() > 0) {
1203
1204                 XMLNode* nested_node = new XMLNode (X_("NestedSource"));
1205
1206                 /* region is compound - get its playlist and
1207                    store that before we list the region that
1208                    needs it ...
1209                 */
1210
1211                 for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
1212                         nested_node->add_child_nocopy ((*s)->get_state ());
1213                 }
1214
1215                 if (nested_node) {
1216                         node->add_child_nocopy (*nested_node);
1217                 }
1218         }
1219
1220         if (_extra_xml) {
1221                 node->add_child_copy (*_extra_xml);
1222         }
1223
1224         return *node;
1225 }
1226
1227 XMLNode&
1228 Region::get_state ()
1229 {
1230         return state ();
1231 }
1232
1233 int
1234 Region::set_state (const XMLNode& node, int version)
1235 {
1236         PropertyChange what_changed;
1237         return _set_state (node, version, what_changed, true);
1238 }
1239
1240 int
1241 Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
1242 {
1243         const XMLProperty* prop;
1244
1245         Stateful::save_extra_xml (node);
1246
1247         what_changed = set_values (node);
1248
1249         set_id (node);
1250
1251         if (_position_lock_style == MusicTime) {
1252                 if ((prop = node.property ("bbt-position")) == 0) {
1253                         /* missing BBT info, revert to audio time locking */
1254                         _position_lock_style = AudioTime;
1255                 } else {
1256                         if (sscanf (prop->value().c_str(), "%d|%d|%d",
1257                                     &_bbt_time.bars,
1258                                     &_bbt_time.beats,
1259                                     &_bbt_time.ticks) != 3) {
1260                                 _position_lock_style = AudioTime;
1261                         }
1262                 }
1263         }
1264
1265         /* fix problems with old sessions corrupted by impossible
1266            values for _stretch or _shift
1267         */
1268         if (_stretch == 0.0f) {
1269                 _stretch = 1.0f;
1270         }
1271
1272         if (_shift == 0.0f) {
1273                 _shift = 1.0f;
1274         }
1275
1276         if (send) {
1277                 send_change (what_changed);
1278         }
1279
1280         /* Quick fix for 2.x sessions when region is muted */
1281         if ((prop = node.property (X_("flags")))) {
1282                 if (string::npos != prop->value().find("Muted")){
1283                         set_muted (true);
1284                 }
1285         }
1286
1287         // saved property is invalid, region-transients are not saved
1288         if (_transients.size() == 0){
1289                 _valid_transients = false;
1290         }
1291
1292         return 0;
1293 }
1294
1295 void
1296 Region::suspend_property_changes ()
1297 {
1298         Stateful::suspend_property_changes ();
1299         _last_length = _length;
1300         _last_position = _position;
1301 }
1302
1303 void
1304 Region::mid_thaw (const PropertyChange& what_changed)
1305 {
1306         if (what_changed.contains (Properties::length)) {
1307                 if (what_changed.contains (Properties::position)) {
1308                         recompute_at_start ();
1309                 }
1310                 recompute_at_end ();
1311         }
1312 }
1313
1314 void
1315 Region::send_change (const PropertyChange& what_changed)
1316 {
1317         if (what_changed.empty()) {
1318                 return;
1319         }
1320
1321         Stateful::send_change (what_changed);
1322
1323         if (!Stateful::property_changes_suspended()) {
1324
1325                 /* Try and send a shared_pointer unless this is part of the constructor.
1326                    If so, do nothing.
1327                 */
1328
1329                 try {
1330                         boost::shared_ptr<Region> rptr = shared_from_this();
1331                         RegionPropertyChanged (rptr, what_changed);
1332                 } catch (...) {
1333                         /* no shared_ptr available, relax; */
1334                 }
1335         }
1336 }
1337
1338 bool
1339 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1340 {
1341         return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1342 }
1343
1344 bool
1345 Region::equivalent (boost::shared_ptr<const Region> other) const
1346 {
1347         return _start == other->_start &&
1348                 _position == other->_position &&
1349                 _length == other->_length;
1350 }
1351
1352 bool
1353 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1354 {
1355         return _start == other->_start &&
1356                 _length == other->_length;
1357 }
1358
1359 bool
1360 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1361 {
1362         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1363 }
1364
1365 void
1366 Region::source_deleted (boost::weak_ptr<Source>)
1367 {
1368         drop_sources ();
1369
1370         if (!_session.deletion_in_progress()) {
1371                 /* this is a very special case: at least one of the region's
1372                    sources has bee deleted, so invalidate all references to
1373                    ourselves. Do NOT do this during session deletion, because
1374                    then we run the risk that this will actually result
1375                    in this object being deleted (as refcnt goes to zero)
1376                    while emitting DropReferences.
1377                 */
1378
1379                 drop_references ();
1380         }
1381 }
1382
1383 vector<string>
1384 Region::master_source_names ()
1385 {
1386         SourceList::iterator i;
1387
1388         vector<string> names;
1389         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1390                 names.push_back((*i)->name());
1391         }
1392
1393         return names;
1394 }
1395
1396 void
1397 Region::set_master_sources (const SourceList& srcs)
1398 {
1399         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1400                 (*i)->dec_use_count ();
1401         }
1402
1403         _master_sources = srcs;
1404         assert (_sources.size() == _master_sources.size());
1405
1406         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1407                 (*i)->inc_use_count ();
1408         }
1409 }
1410
1411 bool
1412 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1413 {
1414         if (!other)
1415                 return false;
1416
1417         if ((_sources.size() != other->_sources.size()) ||
1418             (_master_sources.size() != other->_master_sources.size())) {
1419                 return false;
1420         }
1421
1422         SourceList::const_iterator i;
1423         SourceList::const_iterator io;
1424
1425         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1426                 if ((*i)->id() != (*io)->id()) {
1427                         return false;
1428                 }
1429         }
1430
1431         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1432                 if ((*i)->id() != (*io)->id()) {
1433                         return false;
1434                 }
1435         }
1436
1437         return true;
1438 }
1439
1440 bool
1441 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1442 {
1443         if (!other) {
1444                 return false;
1445         }
1446
1447         SourceList::const_iterator i;
1448         SourceList::const_iterator io;
1449
1450         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1451                 if ((*i)->id() == (*io)->id()) {
1452                         return true;
1453                 }
1454         }
1455
1456         return false;
1457 }
1458
1459 std::string
1460 Region::source_string () const
1461 {
1462         //string res = itos(_sources.size());
1463
1464         stringstream res;
1465         res << _sources.size() << ":";
1466
1467         SourceList::const_iterator i;
1468
1469         for (i = _sources.begin(); i != _sources.end(); ++i) {
1470                 res << (*i)->id() << ":";
1471         }
1472
1473         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1474                 res << (*i)->id() << ":";
1475         }
1476
1477         return res.str();
1478 }
1479
1480 bool
1481 Region::uses_source (boost::shared_ptr<const Source> source) const
1482 {
1483         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1484                 if (*i == source) {
1485                         return true;
1486                 }
1487
1488                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1489
1490                 if (ps) {
1491                         if (ps->playlist()->uses_source (source)) {
1492                                 return true;
1493                         }
1494                 }
1495         }
1496
1497         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1498                 if (*i == source) {
1499                         return true;
1500                 }
1501
1502                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1503
1504                 if (ps) {
1505                         if (ps->playlist()->uses_source (source)) {
1506                                 return true;
1507                         }
1508                 }
1509         }
1510
1511         return false;
1512 }
1513
1514 framecnt_t
1515 Region::source_length(uint32_t n) const
1516 {
1517         assert (n < _sources.size());
1518         return _sources[n]->length (_position - _start);
1519 }
1520
1521 bool
1522 Region::verify_length (framecnt_t& len)
1523 {
1524         if (source() && (source()->destructive() || source()->length_mutable())) {
1525                 return true;
1526         }
1527
1528         framecnt_t maxlen = 0;
1529
1530         for (uint32_t n = 0; n < _sources.size(); ++n) {
1531                 maxlen = max (maxlen, source_length(n) - _start);
1532         }
1533
1534         len = min (len, maxlen);
1535
1536         return true;
1537 }
1538
1539 bool
1540 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
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) - new_start);
1550         }
1551
1552         new_length = min (new_length, maxlen);
1553
1554         return true;
1555 }
1556
1557 bool
1558 Region::verify_start (framepos_t pos)
1559 {
1560         if (source() && (source()->destructive() || source()->length_mutable())) {
1561                 return true;
1562         }
1563
1564         for (uint32_t n = 0; n < _sources.size(); ++n) {
1565                 if (pos > source_length(n) - _length) {
1566                         return false;
1567                 }
1568         }
1569         return true;
1570 }
1571
1572 bool
1573 Region::verify_start_mutable (framepos_t& new_start)
1574 {
1575         if (source() && (source()->destructive() || source()->length_mutable())) {
1576                 return true;
1577         }
1578
1579         for (uint32_t n = 0; n < _sources.size(); ++n) {
1580                 if (new_start > source_length(n) - _length) {
1581                         new_start = source_length(n) - _length;
1582                 }
1583         }
1584         return true;
1585 }
1586
1587 boost::shared_ptr<Region>
1588 Region::get_parent() const
1589 {
1590         boost::shared_ptr<Playlist> pl (playlist());
1591
1592         if (pl) {
1593                 boost::shared_ptr<Region> r;
1594                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1595
1596                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1597                         return boost::static_pointer_cast<Region> (r);
1598                 }
1599         }
1600
1601         return boost::shared_ptr<Region>();
1602 }
1603
1604 int
1605 Region::apply (Filter& filter, Progress* progress)
1606 {
1607         return filter.run (shared_from_this(), progress);
1608 }
1609
1610
1611 void
1612 Region::invalidate_transients ()
1613 {
1614         _valid_transients = false;
1615         _transients.clear ();
1616
1617         send_change (PropertyChange (Properties::valid_transients));
1618 }
1619
1620 void
1621 Region::drop_sources ()
1622 {
1623         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1624                 (*i)->dec_use_count ();
1625         }
1626
1627         _sources.clear ();
1628
1629         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1630                 (*i)->dec_use_count ();
1631         }
1632
1633         _master_sources.clear ();
1634 }
1635
1636 void
1637 Region::use_sources (SourceList const & s)
1638 {
1639         set<boost::shared_ptr<Source> > unique_srcs;
1640
1641         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1642
1643                 _sources.push_back (*i);
1644                 (*i)->inc_use_count ();
1645                 _master_sources.push_back (*i);
1646                 (*i)->inc_use_count ();
1647
1648                 /* connect only once to DropReferences, even if sources are replicated
1649                  */
1650
1651                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1652                         unique_srcs.insert (*i);
1653                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1654                 }
1655         }
1656 }
1657
1658 Trimmable::CanTrim
1659 Region::can_trim () const
1660 {
1661         CanTrim ct = CanTrim (0);
1662
1663         if (locked()) {
1664                 return ct;
1665         }
1666
1667         /* if not locked, we can always move the front later, and the end earlier
1668          */
1669
1670         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1671
1672         if (start() != 0 || can_trim_start_before_source_start ()) {
1673                 ct = CanTrim (ct | FrontTrimEarlier);
1674         }
1675
1676         if (!_sources.empty()) {
1677                 if ((start() + length()) < _sources.front()->length (0)) {
1678                         ct = CanTrim (ct | EndTrimLater);
1679                 }
1680         }
1681
1682         return ct;
1683 }
1684
1685 uint32_t
1686 Region::max_source_level () const
1687 {
1688         uint32_t lvl = 0;
1689
1690         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1691                 lvl = max (lvl, (*i)->level());
1692         }
1693
1694         return lvl;
1695 }
1696
1697 bool
1698 Region::is_compound () const
1699 {
1700         return max_source_level() > 0;
1701 }
1702
1703 void
1704 Region::post_set (const PropertyChange& pc)
1705 {
1706         if (pc.contains (Properties::position)) {
1707                 recompute_position_from_lock_style ();
1708         }
1709 }
1710
1711 void
1712 Region::set_start_internal (framecnt_t s)
1713 {
1714         _start = s;
1715 }
1716
1717 framepos_t
1718 Region::earliest_possible_position () const
1719 {
1720         if (_start > _position) {
1721                 return 0;
1722         } else {
1723                 return _position - _start;
1724         }
1725 }
1726
1727 framecnt_t
1728 Region::latest_possible_frame () const
1729 {
1730         framecnt_t minlen = max_framecnt;
1731
1732         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1733                 /* non-audio regions have a length that may vary based on their
1734                  * position, so we have to pass it in the call.
1735                  */
1736                 minlen = min (minlen, (*i)->length (_position));
1737         }
1738
1739         /* the latest possible last frame is determined by the current
1740          * position, plus the shortest source extent past _start.
1741          */
1742
1743         return _position + (minlen - _start) - 1;
1744 }
1745