Fix broken whitespace. I'd apologize for the compile times if it was my fault :D
[ardour.git] / libs / ardour / region.cc
index 018e19d60b5e7f2ef9a66744e0968a142af35e11..17698d88a75f22fd1f223a581816ceea7933f973 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2000-2003 Paul Davis 
+    Copyright (C) 2000-2003 Paul Davis
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <algorithm>
 #include <sstream>
 
-#include <sigc++/bind.h>
-#include <sigc++/class_slot.h>
-
 #include <glibmm/thread.h>
-#include <pbd/xml++.h>
-#include <pbd/stacktrace.h>
-#include <pbd/enumwriter.h>
-
-#include <ardour/region.h>
-#include <ardour/playlist.h>
-#include <ardour/session.h>
-#include <ardour/source.h>
-#include <ardour/tempo.h>
-#include <ardour/region_factory.h>
-#include <ardour/filter.h>
-#include <ardour/profile.h>
+#include "pbd/xml++.h"
+#include "pbd/stacktrace.h"
+#include "pbd/enumwriter.h"
+
+#include "ardour/debug.h"
+#include "ardour/file_source.h"
+#include "ardour/filter.h"
+#include "ardour/playlist.h"
+#include "ardour/profile.h"
+#include "ardour/region.h"
+#include "ardour/region_factory.h"
+#include "ardour/session.h"
+#include "ardour/source.h"
+#include "ardour/source_factory.h"
+#include "ardour/tempo.h"
+#include "ardour/utils.h"
 
 #include "i18n.h"
 
@@ -46,403 +47,376 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
-Change Region::FadeChanged       = ARDOUR::new_change ();
-Change Region::SyncOffsetChanged = ARDOUR::new_change ();
-Change Region::MuteChanged       = ARDOUR::new_change ();
-Change Region::OpacityChanged    = ARDOUR::new_change ();
-Change Region::LockChanged       = ARDOUR::new_change ();
-Change Region::LayerChanged      = ARDOUR::new_change ();
-Change Region::HiddenChanged     = ARDOUR::new_change ();
+namespace ARDOUR {
+       namespace Properties {
+               PBD::PropertyDescriptor<bool> muted;
+               PBD::PropertyDescriptor<bool> opaque;
+               PBD::PropertyDescriptor<bool> locked;
+               PBD::PropertyDescriptor<bool> automatic;
+               PBD::PropertyDescriptor<bool> whole_file;
+               PBD::PropertyDescriptor<bool> import;
+               PBD::PropertyDescriptor<bool> external;
+               PBD::PropertyDescriptor<bool> sync_marked;
+               PBD::PropertyDescriptor<bool> left_of_split;
+               PBD::PropertyDescriptor<bool> right_of_split;
+               PBD::PropertyDescriptor<bool> hidden;
+               PBD::PropertyDescriptor<bool> position_locked;
+               PBD::PropertyDescriptor<bool> valid_transients;
+               PBD::PropertyDescriptor<framepos_t> start;
+               PBD::PropertyDescriptor<framecnt_t> length;
+               PBD::PropertyDescriptor<framepos_t> position;
+               PBD::PropertyDescriptor<framecnt_t> sync_position;
+               PBD::PropertyDescriptor<layer_t> layer;
+               PBD::PropertyDescriptor<framepos_t> ancestral_start;
+               PBD::PropertyDescriptor<framecnt_t> ancestral_length;
+               PBD::PropertyDescriptor<float> stretch;
+               PBD::PropertyDescriptor<float> shift;
+               PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
+       }
+}
+
+PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
+
+void
+Region::make_property_quarks ()
+{
+       Properties::muted.property_id = g_quark_from_static_string (X_("muted"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for muted = %1\n",       Properties::muted.property_id));
+       Properties::opaque.property_id = g_quark_from_static_string (X_("opaque"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for opaque = %1\n",      Properties::opaque.property_id));
+       Properties::locked.property_id = g_quark_from_static_string (X_("locked"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for locked = %1\n",      Properties::locked.property_id));
+       Properties::automatic.property_id = g_quark_from_static_string (X_("automatic"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for automatic = %1\n",   Properties::automatic.property_id));
+       Properties::whole_file.property_id = g_quark_from_static_string (X_("whole-file"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for whole-file = %1\n",  Properties::whole_file.property_id));
+       Properties::import.property_id = g_quark_from_static_string (X_("import"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for import = %1\n",      Properties::import.property_id));
+       Properties::external.property_id = g_quark_from_static_string (X_("external"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for external = %1\n",    Properties::external.property_id));
+       Properties::sync_marked.property_id = g_quark_from_static_string (X_("sync-marked"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-marked = %1\n",         Properties::sync_marked.property_id));
+       Properties::left_of_split.property_id = g_quark_from_static_string (X_("left-of-split"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for left-of-split = %1\n",       Properties::left_of_split.property_id));
+       Properties::right_of_split.property_id = g_quark_from_static_string (X_("right-of-split"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for right-of-split = %1\n",      Properties::right_of_split.property_id));
+       Properties::hidden.property_id = g_quark_from_static_string (X_("hidden"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n",      Properties::hidden.property_id));
+       Properties::position_locked.property_id = g_quark_from_static_string (X_("position-locked"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position-locked = %1\n",     Properties::position_locked.property_id));
+       Properties::valid_transients.property_id = g_quark_from_static_string (X_("valid-transients"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for valid-transients = %1\n",    Properties::valid_transients.property_id));
+       Properties::start.property_id = g_quark_from_static_string (X_("start"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start = %1\n",       Properties::start.property_id));
+       Properties::length.property_id = g_quark_from_static_string (X_("length"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length = %1\n",      Properties::length.property_id));
+       Properties::position.property_id = g_quark_from_static_string (X_("position"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position = %1\n",    Properties::position.property_id));
+       Properties::sync_position.property_id = g_quark_from_static_string (X_("sync-position"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for sync-position = %1\n",       Properties::sync_position.property_id));
+       Properties::layer.property_id = g_quark_from_static_string (X_("layer"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layer = %1\n",       Properties::layer.property_id));
+       Properties::ancestral_start.property_id = g_quark_from_static_string (X_("ancestral-start"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-start = %1\n",     Properties::ancestral_start.property_id));
+       Properties::ancestral_length.property_id = g_quark_from_static_string (X_("ancestral-length"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for ancestral-length = %1\n",    Properties::ancestral_length.property_id));
+       Properties::stretch.property_id = g_quark_from_static_string (X_("stretch"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for stretch = %1\n",     Properties::stretch.property_id));
+       Properties::shift.property_id = g_quark_from_static_string (X_("shift"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n",       Properties::shift.property_id));
+       Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n",         Properties::position_lock_style.property_id));
+}
 
-sigc::signal<void,boost::shared_ptr<ARDOUR::Region> > Region::RegionPropertyChanged;
+void
+Region::register_properties ()
+{
+       _xml_node_name = X_("Region");
+
+       add_property (_muted);
+       add_property (_opaque);
+       add_property (_locked);
+       add_property (_automatic);
+       add_property (_whole_file);
+       add_property (_import);
+       add_property (_external);
+       add_property (_sync_marked);
+       add_property (_left_of_split);
+       add_property (_right_of_split);
+       add_property (_hidden);
+       add_property (_position_locked);
+       add_property (_valid_transients);
+       add_property (_start);
+       add_property (_length);
+       add_property (_position);
+       add_property (_sync_position);
+       add_property (_layer);
+       add_property (_ancestral_start);
+       add_property (_ancestral_length);
+       add_property (_stretch);
+       add_property (_shift);
+       add_property (_position_lock_style);
+}
+
+#define REGION_DEFAULT_STATE(s,l) \
+       _muted (Properties::muted, false)            \
+       , _opaque (Properties::opaque, true) \
+       , _locked (Properties::locked, false) \
+       , _automatic (Properties::automatic, false) \
+       , _whole_file (Properties::whole_file, false) \
+       , _import (Properties::import, false) \
+       , _external (Properties::external, false) \
+       , _sync_marked (Properties::sync_marked, false) \
+       , _left_of_split (Properties::left_of_split, false) \
+       , _right_of_split (Properties::right_of_split, false) \
+       , _hidden (Properties::hidden, false) \
+       , _position_locked (Properties::position_locked, false) \
+       , _valid_transients (Properties::valid_transients, false) \
+       , _start (Properties::start, (s))       \
+       , _length (Properties::length, (l))     \
+       , _position (Properties::position, 0) \
+       , _sync_position (Properties::sync_position, (s)) \
+       , _layer (Properties::layer, 0) \
+       , _ancestral_start (Properties::ancestral_start, (s)) \
+       , _ancestral_length (Properties::ancestral_length, (l)) \
+       , _stretch (Properties::stretch, 1.0) \
+       , _shift (Properties::shift, 1.0) \
+       , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime)
+
+#define REGION_COPY_STATE(other) \
+         _muted (Properties::muted, other->_muted)                     \
+       , _opaque (Properties::opaque, other->_opaque)          \
+       , _locked (Properties::locked, other->_locked)          \
+       , _automatic (Properties::automatic, other->_automatic) \
+       , _whole_file (Properties::whole_file, other->_whole_file) \
+       , _import (Properties::import, other->_import)          \
+       , _external (Properties::external, other->_external)    \
+       , _sync_marked (Properties::sync_marked, other->_sync_marked) \
+       , _left_of_split (Properties::left_of_split, other->_left_of_split) \
+       , _right_of_split (Properties::right_of_split, other->_right_of_split) \
+       , _hidden (Properties::hidden, other->_hidden)          \
+       , _position_locked (Properties::position_locked, other->_position_locked) \
+       , _valid_transients (Properties::valid_transients, other->_valid_transients) \
+       , _start(Properties::start, other->_start)              \
+       , _length(Properties::length, other->_length)           \
+       , _position(Properties::position, other->_position)     \
+       , _sync_position(Properties::sync_position, other->_sync_position) \
+       , _layer (Properties::layer, other->_layer)             \
+       , _ancestral_start (Properties::ancestral_start, other->_ancestral_start) \
+       , _ancestral_length (Properties::ancestral_length, other->_ancestral_length) \
+       , _stretch (Properties::stretch, other->_stretch)       \
+       , _shift (Properties::shift, other->_shift)             \
+       , _position_lock_style (Properties::position_lock_style, other->_position_lock_style)
 
 /* derived-from-derived constructor (no sources in constructor) */
-Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
+Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
        : SessionObject(s, name)
        , _type(type)
-       , _flags(flags)
-       , _start(start) 
-       , _length(length) 
-       , _position(0) 
-       , _last_position(0) 
-       , _positional_lock_style(AudioTime)
-       , _sync_position(_start)
-       , _layer(layer)
-       , _first_edit(EditChangesNothing)
-       , _frozen(0)
-       , _stretch(1.0)
-       , _shift(1.0)
+       , REGION_DEFAULT_STATE(start,length)
+       , _last_length (length)
+       , _last_position (0)
+       , _first_edit (EditChangesNothing)
        , _read_data_count(0)
-       , _pending_changed(Change (0))
        , _last_layer_op(0)
+       , _pending_explicit_relayer (false)
 {
-       /* no sources at this point */
-}
+       register_properties ();
 
-/** Basic Region constructor (single source) */
-Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
-       : SessionObject(src->session(), name)
-       , _type(type)
-       , _flags(flags)
-       , _start(start) 
-       , _length(length) 
-       , _position(0) 
-       , _last_position(0) 
-       , _positional_lock_style(AudioTime)
-       , _sync_position(_start)
-       , _layer(layer)
-       , _first_edit(EditChangesNothing)
-       , _frozen(0)
-       , _ancestral_start (0)
-       , _ancestral_length (0)
-       , _stretch (1.0)
-       , _shift (1.0)
-       , _valid_transients(false)
-       , _read_data_count(0)
-       , _pending_changed(Change (0))
-       , _last_layer_op(0)
-
-{
-       _sources.push_back (src);
-       _master_sources.push_back (src);
-
-       src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
-
-       assert(_sources.size() > 0);
-       _positional_lock_style = AudioTime;
+       /* no sources at this point */
 }
 
 /** Basic Region constructor (many sources) */
-Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
-       : SessionObject(srcs.front()->session(), name)
-       , _type(type)
-       , _flags(flags)
-       , _start(start) 
-       , _length(length) 
-       , _position(0) 
-       , _last_position(0) 
-       , _positional_lock_style(AudioTime)
-       , _sync_position(_start)
-       , _layer(layer)
-       , _first_edit(EditChangesNothing)
-       , _frozen(0)
-       , _stretch(1.0)
-       , _shift(1.0)
+Region::Region (const SourceList& srcs)
+       : SessionObject(srcs.front()->session(), "toBeRenamed")
+       , _type (srcs.front()->type())
+       , REGION_DEFAULT_STATE(0,0)
+       , _last_length (0)
+       , _last_position (0)
+       , _first_edit (EditChangesNothing)
        , _read_data_count(0)
-       , _pending_changed(Change (0))
-       , _last_layer_op(0)
+       , _last_layer_op (0)
+       , _pending_explicit_relayer (false)
 {
-       
-       set<boost::shared_ptr<Source> > unique_srcs;
+       register_properties ();
 
-       for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
-               _sources.push_back (*i);
-               (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
-               unique_srcs.insert (*i);
-       }
+       _type = srcs.front()->type();
+
+       use_sources (srcs);
 
-       for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
-               _master_sources.push_back (*i);
-               if (unique_srcs.find (*i) == unique_srcs.end()) {
-                       (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
-               }
-       }
-       
        assert(_sources.size() > 0);
+       assert (_type == srcs.front()->type());
 }
 
-/** Create a new Region from part of an existing one */
-Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : SessionObject(other->session(), name)
+/** Create a new Region from an existing one */
+Region::Region (boost::shared_ptr<const Region> other)
+       : SessionObject(other->session(), other->name())
        , _type (other->data_type())
+       , REGION_COPY_STATE (other)
+       , _last_length (other->_last_length)
+       , _last_position(other->_last_position) \
+       , _first_edit (EditChangesNothing)
+       , _read_data_count(0)
+       , _last_layer_op (0)
+       , _pending_explicit_relayer (false)
 
 {
-       _start = other->_start + offset;
-       copy_stuff (other, offset, length, name, layer, flags);
+       register_properties ();
 
-       /* if the other region had a distinct sync point
-          set, then continue to use it as best we can.
-          otherwise, reset sync point back to start.
-       */
+       /* override state that may have been incorrectly inherited from the other region
+        */
 
-       if (other->flags() & SyncMarked) {
-               if (other->_sync_position < _start) {
-                       _flags = Flag (_flags & ~SyncMarked);
-                       _sync_position = _start;
-               } else {
-                       _sync_position = other->_sync_position;
-               }
-       } else {
-               _flags = Flag (_flags & ~SyncMarked);
-               _sync_position = _start;
-       }
+       _position = 0;
+       _locked = false;
+       _whole_file = false;
+       _hidden = false;
 
-       if (Profile->get_sae()) {
-               /* reset sync point to start if its ended up
-                  outside region bounds.
-               */
+       use_sources (other->_sources);
 
-               if (_sync_position < _start || _sync_position >= _start + _length) {
-                       _flags = Flag (_flags & ~SyncMarked);
-                       _sync_position = _start;
-               }
-       }
-}
-
-Region::Region (boost::shared_ptr<const Region> other, nframes_t length, const string& name, layer_t layer, Flag flags)
-       : SessionObject(other->session(), name)
-       , _type (other->data_type())
-{
-       /* create a new Region exactly like another but starting at 0 in its sources */
+       _position_lock_style = other->_position_lock_style;
+       _first_edit = other->_first_edit;
 
-       _start = 0;
-       copy_stuff (other, 0, length, name, layer, flags);
+       _start = 0; // It seems strange _start is not inherited here?
 
        /* sync pos is relative to start of file. our start-in-file is now zero,
           so set our sync position to whatever the the difference between
-          _start and _sync_pos was in the other region. 
+          _start and _sync_pos was in the other region.
 
-          result is that our new sync pos points to the same point in our source(s) 
+          result is that our new sync pos points to the same point in our source(s)
           as the sync in the other region did in its source(s).
 
           since we start at zero in our source(s), it is not possible to use a sync point that
           is before the start. reset it to _start if that was true in the other region.
        */
-       
-       if (other->flags() & SyncMarked) {
+
+       if (other->sync_marked()) {
                if (other->_start < other->_sync_position) {
                        /* sync pos was after the start point of the other region */
                        _sync_position = other->_sync_position - other->_start;
                } else {
                        /* sync pos was before the start point of the other region. not possible here. */
-                       _flags = Flag (_flags & ~SyncMarked);
+                       _sync_marked = false;
                        _sync_position = _start;
                }
        } else {
-               _flags = Flag (_flags & ~SyncMarked);
+               _sync_marked = false;
                _sync_position = _start;
        }
-               
+
        if (Profile->get_sae()) {
                /* reset sync point to start if its ended up
                   outside region bounds.
                */
 
                if (_sync_position < _start || _sync_position >= _start + _length) {
-                       _flags = Flag (_flags & ~SyncMarked);
+                       _sync_marked = false;
                        _sync_position = _start;
                }
        }
 
-       /* reset a couple of things that copy_stuff() gets wrong in this particular case */
-
-       _positional_lock_style = other->_positional_lock_style;
-       _first_edit = other->_first_edit;
+       assert (_type == other->data_type());
 }
 
-/** Pure copy constructor */
-Region::Region (boost::shared_ptr<const Region> other)
+/** Create a new Region from part of an existing one.
+
+    the start within \a other is given by \a offset
+    (i.e. relative to the start of \a other's sources, the start is \a offset + \a other.start()
+*/
+Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
        : SessionObject(other->session(), other->name())
-       , _type(other->data_type())
-       , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
-       , _start(other->_start) 
-       , _length(other->_length) 
-       , _position(other->_position) 
-       , _last_position(other->_last_position) 
-       , _positional_lock_style(other->_positional_lock_style)
-       , _sync_position(other->_sync_position)
-       , _layer(other->_layer)
-       , _first_edit(EditChangesID)
-       , _frozen(0)
-       , _ancestral_start (other->_ancestral_start)
-       , _ancestral_length (other->_ancestral_length)
-       , _stretch (other->_stretch)
-       , _shift (other->_shift)
-       , _valid_transients(false)
+       , _type (other->data_type())
+       , REGION_COPY_STATE (other)
+       , _last_length (other->_last_length)
+       , _last_position(other->_last_position) \
+       , _first_edit (EditChangesNothing)
        , _read_data_count(0)
-       , _pending_changed(Change(0))
-       , _last_layer_op(other->_last_layer_op)
-{
-       other->_first_edit = EditChangesName;
+       , _last_layer_op (0)
+       , _pending_explicit_relayer (false)
 
-       if (other->_extra_xml) {
-               _extra_xml = new XMLNode (*other->_extra_xml);
-       } else {
-               _extra_xml = 0;
-       }
+{
+       register_properties ();
 
-       set<boost::shared_ptr<Source> > unique_srcs;
+       /* override state that may have been incorrectly inherited from the other region
+        */
 
-       for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
-               _sources.push_back (*i);
-               (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
-               unique_srcs.insert (*i);
-       }
+       _position = 0;
+       _locked = false;
+       _whole_file = false;
+       _hidden = false;
 
-       for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
-               _master_sources.push_back (*i);
-               if (unique_srcs.find (*i) == unique_srcs.end()) {
-                       (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
-               }
-       }
-       
-       assert(_sources.size() > 0);
-}
+       use_sources (other->_sources);
 
-Region::Region (const SourceList& srcs, const XMLNode& node)
-       : SessionObject(srcs.front()->session(), X_("error: XML did not reset this"))
-       , _type(DataType::NIL) // to be loaded from XML
-       , _flags(Flag(0))
-       , _start(0) 
-       , _length(0) 
-       , _position(0) 
-       , _last_position(0) 
-       , _positional_lock_style(AudioTime)
-       , _sync_position(_start)
-       , _layer(0)
-       , _first_edit(EditChangesNothing)
-       , _frozen(0)
-       , _stretch(1.0)
-       , _shift(1.0)
-       , _read_data_count(0)
-       , _pending_changed(Change(0))
-       , _last_layer_op(0)
-{
-       set<boost::shared_ptr<Source> > unique_srcs;
+       _start = other->_start + offset;
 
-       for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
-               _sources.push_back (*i);
-               (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
-               unique_srcs.insert (*i);
-       }
+       /* if the other region had a distinct sync point
+          set, then continue to use it as best we can.
+          otherwise, reset sync point back to start.
+       */
 
-       for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
-               _master_sources.push_back (*i);
-               if (unique_srcs.find (*i) == unique_srcs.end()) {
-                       (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
+       if (other->sync_marked()) {
+               if (other->_sync_position < _start) {
+                       _sync_marked = false;
+                       _sync_position = _start;
+               } else {
+                       _sync_position = other->_sync_position;
                }
+       } else {
+               _sync_marked = false;
+               _sync_position = _start;
        }
 
-       if (set_state (node)) {
-               throw failed_constructor();
+       if (Profile->get_sae()) {
+               /* reset sync point to start if its ended up
+                  outside region bounds.
+               */
+
+               if (_sync_position < _start || _sync_position >= _start + _length) {
+                       _sync_marked = false;
+                       _sync_position = _start;
+               }
        }
 
-       assert(_type != DataType::NIL);
-       assert(_sources.size() > 0);
+       assert (_type == other->data_type());
 }
 
-Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
-       : SessionObject(src->session(), X_("error: XML did not reset this"))
-       , _type(DataType::NIL)
-       , _flags(Flag(0))
-       , _start(0) 
-       , _length(0) 
-       , _position(0) 
-       , _last_position(0) 
-       , _positional_lock_style(AudioTime)
-       , _sync_position(_start)
-       , _layer(0)
-       , _first_edit(EditChangesNothing)
-       , _frozen(0)
-       , _stretch(1.0)
-       , _shift(1.0)
-       , _read_data_count(0)
-       , _pending_changed(Change(0))
-       , _last_layer_op(0)
+/** Create a copy of @param other but with different sources. Used by filters */
+Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
+       : SessionObject (other->session(), other->name())
+       , _type (srcs.front()->type())
+       , REGION_COPY_STATE (other)
+       , _last_length (other->_last_length)
+       , _last_position (other->_last_position)
+       , _first_edit (EditChangesID)
+       , _read_data_count (0)
+       , _last_layer_op (other->_last_layer_op)
+       , _pending_explicit_relayer (false)
 {
-       _sources.push_back (src);
+       register_properties ();
 
-       if (set_state (node)) {
-               throw failed_constructor();
-       }
-       
-       assert(_type != DataType::NIL);
-       assert(_sources.size() > 0);
-}
+       _locked = false;
+       _position_locked = false;
 
-Region::~Region ()
-{
-       boost::shared_ptr<Playlist> pl (playlist());
+       other->_first_edit = EditChangesName;
 
-       if (pl) {
-               for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-                       (*i)->remove_playlist (pl);
-               }
-               for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
-                       (*i)->remove_playlist (pl);
-               }
+       if (other->_extra_xml) {
+               _extra_xml = new XMLNode (*other->_extra_xml);
+       } else {
+               _extra_xml = 0;
        }
-       
-       notify_callbacks ();
-       GoingAway (); /* EMIT SIGNAL */
+
+       use_sources (srcs);
+       assert(_sources.size() > 0);
 }
 
-void
-Region::copy_stuff (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
+Region::~Region ()
 {
-       _frozen = 0;
-       _pending_changed = Change (0);
-       _read_data_count = 0;
-       _valid_transients = false;
-
-       _length = length; 
-       _last_length = length; 
-       _sync_position = other->_sync_position;
-       _ancestral_start = other->_ancestral_start;
-       _ancestral_length = other->_ancestral_length; 
-       _stretch = other->_stretch;
-       _shift = other->_shift;
-       _name = name;
-       _last_position = 0; 
-       _position = 0; 
-       _layer = layer; 
-       _flags = Flag (flags & ~(Locked|WholeFile|Hidden));
-       _first_edit = EditChangesNothing;
-       _last_layer_op = 0;
-       _positional_lock_style = AudioTime;
+       DEBUG_TRACE (DEBUG::Destruction, string_compose ("Region %1 destructor @ %2\n", _name, this));
+        drop_sources ();
 }
 
 void
 Region::set_playlist (boost::weak_ptr<Playlist> wpl)
 {
-       boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
-
-       boost::shared_ptr<Playlist> pl (wpl.lock());
-
-       if (old_playlist == pl) {
-               return;
-       }
-
-       _playlist = pl;
-
-       if (pl) {
-               if (old_playlist) {
-                       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-                               (*i)->remove_playlist (_playlist);      
-                               (*i)->add_playlist (pl);
-                       }
-                       for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
-                               (*i)->remove_playlist (_playlist);      
-                               (*i)->add_playlist (pl);
-                       }
-               } else {
-                       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-                               (*i)->add_playlist (pl);
-                       }
-                       for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
-                               (*i)->add_playlist (pl);
-                       }
-               }
-       } else {
-               if (old_playlist) {
-                       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
-                               (*i)->remove_playlist (old_playlist);
-                       }
-                       for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
-                               (*i)->remove_playlist (old_playlist);
-                       }
-               }
-       }
+       _playlist = wpl.lock();
 }
 
 bool
@@ -450,56 +424,62 @@ Region::set_name (const std::string& str)
 {
        if (_name != str) {
                SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
-               assert(_name == str); 
-               send_change (ARDOUR::NameChanged);
+               assert(_name == str);
+
+               send_change (Properties::name);
        }
 
        return true;
 }
 
 void
-Region::set_length (nframes_t len, void *src)
+Region::set_length (framecnt_t len)
 {
        //cerr << "Region::set_length() len = " << len << endl;
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
        if (_length != len && len != 0) {
 
-               /* check that the current _position wouldn't make the new 
+               /* check that the current _position wouldn't make the new
                   length impossible.
                */
 
-               if (max_frames - len < _position) {
+               if (max_framepos - len < _position) {
                        return;
                }
 
                if (!verify_length (len)) {
                        return;
                }
-               
-
-               _last_length = _length;
-               _length = len;
 
-               _flags = Region::Flag (_flags & ~WholeFile);
 
+               _last_length = _length;
+               set_length_internal (len);
+               _whole_file = false;
                first_edit ();
                maybe_uncopy ();
                invalidate_transients ();
 
-               if (!_frozen) {
+               if (!property_changes_suspended()) {
                        recompute_at_end ();
                }
 
-               send_change (LengthChanged);
+               send_change (Properties::length);
        }
 }
 
+void
+Region::set_length_internal (framecnt_t len)
+{
+       _length = len;
+}
+
 void
 Region::maybe_uncopy ()
 {
+       /* this does nothing but marked a semantic moment once upon a time */
 }
 
 void
@@ -509,10 +489,11 @@ Region::first_edit ()
 
        if (_first_edit != EditChangesNothing && pl) {
 
-               _name = pl->session().new_region_name (_name);
+               _name = RegionFactory::new_region_name (_name);
                _first_edit = EditChangesNothing;
 
-               send_change (ARDOUR::NameChanged);
+               send_change (Properties::name);
+
                RegionFactory::CheckNewRegion (shared_from_this());
        }
 }
@@ -525,7 +506,7 @@ Region::at_natural_position () const
        if (!pl) {
                return false;
        }
-       
+
        boost::shared_ptr<Region> whole_file_region = get_parent();
 
        if (whole_file_region) {
@@ -538,25 +519,25 @@ Region::at_natural_position () const
 }
 
 void
-Region::move_to_natural_position (void *src)
+Region::move_to_natural_position ()
 {
        boost::shared_ptr<Playlist> pl (playlist());
 
        if (!pl) {
                return;
        }
-       
+
        boost::shared_ptr<Region> whole_file_region = get_parent();
 
        if (whole_file_region) {
-               set_position (whole_file_region->position() + _start, src);
+               set_position (whole_file_region->position() + _start);
        }
 }
-       
+
 void
-Region::special_set_position (nframes_t pos)
+Region::special_set_position (framepos_t pos)
 {
-       /* this is used when creating a whole file region as 
+       /* this is used when creating a whole file region as
           a way to store its "natural" or "captured" position.
        */
 
@@ -567,86 +548,95 @@ Region::special_set_position (nframes_t pos)
 void
 Region::set_position_lock_style (PositionLockStyle ps)
 {
-       boost::shared_ptr<Playlist> pl (playlist());
+       if (_position_lock_style != ps) {
 
-       if (!pl) {
-               return;
-       }
+               boost::shared_ptr<Playlist> pl (playlist());
 
-       _positional_lock_style = ps;
+               if (!pl) {
+                       return;
+               }
+
+               _position_lock_style = ps;
+
+               if (_position_lock_style == MusicTime) {
+                       _session.tempo_map().bbt_time (_position, _bbt_time);
+               }
+
+               send_change (Properties::position_lock_style);
 
-       if (_positional_lock_style == MusicTime) {
-               pl->session().tempo_map().bbt_time (_position, _bbt_time);
        }
-       
 }
 
 void
 Region::update_position_after_tempo_map_change ()
 {
        boost::shared_ptr<Playlist> pl (playlist());
-       
-       if (!pl || _positional_lock_style != MusicTime) {
+
+       if (!pl || _position_lock_style != MusicTime) {
                return;
        }
 
-       TempoMap& map (pl->session().tempo_map());
-       nframes_t pos = map.frame_time (_bbt_time);
+       TempoMap& map (_session.tempo_map());
+       framepos_t pos = map.frame_time (_bbt_time);
        set_position_internal (pos, false);
+
+       /* do this even if the position is the same. this helps out
+          a GUI that has moved its representation already.
+       */
+       send_change (Properties::position);
 }
 
 void
-Region::set_position (nframes_t pos, void *src)
+Region::set_position (framepos_t pos)
 {
        if (!can_move()) {
                return;
        }
 
        set_position_internal (pos, true);
+
+       /* do this even if the position is the same. this helps out
+          a GUI that has moved its representation already.
+       */
+       send_change (Properties::position);
+
 }
 
 void
-Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
+Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
 {
        if (_position != pos) {
                _last_position = _position;
                _position = pos;
 
                /* check that the new _position wouldn't make the current
-                  length impossible - if so, change the length. 
+                  length impossible - if so, change the length.
 
                   XXX is this the right thing to do?
                */
 
-               if (max_frames - _length < _position) {
+               if (max_framepos - _length < _position) {
                        _last_length = _length;
-                       _length = max_frames - _position;
+                       _length = max_framepos - _position;
                }
 
                if (allow_bbt_recompute) {
                        recompute_position_from_lock_style ();
                }
 
-               invalidate_transients ();
+               //invalidate_transients ();
        }
-
-       /* do this even if the position is the same. this helps out
-          a GUI that has moved its representation already.
-       */
-
-       send_change (PositionChanged);
 }
 
 void
-Region::set_position_on_top (nframes_t pos, void *src)
+Region::set_position_on_top (framepos_t pos)
 {
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
        if (_position != pos) {
-               _last_position = _position;
-               _position = pos;
+               set_position_internal (pos, true);
        }
 
        boost::shared_ptr<Playlist> pl (playlist());
@@ -658,53 +648,51 @@ Region::set_position_on_top (nframes_t pos, void *src)
        /* do this even if the position is the same. this helps out
           a GUI that has moved its representation already.
        */
-       
-       send_change (PositionChanged);
+       send_change (Properties::position);
 }
 
 void
 Region::recompute_position_from_lock_style ()
 {
-       if (_positional_lock_style == MusicTime) {
-               boost::shared_ptr<Playlist> pl (playlist());
-               if (pl) {
-                       pl->session().tempo_map().bbt_time (_position, _bbt_time);
-               }
+       if (_position_lock_style == MusicTime) {
+               _session.tempo_map().bbt_time (_position, _bbt_time);
        }
 }
-               
+
 void
-Region::nudge_position (nframes64_t n, void *src)
+Region::nudge_position (frameoffset_t n)
 {
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
        if (n == 0) {
                return;
        }
-       
-       _last_position = _position;
+
+       framepos_t new_position = _position;
 
        if (n > 0) {
-               if (_position > max_frames - n) {
-                       _position = max_frames;
+               if (_position > max_framepos - n) {
+                       new_position = max_framepos;
                } else {
-                       _position += n;
+                       new_position += n;
                }
        } else {
-               if (_position < (nframes_t) -n) {
-                       _position = 0;
+               if (_position < -n) {
+                       new_position = 0;
                } else {
-                       _position += n;
+                       new_position += n;
                }
        }
 
-       send_change (PositionChanged);
+       set_position_internal (new_position, true);
+
+       send_change (Properties::position);
 }
 
 void
-Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
+Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
 {
        _ancestral_length = l;
        _ancestral_start = s;
@@ -713,9 +701,9 @@ Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
 }
 
 void
-Region::set_start (nframes_t pos, void *src)
+Region::set_start (framepos_t pos)
 {
-       if (_flags & (Locked|PositionLocked)) {
+       if (locked() || position_locked()) {
                return;
        }
        /* This just sets the start, nothing else. It effectively shifts
@@ -730,33 +718,27 @@ Region::set_start (nframes_t pos, void *src)
                }
 
                _start = pos;
-               _flags = Region::Flag (_flags & ~WholeFile);
+               _whole_file = false;
                first_edit ();
                invalidate_transients ();
 
-               send_change (StartChanged);
+               send_change (Properties::start);
        }
 }
 
 void
-Region::trim_start (nframes_t new_position, void *src)
+Region::trim_start (framepos_t new_position)
 {
-       if (_flags & (Locked|PositionLocked)) {
+       if (locked() || position_locked()) {
                return;
        }
-       nframes_t new_start;
-       int32_t start_shift;
-       
-       if (new_position > _position) {
-               start_shift = new_position - _position;
-       } else {
-               start_shift = -(_position - new_position);
-       }
+       framepos_t new_start;
+       frameoffset_t const start_shift = new_position - _position;
 
        if (start_shift > 0) {
 
-               if (_start > max_frames - start_shift) {
-                       new_start = max_frames;
+               if (_start > max_framepos - start_shift) {
+                       new_start = max_framepos;
                } else {
                        new_start = _start + start_shift;
                }
@@ -767,11 +749,12 @@ Region::trim_start (nframes_t new_position, void *src)
 
        } else if (start_shift < 0) {
 
-               if (_start < (nframes_t) -start_shift) {
+               if (_start < -start_shift) {
                        new_start = 0;
                } else {
                        new_start = _start + start_shift;
                }
+
        } else {
                return;
        }
@@ -779,23 +762,41 @@ Region::trim_start (nframes_t new_position, void *src)
        if (new_start == _start) {
                return;
        }
-       
+
        _start = new_start;
-       _flags = Region::Flag (_flags & ~WholeFile);
+       _whole_file = false;
        first_edit ();
 
-       send_change (StartChanged);
+       send_change (Properties::start);
+}
+
+void
+Region::trim_front (framepos_t new_position)
+{
+       modify_front (new_position, false);
+}
+
+void
+Region::cut_front (framepos_t new_position)
+{
+       modify_front (new_position, true);
+}
+
+void
+Region::cut_end (framepos_t new_endpoint)
+{
+       modify_end (new_endpoint, true);
 }
 
 void
-Region::trim_front (nframes_t new_position, void *src)
+Region::modify_front (framepos_t new_position, bool reset_fade)
 {
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
-       nframes_t end = last_frame();
-       nframes_t source_zero;
+       framepos_t end = last_frame();
+       framepos_t source_zero;
 
        if (_position > _start) {
                source_zero = _position - _start;
@@ -804,89 +805,109 @@ Region::trim_front (nframes_t new_position, void *src)
        }
 
        if (new_position < end) { /* can't trim it zero or negative length */
-               
-               nframes_t newlen;
-
-               /* can't trim it back passed where source position zero is located */
-               
-               new_position = max (new_position, source_zero);
-               
-               
+
+               framecnt_t newlen = 0;
+               framepos_t delta = 0;
+
+               if (!can_trim_start_before_source_start ()) {
+                       /* can't trim it back past where source position zero is located */
+                       new_position = max (new_position, source_zero);
+               }
+
                if (new_position > _position) {
                        newlen = _length - (new_position - _position);
+                       delta = -1 * (new_position - _position);
                } else {
                        newlen = _length + (_position - new_position);
+                       delta = _position - new_position;
                }
-               
-               trim_to_internal (new_position, newlen, src);
-               if (!_frozen) {
+
+               trim_to_internal (new_position, newlen);
+
+               if (reset_fade) {
+                       _right_of_split = true;
+               }
+
+               if (!property_changes_suspended()) {
                        recompute_at_start ();
                }
+
+               if (_transients.size() > 0){
+                       adjust_transients(delta);
+               }
        }
 }
 
 void
-Region::trim_end (nframes_t new_endpoint, void *src)
+Region::modify_end (framepos_t new_endpoint, bool reset_fade)
 {
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
        if (new_endpoint > _position) {
-               trim_to_internal (_position, new_endpoint - _position, this);
-               if (!_frozen) {
+               trim_to_internal (_position, new_endpoint - _position +1);
+               if (reset_fade) {
+                       _left_of_split = true;
+               }
+               if (!property_changes_suspended()) {
                        recompute_at_end ();
                }
        }
 }
 
+/** @param new_endpoint New region end point, such that, for example,
+ *  a region at 0 of length 10 has an endpoint of 9.
+ */
+
 void
-Region::trim_to (nframes_t position, nframes_t length, void *src)
+Region::trim_end (framepos_t new_endpoint)
 {
-       if (_flags & Locked) {
+       modify_end (new_endpoint, false);
+}
+
+void
+Region::trim_to (framepos_t position, framecnt_t length)
+{
+       if (locked()) {
                return;
        }
 
-       trim_to_internal (position, length, src);
+       trim_to_internal (position, length);
 
-       if (!_frozen) {
+       if (!property_changes_suspended()) {
                recompute_at_start ();
                recompute_at_end ();
        }
 }
 
 void
-Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
+Region::trim_to_internal (framepos_t position, framecnt_t length)
 {
-       int32_t start_shift;
-       nframes_t new_start;
+       framepos_t new_start;
 
-       if (_flags & Locked) {
+       if (locked()) {
                return;
        }
 
-       if (position > _position) {
-               start_shift = position - _position;
-       } else {
-               start_shift = -(_position - position);
-       }
+       frameoffset_t const start_shift = position - _position;
 
        if (start_shift > 0) {
 
-               if (_start > max_frames - start_shift) {
-                       new_start = max_frames;
+               if (_start > max_framepos - start_shift) {
+                       new_start = max_framepos;
                } else {
                        new_start = _start + start_shift;
                }
 
-
        } else if (start_shift < 0) {
 
-               if (_start < (nframes_t) -start_shift) {
+               if (_start < -start_shift && !can_trim_start_before_source_start ()) {
                        new_start = 0;
                } else {
                        new_start = _start + start_shift;
                }
+
        } else {
                new_start = _start;
        }
@@ -895,65 +916,82 @@ Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
                return;
        }
 
-       Change what_changed = Change (0);
+       PropertyChange what_changed;
 
        if (_start != new_start) {
                _start = new_start;
-               what_changed = Change (what_changed|StartChanged);
-       }
-       if (_length != length) {
-               if (!_frozen) {
-                       _last_length = _length;
-               }
-               _length = length;
-               what_changed = Change (what_changed|LengthChanged);
+               what_changed.add (Properties::start);
        }
+
+       /* Set position before length, otherwise for MIDI regions this bad thing happens:
+        * 1. we call set_length_internal; length in beats is computed using the region's current
+        *    (soon-to-be old) position
+        * 2. we call set_position_internal; position is set and length in frames re-computed using
+        *    length in beats from (1) but at the new position, which is wrong if the region
+        *    straddles a tempo/meter change.
+        */
+
        if (_position != position) {
-               if (!_frozen) {
+               if (!property_changes_suspended()) {
                        _last_position = _position;
                }
-               _position = position;
-               what_changed = Change (what_changed|PositionChanged);
+               set_position_internal (position, true);
+               what_changed.add (Properties::position);
        }
-       
-       _flags = Region::Flag (_flags & ~WholeFile);
 
-       if (what_changed & (StartChanged|LengthChanged)) {
+       if (_length != length) {
+               if (!property_changes_suspended()) {
+                       _last_length = _length;
+               }
+               set_length_internal (length);
+               what_changed.add (Properties::length);
+       }
+
+       _whole_file = false;
+
+       PropertyChange start_and_length;
+
+       start_and_length.add (Properties::start);
+       start_and_length.add (Properties::length);
+
+       if (what_changed.contains (start_and_length)) {
                first_edit ();
-       } 
+       }
 
-       if (what_changed) {
+       if (!what_changed.empty()) {
                send_change (what_changed);
        }
-}      
+}
 
 void
 Region::set_hidden (bool yn)
 {
        if (hidden() != yn) {
+               _hidden = yn;
+               send_change (Properties::hidden);
+       }
+}
 
-               if (yn) {
-                       _flags = Flag (_flags|Hidden);
-               } else {
-                       _flags = Flag (_flags & ~Hidden);
-               }
+void
+Region::set_whole_file (bool yn)
+{
+       _whole_file = yn;
+       /* no change signal */
+}
 
-               send_change (HiddenChanged);
-       }
+void
+Region::set_automatic (bool yn)
+{
+       _automatic = yn;
+       /* no change signal */
 }
 
 void
 Region::set_muted (bool yn)
 {
        if (muted() != yn) {
-
-               if (yn) {
-                       _flags = Flag (_flags|Muted);
-               } else {
-                       _flags = Flag (_flags & ~Muted);
-               }
-
-               send_change (MuteChanged);
+               _muted = yn;
+               send_change (Properties::muted);
        }
 }
 
@@ -961,12 +999,8 @@ void
 Region::set_opaque (bool yn)
 {
        if (opaque() != yn) {
-               if (yn) {
-                       _flags = Flag (_flags|Opaque);
-               } else {
-                       _flags = Flag (_flags & ~Opaque);
-               }
-               send_change (OpacityChanged);
+               _opaque = yn;
+               send_change (Properties::opaque);
        }
 }
 
@@ -974,12 +1008,8 @@ void
 Region::set_locked (bool yn)
 {
        if (locked() != yn) {
-               if (yn) {
-                       _flags = Flag (_flags|Locked);
-               } else {
-                       _flags = Flag (_flags & ~Locked);
-               }
-               send_change (LockChanged);
+               _locked = yn;
+               send_change (Properties::locked);
        }
 }
 
@@ -987,56 +1017,52 @@ void
 Region::set_position_locked (bool yn)
 {
        if (position_locked() != yn) {
-               if (yn) {
-                       _flags = Flag (_flags|PositionLocked);
-               } else {
-                       _flags = Flag (_flags & ~PositionLocked);
-               }
-               send_change (LockChanged);
+               _position_locked = yn;
+               send_change (Properties::locked);
        }
 }
 
+/** Set the region's sync point.
+ *  @param absolute_pos Session time.
+ */
 void
-Region::set_sync_position (nframes_t absolute_pos)
+Region::set_sync_position (framepos_t absolute_pos)
 {
-       nframes_t file_pos;
-
-       file_pos = _start + (absolute_pos - _position);
+       /* position within our file */
+       framepos_t const file_pos = _start + (absolute_pos - _position);
 
        if (file_pos != _sync_position) {
-               
+               _sync_marked = true;
                _sync_position = file_pos;
-               _flags = Flag (_flags|SyncMarked);
-
-               if (!_frozen) {
+               if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
-               send_change (SyncOffsetChanged);
+
+               send_change (Properties::sync_position);
        }
 }
 
 void
 Region::clear_sync_position ()
 {
-       if (_flags & SyncMarked) {
-               _flags = Flag (_flags & ~SyncMarked);
-
-               if (!_frozen) {
+       if (sync_marked()) {
+               _sync_marked = false;
+               if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
-               send_change (SyncOffsetChanged);
+
+               send_change (Properties::sync_position);
        }
 }
 
-nframes_t
+/* @return the sync point relative the first frame of the region */
+frameoffset_t
 Region::sync_offset (int& dir) const
 {
-       /* returns the sync point relative the first frame of the region */
-
-       if (_flags & SyncMarked) {
+       if (sync_marked()) {
                if (_sync_position > _start) {
                        dir = 1;
-                       return _sync_position - _start; 
+                       return _sync_position - _start;
                } else {
                        dir = -1;
                        return _start - _sync_position;
@@ -1047,14 +1073,14 @@ Region::sync_offset (int& dir) const
        }
 }
 
-nframes_t 
-Region::adjust_to_sync (nframes_t pos) const
+framepos_t
+Region::adjust_to_sync (framepos_t pos) const
 {
        int sync_dir;
-       nframes_t offset = sync_offset (sync_dir);
+       frameoffset_t offset = sync_offset (sync_dir);
 
        // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
-       
+
        if (sync_dir > 0) {
                if (pos > offset) {
                        pos -= offset;
@@ -1062,7 +1088,7 @@ Region::adjust_to_sync (nframes_t pos) const
                        pos = 0;
                }
        } else {
-               if (max_frames - pos > offset) {
+               if (max_framepos - pos > offset) {
                        pos += offset;
                }
        }
@@ -1070,13 +1096,15 @@ Region::adjust_to_sync (nframes_t pos) const
        return pos;
 }
 
-nframes_t
+/** @return Sync position in session time */
+framepos_t
 Region::sync_position() const
 {
-       if (_flags & SyncMarked) {
-               return _sync_position; 
+       if (sync_marked()) {
+               return _position - _start + _sync_position;
        } else {
-               return _start;
+               /* if sync has not been marked, use the start of the region */
+               return _position;
        }
 }
 
@@ -1122,37 +1150,26 @@ Region::set_layer (layer_t l)
 {
        if (_layer != l) {
                _layer = l;
-               
-               send_change (LayerChanged);
+
+               send_change (Properties::layer);
        }
 }
 
 XMLNode&
-Region::state (bool full_state)
+Region::state ()
 {
        XMLNode *node = new XMLNode ("Region");
        char buf[64];
+       char buf2[64];
+       LocaleGuard lg (X_("POSIX"));
        const char* fe = NULL;
 
+       add_properties (*node);
+
        _id.print (buf, sizeof (buf));
        node->add_property ("id", buf);
-       node->add_property ("name", _name);
        node->add_property ("type", _type.to_string());
-       snprintf (buf, sizeof (buf), "%u", _start);
-       node->add_property ("start", buf);
-       snprintf (buf, sizeof (buf), "%u", _length);
-       node->add_property ("length", buf);
-       snprintf (buf, sizeof (buf), "%u", _position);
-       node->add_property ("position", buf);
-       snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
-       node->add_property ("ancestral-start", buf);
-       snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
-       node->add_property ("ancestral-length", buf);
-       snprintf (buf, sizeof (buf), "%.12g", _stretch);
-       node->add_property ("stretch", buf);
-       snprintf (buf, sizeof (buf), "%.12g", _shift);
-       node->add_property ("shift", buf);
-       
+
        switch (_first_edit) {
        case EditChangesNothing:
                fe = X_("nothing");
@@ -1172,283 +1189,165 @@ Region::state (bool full_state)
 
        /* note: flags are stored by derived classes */
 
-       snprintf (buf, sizeof (buf), "%d", (int) _layer);
-       node->add_property ("layer", buf);
-       snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
-       node->add_property ("sync-position", buf);
-
-       if (_positional_lock_style != AudioTime) {
-               node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
+       if (_position_lock_style != AudioTime) {
                stringstream str;
                str << _bbt_time;
                node->add_property ("bbt-position", str.str());
        }
 
-       return *node;
-}
-
-XMLNode&
-Region::get_state ()
-{
-       return state (true);
-}
-
-int
-Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
-{
-       const XMLNodeList& nlist = node.children();
-       const XMLProperty *prop;
-       nframes_t val;
-
-       /* this is responsible for setting those aspects of Region state 
-          that are mutable after construction.
-       */
-
-       if ((prop = node.property ("name")) == 0) {
-               error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
-               return -1;
+       for (uint32_t n=0; n < _sources.size(); ++n) {
+               snprintf (buf2, sizeof(buf2), "source-%d", n);
+               _sources[n]->id().print (buf, sizeof(buf));
+               node->add_property (buf2, buf);
        }
 
-       _name = prop->value();
-       
-       if ((prop = node.property ("type")) == 0) {
-               _type = DataType::AUDIO;
-       } else {
-               _type = DataType(prop->value());
+       for (uint32_t n=0; n < _master_sources.size(); ++n) {
+               snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+               _master_sources[n]->id().print (buf, sizeof (buf));
+               node->add_property (buf2, buf);
        }
 
-       if ((prop = node.property ("start")) != 0) {
-               sscanf (prop->value().c_str(), "%" PRIu32, &val);
-               if (val != _start) {
-                       what_changed = Change (what_changed|StartChanged);      
-                       _start = val;
-               }
-       } else {
-               _start = 0;
-       }
+       if (max_source_level() > 0) {
 
-       if ((prop = node.property ("length")) != 0) {
-               sscanf (prop->value().c_str(), "%" PRIu32, &val);
-               if (val != _length) {
-                       what_changed = Change (what_changed|LengthChanged);
-                       _last_length = _length;
-                       _length = val;
-               }
-       } else {
-               _last_length = _length;
-               _length = 1;
-       }
+               XMLNode* nested_node = new XMLNode (X_("NestedSource"));
 
-       if ((prop = node.property ("position")) != 0) {
-               sscanf (prop->value().c_str(), "%" PRIu32, &val);
-               if (val != _position) {
-                       what_changed = Change (what_changed|PositionChanged);
-                       _last_position = _position;
-                       _position = val;
-               }
-       } else {
-               _last_position = _position;
-               _position = 0;
-       }
+               /* region is compound - get its playlist and
+                  store that before we list the region that
+                  needs it ...
+               */
 
-       if ((prop = node.property ("layer")) != 0) {
-               layer_t x;
-               x = (layer_t) atoi (prop->value().c_str());
-               if (x != _layer) {
-                       what_changed = Change (what_changed|LayerChanged);
-                       _layer = x;
+               for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
+                       nested_node->add_child_nocopy ((*s)->get_state ());
                }
-       } else {
-               _layer = 0;
-       }
 
-       if ((prop = node.property ("sync-position")) != 0) {
-               sscanf (prop->value().c_str(), "%" PRIu32, &val);
-               if (val != _sync_position) {
-                       what_changed = Change (what_changed|SyncOffsetChanged);
-                       _sync_position = val;
+               if (nested_node) {
+                       node->add_child_nocopy (*nested_node);
                }
-       } else {
-               _sync_position = _start;
        }
 
-       if ((prop = node.property ("positional-lock-style")) != 0) {
-               _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
-
-               if (_positional_lock_style == MusicTime) {
-                       if ((prop = node.property ("bbt-position")) == 0) {
-                               /* missing BBT info, revert to audio time locking */
-                               _positional_lock_style = AudioTime;
-                       } else {
-                               if (sscanf (prop->value().c_str(), "%d|%d|%d", 
-                                           &_bbt_time.bars,
-                                           &_bbt_time.beats,
-                                           &_bbt_time.ticks) != 3) {
-                                       _positional_lock_style = AudioTime;
-                               }
-                       }
-               }
-                       
-       } else {
-               _positional_lock_style = AudioTime;
+       if (_extra_xml) {
+               node->add_child_copy (*_extra_xml);
        }
 
-       /* XXX FIRST EDIT !!! */
-       
-       /* these 3 properties never change as a result of any editing */
+       return *node;
+}
 
-       if ((prop = node.property ("ancestral-start")) != 0) {
-               _ancestral_start = atoi (prop->value());
-       } else {
-               _ancestral_start = _start;
-       }
+XMLNode&
+Region::get_state ()
+{
+       return state ();
+}
 
-       if ((prop = node.property ("ancestral-length")) != 0) {
-               _ancestral_length = atoi (prop->value());
-       } else {
-               _ancestral_length = _length;
-       }
+int
+Region::set_state (const XMLNode& node, int version)
+{
+       PropertyChange what_changed;
+       return _set_state (node, version, what_changed, true);
+}
 
-       if ((prop = node.property ("stretch")) != 0) {
-               _stretch = atof (prop->value());
+int
+Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_changed, bool send)
+{
+       const XMLProperty* prop;
+       const XMLNodeList& nlist = node.children();
 
-               /* fix problem with old sessions corrupted by an impossible
-                  value for _stretch
-               */
-               if (_stretch == 0.0) {
-                       _stretch = 1.0;
-               }
-       } else {
-               _stretch = 1.0;
-       }
+       for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
-       if ((prop = node.property ("shift")) != 0) {
-               _shift = atof (prop->value());
+               XMLNode *child = (*niter);
 
-               /* fix problem with old sessions corrupted by an impossible
-                  value for _shift
-               */
-               if (_shift == 0.0) {
-                       _shift = 1.0;
+               if (child->name () == "Extra") {
+                       delete _extra_xml;
+                       _extra_xml = new XMLNode (*child);
                }
-       } else {
-               _shift = 1.0;
        }
 
+       what_changed = set_values (node);
 
-       /* note: derived classes set flags */
-
-       delete _extra_xml;
-       _extra_xml = 0;
+       if ((prop = node.property (X_("id")))) {
+               _id = prop->value();
+       }
 
-       for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
-               
-               XMLNode *child;
-               
-               child = (*niter);
-               
-               if (child->name () == "Extra") {
-                       _extra_xml = new XMLNode (*child);
-                       break;
+       if (_position_lock_style == MusicTime) {
+               if ((prop = node.property ("bbt-position")) == 0) {
+                       /* missing BBT info, revert to audio time locking */
+                       _position_lock_style = AudioTime;
+               } else {
+                       if (sscanf (prop->value().c_str(), "%d|%d|%d",
+                                   &_bbt_time.bars,
+                                   &_bbt_time.beats,
+                                   &_bbt_time.ticks) != 3) {
+                               _position_lock_style = AudioTime;
+                       }
                }
        }
 
-       if (send) {
-               send_change (what_changed);
+       /* fix problems with old sessions corrupted by impossible
+          values for _stretch or _shift
+       */
+       if (_stretch == 0.0f) {
+               _stretch = 1.0f;
        }
 
-       return 0;
-}
-
-int
-Region::set_state (const XMLNode& node)
-{
-       const XMLProperty *prop;
-       Change what_changed = Change (0);
+       if (_shift == 0.0f) {
+               _shift = 1.0f;
+       }
 
-       /* ID is not allowed to change, ever */
+       if (send) {
+               send_change (what_changed);
+       }
 
-       if ((prop = node.property ("id")) == 0) {
-               error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
-               return -1;
+       /* Quick fix for 2.x sessions when region is muted */
+       if ((prop = node.property (X_("flags")))) {
+               if (string::npos != prop->value().find("Muted")){
+                       set_muted (true);
+               }
        }
 
-       _id = prop->value();
-       
-       _first_edit = EditChangesNothing;
-       
-       set_live_state (node, what_changed, true);
 
        return 0;
 }
 
 void
-Region::freeze ()
+Region::suspend_property_changes ()
 {
-       _frozen++;
+       Stateful::suspend_property_changes ();
        _last_length = _length;
        _last_position = _position;
 }
 
 void
-Region::thaw (const string& why)
+Region::mid_thaw (const PropertyChange& what_changed)
 {
-       Change what_changed = Change (0);
-
-       {
-               Glib::Mutex::Lock lm (_lock);
-
-               if (_frozen && --_frozen > 0) {
-                       return;
-               }
-
-               if (_pending_changed) {
-                       what_changed = _pending_changed;
-                       _pending_changed = Change (0);
-               }
-       }
-
-       if (what_changed == Change (0)) {
-               return;
-       }
-
-       if (what_changed & LengthChanged) {
-               if (what_changed & PositionChanged) {
+       if (what_changed.contains (Properties::length)) {
+               if (what_changed.contains (Properties::position)) {
                        recompute_at_start ();
-               } 
+               }
                recompute_at_end ();
        }
-               
-       StateChanged (what_changed);
 }
 
 void
-Region::send_change (Change what_changed)
+Region::send_change (const PropertyChange& what_changed)
 {
-       {
-               Glib::Mutex::Lock lm (_lock);
-               if (_frozen) {
-                       _pending_changed = Change (_pending_changed|what_changed);
-                       return;
-               } 
+       if (what_changed.empty()) {
+               return;
        }
 
-       StateChanged (what_changed);
-       
-       if (!(_flags & DoNotSaveState)) {
-               
+       Stateful::send_change (what_changed);
+
+       if (!Stateful::frozen()) {
+
                /* Try and send a shared_pointer unless this is part of the constructor.
                   If so, do nothing.
                */
-               
+
                try {
                        boost::shared_ptr<Region> rptr = shared_from_this();
-                       RegionPropertyChanged (rptr);
+                       RegionPropertyChanged (rptr, what_changed);
                } catch (...) {
                        /* no shared_ptr available, relax; */
                }
        }
-       
 }
 
 void
@@ -1485,10 +1384,21 @@ Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
 }
 
 void
-Region::source_deleted (boost::shared_ptr<Source>)
+Region::source_deleted (boost::weak_ptr<Source>)
 {
-       _sources.clear ();
-       drop_references ();
+       drop_sources ();
+
+       if (!_session.deletion_in_progress()) {
+               /* this is a very special case: at least one of the region's
+                  sources has bee deleted, so invalidate all references to
+                  ourselves. Do NOT do this during session deletion, because
+                  then we run the risk that this will actually result
+                  in this object being deleted (as refcnt goes to zero)
+                  while emitting DropReferences.
+               */
+
+               drop_references ();
+       }
 }
 
 vector<string>
@@ -1507,7 +1417,16 @@ Region::master_source_names ()
 void
 Region::set_master_sources (const SourceList& srcs)
 {
+       for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+               (*i)->dec_use_count ();
+       }
+
        _master_sources = srcs;
+       assert (_sources.size() == _master_sources.size());
+
+       for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+               (*i)->inc_use_count ();
+       }
 }
 
 bool
@@ -1516,6 +1435,11 @@ Region::source_equivalent (boost::shared_ptr<const Region> other) const
        if (!other)
                return false;
 
+       if ((_sources.size() != other->_sources.size()) ||
+           (_master_sources.size() != other->_master_sources.size())) {
+               return false;
+       }
+
        SourceList::const_iterator i;
        SourceList::const_iterator io;
 
@@ -1534,35 +1458,88 @@ Region::source_equivalent (boost::shared_ptr<const Region> other) const
        return true;
 }
 
+std::string
+Region::source_string () const
+{
+       //string res = itos(_sources.size());
+
+       stringstream res;
+       res << _sources.size() << ":";
+
+       SourceList::const_iterator i;
+
+       for (i = _sources.begin(); i != _sources.end(); ++i) {
+               res << (*i)->id() << ":";
+       }
+
+       for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+               res << (*i)->id() << ":";
+       }
+
+       return res.str();
+}
+
+bool
+Region::uses_source (boost::shared_ptr<const Source> source) const
+{
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+               if (*i == source) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 bool
-Region::verify_length (nframes_t len)
+Region::uses_source_path (const std::string& path) const
+{
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+                boost::shared_ptr<const FileSource> fs = boost::dynamic_pointer_cast<const FileSource> (*i);
+                if (fs) {
+                        if (fs->path() == path) {
+                                return true;
+                        }
+                }
+       }
+       return false;
+}
+
+framecnt_t
+Region::source_length(uint32_t n) const
+{
+       assert (n < _sources.size());
+       return _sources[n]->length (_position - _start);
+}
+
+bool
+Region::verify_length (framecnt_t len)
 {
        if (source() && (source()->destructive() || source()->length_mutable())) {
                return true;
        }
 
-       nframes_t maxlen = 0;
+       framecnt_t maxlen = 0;
 
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               maxlen = max (maxlen, _sources[n]->length() - _start);
+       for (uint32_t n = 0; n < _sources.size(); ++n) {
+               maxlen = max (maxlen, source_length(n) - _start);
        }
-       
+
        len = min (len, maxlen);
-       
+
        return true;
 }
 
 bool
-Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
+Region::verify_start_and_length (framepos_t new_start, framecnt_t& new_length)
 {
        if (source() && (source()->destructive() || source()->length_mutable())) {
                return true;
        }
 
-       nframes_t maxlen = 0;
+       framecnt_t maxlen = 0;
 
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               maxlen = max (maxlen, _sources[n]->length() - new_start);
+       for (uint32_t n = 0; n < _sources.size(); ++n) {
+               maxlen = max (maxlen, source_length(n) - new_start);
        }
 
        new_length = min (new_length, maxlen);
@@ -1571,14 +1548,14 @@ Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
 }
 
 bool
-Region::verify_start (nframes_t pos)
+Region::verify_start (framepos_t pos)
 {
        if (source() && (source()->destructive() || source()->length_mutable())) {
                return true;
        }
 
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               if (pos > _sources[n]->length() - _length) {
+       for (uint32_t n = 0; n < _sources.size(); ++n) {
+               if (pos > source_length(n) - _length) {
                        return false;
                }
        }
@@ -1586,15 +1563,15 @@ Region::verify_start (nframes_t pos)
 }
 
 bool
-Region::verify_start_mutable (nframes_t& new_start)
+Region::verify_start_mutable (framepos_t& new_start)
 {
        if (source() && (source()->destructive() || source()->length_mutable())) {
                return true;
        }
 
-       for (uint32_t n=0; n < _sources.size(); ++n) {
-               if (new_start > _sources[n]->length() - _length) {
-                       new_start = _sources[n]->length() - _length;
+       for (uint32_t n = 0; n < _sources.size(); ++n) {
+               if (new_start > source_length(n) - _length) {
+                       new_start = source_length(n) - _length;
                }
        }
        return true;
@@ -1608,19 +1585,19 @@ Region::get_parent() const
        if (pl) {
                boost::shared_ptr<Region> r;
                boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
-               
-               if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
+
+               if (grrr2 && (r = _session.find_whole_file_parent (grrr2))) {
                        return boost::static_pointer_cast<Region> (r);
                }
        }
-       
+
        return boost::shared_ptr<Region>();
 }
 
 int
-Region::apply (Filter& filter)
+Region::apply (Filter& filter, Progress* progress)
 {
-       return filter.run (shared_from_this());
+       return filter.run (shared_from_this(), progress);
 }
 
 
@@ -1629,5 +1606,89 @@ Region::invalidate_transients ()
 {
        _valid_transients = false;
        _transients.clear ();
+
+       send_change (PropertyChange (Properties::valid_transients));
+}
+
+void
+Region::drop_sources ()
+{
+       for (SourceList::const_iterator i = _sources.begin (); i != _sources.end(); ++i) {
+               (*i)->dec_use_count ();
+       }
+
+       _sources.clear ();
+
+       for (SourceList::const_iterator i = _master_sources.begin (); i != _master_sources.end(); ++i) {
+               (*i)->dec_use_count ();
+       }
+
+       _master_sources.clear ();
+}
+
+void
+Region::use_sources (SourceList const & s)
+{
+       set<boost::shared_ptr<Source> > unique_srcs;
+
+       for (SourceList::const_iterator i = s.begin (); i != s.end(); ++i) {
+
+               _sources.push_back (*i);
+               (*i)->inc_use_count ();
+               _master_sources.push_back (*i);
+               (*i)->inc_use_count ();
+
+               /* connect only once to DropReferences, even if sources are replicated
+                */
+
+               if (unique_srcs.find (*i) == unique_srcs.end ()) {
+                       unique_srcs.insert (*i);
+                       (*i)->DropReferences.connect_same_thread (*this, boost::bind (&Region::source_deleted, this, boost::weak_ptr<Source>(*i)));
+               }
+       }
 }
 
+Trimmable::CanTrim
+Region::can_trim () const
+{
+       CanTrim ct = CanTrim (0);
+
+       if (locked()) {
+               return ct;
+       }
+
+       /* if not locked, we can always move the front later, and the end earlier
+        */
+
+       ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
+
+       if (start() != 0 || can_trim_start_before_source_start ()) {
+               ct = CanTrim (ct | FrontTrimEarlier);
+       }
+
+       if (!_sources.empty()) {
+               if ((start() + length()) < _sources.front()->length (0)) {
+                       ct = CanTrim (ct | EndTrimLater);
+               }
+       }
+
+       return ct;
+}
+
+uint32_t
+Region::max_source_level () const
+{
+       uint32_t lvl = 0;
+
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+               lvl = max (lvl, (*i)->level());
+       }
+
+       return lvl;
+}
+
+bool
+Region::is_compound () const
+{
+       return max_source_level() > 0;
+}