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