Merge branch 'master' into cairocanvas
[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_("POSIX"));
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
1288         return 0;
1289 }
1290
1291 void
1292 Region::suspend_property_changes ()
1293 {
1294         Stateful::suspend_property_changes ();
1295         _last_length = _length;
1296         _last_position = _position;
1297 }
1298
1299 void
1300 Region::mid_thaw (const PropertyChange& what_changed)
1301 {
1302         if (what_changed.contains (Properties::length)) {
1303                 if (what_changed.contains (Properties::position)) {
1304                         recompute_at_start ();
1305                 }
1306                 recompute_at_end ();
1307         }
1308 }
1309
1310 void
1311 Region::send_change (const PropertyChange& what_changed)
1312 {
1313         if (what_changed.empty()) {
1314                 return;
1315         }
1316
1317         Stateful::send_change (what_changed);
1318
1319         if (!Stateful::property_changes_suspended()) {
1320
1321                 /* Try and send a shared_pointer unless this is part of the constructor.
1322                    If so, do nothing.
1323                 */
1324
1325                 try {
1326                         boost::shared_ptr<Region> rptr = shared_from_this();
1327                         RegionPropertyChanged (rptr, what_changed);
1328                 } catch (...) {
1329                         /* no shared_ptr available, relax; */
1330                 }
1331         }
1332 }
1333
1334 bool
1335 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
1336 {
1337         return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
1338 }
1339
1340 bool
1341 Region::equivalent (boost::shared_ptr<const Region> other) const
1342 {
1343         return _start == other->_start &&
1344                 _position == other->_position &&
1345                 _length == other->_length;
1346 }
1347
1348 bool
1349 Region::size_equivalent (boost::shared_ptr<const Region> other) const
1350 {
1351         return _start == other->_start &&
1352                 _length == other->_length;
1353 }
1354
1355 bool
1356 Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
1357 {
1358         return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
1359 }
1360
1361 void
1362 Region::source_deleted (boost::weak_ptr<Source>)
1363 {
1364         drop_sources ();
1365
1366         if (!_session.deletion_in_progress()) {
1367                 /* this is a very special case: at least one of the region's
1368                    sources has bee deleted, so invalidate all references to
1369                    ourselves. Do NOT do this during session deletion, because
1370                    then we run the risk that this will actually result
1371                    in this object being deleted (as refcnt goes to zero)
1372                    while emitting DropReferences.
1373                 */
1374
1375                 drop_references ();
1376         }
1377 }
1378
1379 vector<string>
1380 Region::master_source_names ()
1381 {
1382         SourceList::iterator i;
1383
1384         vector<string> names;
1385         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1386                 names.push_back((*i)->name());
1387         }
1388
1389         return names;
1390 }
1391
1392 void
1393 Region::set_master_sources (const SourceList& srcs)
1394 {
1395         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1396                 (*i)->dec_use_count ();
1397         }
1398
1399         _master_sources = srcs;
1400         assert (_sources.size() == _master_sources.size());
1401
1402         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1403                 (*i)->inc_use_count ();
1404         }
1405 }
1406
1407 bool
1408 Region::source_equivalent (boost::shared_ptr<const Region> other) const
1409 {
1410         if (!other)
1411                 return false;
1412
1413         if ((_sources.size() != other->_sources.size()) ||
1414             (_master_sources.size() != other->_master_sources.size())) {
1415                 return false;
1416         }
1417
1418         SourceList::const_iterator i;
1419         SourceList::const_iterator io;
1420
1421         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1422                 if ((*i)->id() != (*io)->id()) {
1423                         return false;
1424                 }
1425         }
1426
1427         for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
1428                 if ((*i)->id() != (*io)->id()) {
1429                         return false;
1430                 }
1431         }
1432
1433         return true;
1434 }
1435
1436 bool
1437 Region::any_source_equivalent (boost::shared_ptr<const Region> other) const
1438 {
1439         if (!other) {
1440                 return false;
1441         }
1442
1443         SourceList::const_iterator i;
1444         SourceList::const_iterator io;
1445
1446         for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
1447                 if ((*i)->id() == (*io)->id()) {
1448                         return true;
1449                 }
1450         }
1451
1452         return false;
1453 }
1454
1455 std::string
1456 Region::source_string () const
1457 {
1458         //string res = itos(_sources.size());
1459
1460         stringstream res;
1461         res << _sources.size() << ":";
1462
1463         SourceList::const_iterator i;
1464
1465         for (i = _sources.begin(); i != _sources.end(); ++i) {
1466                 res << (*i)->id() << ":";
1467         }
1468
1469         for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1470                 res << (*i)->id() << ":";
1471         }
1472
1473         return res.str();
1474 }
1475
1476 bool
1477 Region::uses_source (boost::shared_ptr<const Source> source) const
1478 {
1479         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1480                 if (*i == source) {
1481                         return true;
1482                 }
1483
1484                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1485
1486                 if (ps) {
1487                         if (ps->playlist()->uses_source (source)) {
1488                                 return true;
1489                         }
1490                 }
1491         }
1492
1493         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
1494                 if (*i == source) {
1495                         return true;
1496                 }
1497
1498                 boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
1499
1500                 if (ps) {
1501                         if (ps->playlist()->uses_source (source)) {
1502                                 return true;
1503                         }
1504                 }
1505         }
1506
1507         return false;
1508 }
1509
1510 framecnt_t
1511 Region::source_length(uint32_t n) const
1512 {
1513         assert (n < _sources.size());
1514         return _sources[n]->length (_position - _start);
1515 }
1516
1517 bool
1518 Region::verify_length (framecnt_t len)
1519 {
1520         if (source() && (source()->destructive() || source()->length_mutable())) {
1521                 return true;
1522         }
1523
1524         framecnt_t maxlen = 0;
1525
1526         for (uint32_t n = 0; n < _sources.size(); ++n) {
1527                 maxlen = max (maxlen, source_length(n) - _start);
1528         }
1529
1530         len = min (len, maxlen);
1531
1532         return true;
1533 }
1534
1535 bool
1536 Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
1537 {
1538         if (source() && (source()->destructive() || source()->length_mutable())) {
1539                 return true;
1540         }
1541
1542         framecnt_t maxlen = 0;
1543
1544         for (uint32_t n = 0; n < _sources.size(); ++n) {
1545                 maxlen = max (maxlen, source_length(n) - new_start);
1546         }
1547
1548         new_length = min (new_length, maxlen);
1549
1550         return true;
1551 }
1552
1553 bool
1554 Region::verify_start (framepos_t pos)
1555 {
1556         if (source() && (source()->destructive() || source()->length_mutable())) {
1557                 return true;
1558         }
1559
1560         for (uint32_t n = 0; n < _sources.size(); ++n) {
1561                 if (pos > source_length(n) - _length) {
1562                         return false;
1563                 }
1564         }
1565         return true;
1566 }
1567
1568 bool
1569 Region::verify_start_mutable (framepos_t& new_start)
1570 {
1571         if (source() && (source()->destructive() || source()->length_mutable())) {
1572                 return true;
1573         }
1574
1575         for (uint32_t n = 0; n < _sources.size(); ++n) {
1576                 if (new_start > source_length(n) - _length) {
1577                         new_start = source_length(n) - _length;
1578                 }
1579         }
1580         return true;
1581 }
1582
1583 boost::shared_ptr<Region>
1584 Region::get_parent() const
1585 {
1586         boost::shared_ptr<Playlist> pl (playlist());
1587
1588         if (pl) {
1589                 boost::shared_ptr<Region> r;
1590                 boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
1591
1592                 if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
1593                         return boost::static_pointer_cast<Region> (r);
1594                 }
1595         }
1596
1597         return boost::shared_ptr<Region>();
1598 }
1599
1600 int
1601 Region::apply (Filter& filter, Progress* progress)
1602 {
1603         return filter.run (shared_from_this(), progress);
1604 }
1605
1606
1607 void
1608 Region::invalidate_transients ()
1609 {
1610         _valid_transients = false;
1611         _transients.clear ();
1612
1613         send_change (PropertyChange (Properties::valid_transients));
1614 }
1615
1616 void
1617 Region::drop_sources ()
1618 {
1619         for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
1620                 (*i)->dec_use_count ();
1621         }
1622
1623         _sources.clear ();
1624
1625         for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
1626                 (*i)->dec_use_count ();
1627         }
1628
1629         _master_sources.clear ();
1630 }
1631
1632 void
1633 Region::use_sources (SourceList const & s)
1634 {
1635         set<boost::shared_ptr<Source> > unique_srcs;
1636
1637         for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
1638
1639                 _sources.push_back (*i);
1640                 (*i)->inc_use_count ();
1641                 _master_sources.push_back (*i);
1642                 (*i)->inc_use_count ();
1643
1644                 /* connect only once to DropReferences, even if sources are replicated
1645                  */
1646
1647                 if (unique_srcs.find (*i) == unique_srcs.end ()) {
1648                         unique_srcs.insert (*i);
1649                         (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
1650                 }
1651         }
1652 }
1653
1654 Trimmable::CanTrim
1655 Region::can_trim () const
1656 {
1657         CanTrim ct = CanTrim (0);
1658
1659         if (locked()) {
1660                 return ct;
1661         }
1662
1663         /* if not locked, we can always move the front later, and the end earlier
1664          */
1665
1666         ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
1667
1668         if (start() != 0 || can_trim_start_before_source_start ()) {
1669                 ct = CanTrim (ct | FrontTrimEarlier);
1670         }
1671
1672         if (!_sources.empty()) {
1673                 if ((start() + length()) < _sources.front()->length (0)) {
1674                         ct = CanTrim (ct | EndTrimLater);
1675                 }
1676         }
1677
1678         return ct;
1679 }
1680
1681 uint32_t
1682 Region::max_source_level () const
1683 {
1684         uint32_t lvl = 0;
1685
1686         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1687                 lvl = max (lvl, (*i)->level());
1688         }
1689
1690         return lvl;
1691 }
1692
1693 bool
1694 Region::is_compound () const
1695 {
1696         return max_source_level() > 0;
1697 }
1698
1699 void
1700 Region::post_set (const PropertyChange& pc)
1701 {
1702         if (pc.contains (Properties::position)) {
1703                 recompute_position_from_lock_style ();
1704         }
1705 }
1706
1707 void
1708 Region::set_start_internal (framecnt_t s)
1709 {
1710         _start = s;
1711 }
1712
1713 framepos_t
1714 Region::earliest_possible_position () const
1715 {
1716         if (_start > _position) {
1717                 return 0;
1718         } else {
1719                 return _position - _start;
1720         }
1721 }
1722
1723 framecnt_t
1724 Region::latest_possible_frame () const
1725 {
1726         framecnt_t minlen = max_framecnt;
1727
1728         for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
1729                 /* non-audio regions have a length that may vary based on their
1730                  * position, so we have to pass it in the call.
1731                  */
1732                 minlen = min (minlen, (*i)->length (_position));
1733         }
1734
1735         /* the latest possible last frame is determined by the current
1736          * position, plus the shortest source extent past _start.
1737          */
1738
1739         return _position + (minlen - _start) - 1;
1740 }
1741