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