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