remove "edit" property from track/bus groups; use "select" property which should...
[ardour.git] / libs / ardour / region.cc
index 5a8970749553a56777071cf8833f82768fe3ea95..c74ce4e41998ec82797f64c6c0112e9a6ee5ce01 100644 (file)
 #include <algorithm>
 #include <sstream>
 
 #include <algorithm>
 #include <sstream>
 
-#include <glibmm/thread.h>
+#include <glibmm/threads.h>
 #include "pbd/xml++.h"
 #include "pbd/xml++.h"
-#include "pbd/stacktrace.h"
-#include "pbd/enumwriter.h"
 
 #include "ardour/debug.h"
 
 #include "ardour/debug.h"
-#include "ardour/region.h"
+#include "ardour/filter.h"
 #include "ardour/playlist.h"
 #include "ardour/playlist.h"
+#include "ardour/playlist_source.h"
+#include "ardour/profile.h"
+#include "ardour/region.h"
+#include "ardour/region_factory.h"
 #include "ardour/session.h"
 #include "ardour/source.h"
 #include "ardour/tempo.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 "ardour/utils.h"
 
 #include "i18n.h"
 
 
 #include "i18n.h"
 
@@ -45,7 +43,8 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
 using namespace ARDOUR;
 using namespace PBD;
 
-namespace ARDOUR { 
+namespace ARDOUR {
+       class Progress;
        namespace Properties {
                PBD::PropertyDescriptor<bool> muted;
                PBD::PropertyDescriptor<bool> opaque;
        namespace Properties {
                PBD::PropertyDescriptor<bool> muted;
                PBD::PropertyDescriptor<bool> opaque;
@@ -70,9 +69,10 @@ namespace ARDOUR {
                PBD::PropertyDescriptor<float> stretch;
                PBD::PropertyDescriptor<float> shift;
                PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
                PBD::PropertyDescriptor<float> stretch;
                PBD::PropertyDescriptor<float> shift;
                PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
+               PBD::PropertyDescriptor<uint64_t> layering_index;
        }
 }
        }
 }
-       
+
 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
 
 void
 PBD::Signal2<void,boost::shared_ptr<ARDOUR::Region>,const PropertyChange&> Region::RegionPropertyChanged;
 
 void
@@ -124,6 +124,8 @@ Region::make_property_quarks ()
        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));
        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));
+       Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
+       DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n",      Properties::layering_index.property_id));
 }
 
 void
 }
 
 void
@@ -148,63 +150,63 @@ Region::register_properties ()
        add_property (_length);
        add_property (_position);
        add_property (_sync_position);
        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);
        add_property (_ancestral_start);
        add_property (_ancestral_length);
        add_property (_stretch);
        add_property (_shift);
        add_property (_position_lock_style);
+       add_property (_layering_index);
 }
 
 #define REGION_DEFAULT_STATE(s,l) \
 }
 
 #define REGION_DEFAULT_STATE(s,l) \
-       _muted (Properties::muted, false)            \
+       _sync_marked (Properties::sync_marked, false) \
+       , _left_of_split (Properties::left_of_split, false) \
+       , _right_of_split (Properties::right_of_split, 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)) \
+       , _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) \
        , _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) \
        , _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) \
        , _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)
+       , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
+       , _layering_index (Properties::layering_index, 0)
 
 #define REGION_COPY_STATE(other) \
 
 #define REGION_COPY_STATE(other) \
-         _muted (Properties::muted, other->_muted)                     \
+         _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) \
+       , _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) \
+        , _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)    \
        , _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) \
        , _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)             \
        , _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)
+       , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
+       , _layering_index (Properties::layering_index, other->_layering_index)
 
 /* derived-from-derived constructor (no sources in constructor) */
 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
 
 /* derived-from-derived constructor (no sources in constructor) */
 Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
@@ -214,9 +216,7 @@ Region::Region (Session& s, framepos_t start, framecnt_t length, const string& n
        , _last_length (length)
        , _last_position (0)
        , _first_edit (EditChangesNothing)
        , _last_length (length)
        , _last_position (0)
        , _first_edit (EditChangesNothing)
-       , _read_data_count(0)
-       , _last_layer_op(0)
-       , _pending_explicit_relayer (false)
+       , _layer (0)
 {
        register_properties ();
 
 {
        register_properties ();
 
@@ -231,9 +231,7 @@ Region::Region (const SourceList& srcs)
        , _last_length (0)
        , _last_position (0)
        , _first_edit (EditChangesNothing)
        , _last_length (0)
        , _last_position (0)
        , _first_edit (EditChangesNothing)
-       , _read_data_count(0)
-       , _last_layer_op (0)
-       , _pending_explicit_relayer (false)
+       , _layer (0)
 {
        register_properties ();
 
 {
        register_properties ();
 
@@ -253,10 +251,7 @@ Region::Region (boost::shared_ptr<const Region> other)
        , _last_length (other->_last_length)
        , _last_position(other->_last_position) \
        , _first_edit (EditChangesNothing)
        , _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)
-
+       , _layer (other->_layer)
 {
        register_properties ();
 
 {
        register_properties ();
 
@@ -278,14 +273,14 @@ Region::Region (boost::shared_ptr<const Region> other)
        /* 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.
        /* 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.
-                          
+
           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).
           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.
        */
           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->sync_marked()) {
                if (other->_start < other->_sync_position) {
                        /* sync pos was after the start point of the other region */
        if (other->sync_marked()) {
                if (other->_start < other->_sync_position) {
                        /* sync pos was after the start point of the other region */
@@ -326,10 +321,7 @@ Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
        , _last_length (other->_last_length)
        , _last_position(other->_last_position) \
        , _first_edit (EditChangesNothing)
        , _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)
-
+       , _layer (other->_layer)
 {
        register_properties ();
 
 {
        register_properties ();
 
@@ -344,12 +336,12 @@ Region::Region (boost::shared_ptr<const Region> other, frameoffset_t offset)
        use_sources (other->_sources);
 
        _start = other->_start + offset;
        use_sources (other->_sources);
 
        _start = other->_start + offset;
-               
+
        /* 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.
        */
        /* 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.
        */
-               
+
        if (other->sync_marked()) {
                if (other->_sync_position < _start) {
                        _sync_marked = false;
        if (other->sync_marked()) {
                if (other->_sync_position < _start) {
                        _sync_marked = false;
@@ -384,9 +376,7 @@ Region::Region (boost::shared_ptr<const Region> other, const SourceList& srcs)
        , _last_length (other->_last_length)
        , _last_position (other->_last_position)
        , _first_edit (EditChangesID)
        , _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)
+       , _layer (other->_layer)
 {
        register_properties ();
 
 {
        register_properties ();
 
@@ -423,6 +413,7 @@ Region::set_name (const std::string& str)
        if (_name != str) {
                SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
                assert(_name == str);
        if (_name != str) {
                SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
                assert(_name == str);
+
                send_change (Properties::name);
        }
 
                send_change (Properties::name);
        }
 
@@ -430,7 +421,7 @@ Region::set_name (const std::string& str)
 }
 
 void
 }
 
 void
-Region::set_length (framecnt_t len, void */*src*/)
+Region::set_length (framecnt_t len)
 {
        //cerr << "Region::set_length() len = " << len << endl;
        if (locked()) {
 {
        //cerr << "Region::set_length() len = " << len << endl;
        if (locked()) {
@@ -490,6 +481,7 @@ Region::first_edit ()
                _first_edit = EditChangesNothing;
 
                send_change (Properties::name);
                _first_edit = EditChangesNothing;
 
                send_change (Properties::name);
+
                RegionFactory::CheckNewRegion (shared_from_this());
        }
 }
                RegionFactory::CheckNewRegion (shared_from_this());
        }
 }
@@ -515,7 +507,7 @@ Region::at_natural_position () const
 }
 
 void
 }
 
 void
-Region::move_to_natural_position (void *src)
+Region::move_to_natural_position ()
 {
        boost::shared_ptr<Playlist> pl (playlist());
 
 {
        boost::shared_ptr<Playlist> pl (playlist());
 
@@ -526,7 +518,7 @@ Region::move_to_natural_position (void *src)
        boost::shared_ptr<Region> whole_file_region = get_parent();
 
        if (whole_file_region) {
        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);
        }
 }
 
        }
 }
 
@@ -547,15 +539,11 @@ Region::set_position_lock_style (PositionLockStyle ps)
        if (_position_lock_style != ps) {
 
                boost::shared_ptr<Playlist> pl (playlist());
        if (_position_lock_style != ps) {
 
                boost::shared_ptr<Playlist> pl (playlist());
-               
-               if (!pl) {
-                       return;
-               }
-               
+
                _position_lock_style = ps;
                _position_lock_style = ps;
-               
+
                if (_position_lock_style == MusicTime) {
                if (_position_lock_style == MusicTime) {
-                       _session.tempo_map().bbt_time (_position, _bbt_time);
+                       _session.bbt_time (_position, _bbt_time);
                }
 
                send_change (Properties::position_lock_style);
                }
 
                send_change (Properties::position_lock_style);
@@ -563,7 +551,7 @@ Region::set_position_lock_style (PositionLockStyle ps)
 }
 
 void
 }
 
 void
-Region::update_position_after_tempo_map_change ()
+Region::update_after_tempo_map_change ()
 {
        boost::shared_ptr<Playlist> pl (playlist());
 
 {
        boost::shared_ptr<Playlist> pl (playlist());
 
@@ -582,7 +570,7 @@ Region::update_position_after_tempo_map_change ()
 }
 
 void
 }
 
 void
-Region::set_position (framepos_t pos, void* /*src*/)
+Region::set_position (framepos_t pos)
 {
        if (!can_move()) {
                return;
 {
        if (!can_move()) {
                return;
@@ -594,14 +582,19 @@ Region::set_position (framepos_t pos, void* /*src*/)
           a GUI that has moved its representation already.
        */
        send_change (Properties::position);
           a GUI that has moved its representation already.
        */
        send_change (Properties::position);
-       
+
 }
 
 void
 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
 {
 }
 
 void
 Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
 {
+       /* We emit a change of Properties::position even if the position hasn't changed
+          (see Region::set_position), so we must always set this up so that
+          e.g. Playlist::notify_region_moved doesn't use an out-of-date last_position.
+       */
+       _last_position = _position;
+       
        if (_position != pos) {
        if (_position != pos) {
-               _last_position = _position;
                _position = pos;
 
                /* check that the new _position wouldn't make the current
                _position = pos;
 
                /* check that the new _position wouldn't make the current
@@ -623,40 +616,16 @@ Region::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
        }
 }
 
        }
 }
 
-void
-Region::set_position_on_top (framepos_t pos, void* /*src*/)
-{
-       if (locked()) {
-               return;
-       }
-
-       if (_position != pos) {
-               set_position_internal (pos, true);
-       }
-
-       boost::shared_ptr<Playlist> pl (playlist());
-
-       if (pl) {
-               pl->raise_region_to_top (shared_from_this ());
-       }
-
-       /* 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::recompute_position_from_lock_style ()
 {
        if (_position_lock_style == MusicTime) {
 void
 Region::recompute_position_from_lock_style ()
 {
        if (_position_lock_style == MusicTime) {
-               _session.tempo_map().bbt_time (_position, _bbt_time);
+               _session.bbt_time (_position, _bbt_time);
        }
 }
 
 void
        }
 }
 
 void
-Region::nudge_position (frameoffset_t n, void* /*src*/)
+Region::nudge_position (frameoffset_t n)
 {
        if (locked()) {
                return;
 {
        if (locked()) {
                return;
@@ -697,7 +666,7 @@ Region::set_ancestral_data (framepos_t s, framecnt_t l, float st, float sh)
 }
 
 void
 }
 
 void
-Region::set_start (framepos_t pos, void* /*src*/)
+Region::set_start (framepos_t pos)
 {
        if (locked() || position_locked()) {
                return;
 {
        if (locked() || position_locked()) {
                return;
@@ -713,7 +682,7 @@ Region::set_start (framepos_t pos, void* /*src*/)
                        return;
                }
 
                        return;
                }
 
-               _start = pos;
+               set_start_internal (pos);
                _whole_file = false;
                first_edit ();
                invalidate_transients ();
                _whole_file = false;
                first_edit ();
                invalidate_transients ();
@@ -723,11 +692,12 @@ Region::set_start (framepos_t pos, void* /*src*/)
 }
 
 void
 }
 
 void
-Region::trim_start (framepos_t new_position, void */*src*/)
+Region::trim_start (framepos_t new_position)
 {
        if (locked() || position_locked()) {
                return;
        }
 {
        if (locked() || position_locked()) {
                return;
        }
+
        framepos_t new_start;
        frameoffset_t const start_shift = new_position - _position;
 
        framepos_t new_start;
        frameoffset_t const start_shift = new_position - _position;
 
@@ -750,7 +720,7 @@ Region::trim_start (framepos_t new_position, void */*src*/)
                } else {
                        new_start = _start + start_shift;
                }
                } else {
                        new_start = _start + start_shift;
                }
-               
+
        } else {
                return;
        }
        } else {
                return;
        }
@@ -759,7 +729,7 @@ Region::trim_start (framepos_t new_position, void */*src*/)
                return;
        }
 
                return;
        }
 
-       _start = new_start;
+       set_start_internal (new_start);
        _whole_file = false;
        first_edit ();
 
        _whole_file = false;
        first_edit ();
 
@@ -767,25 +737,25 @@ Region::trim_start (framepos_t new_position, void */*src*/)
 }
 
 void
 }
 
 void
-Region::trim_front (framepos_t new_position, void *src)
+Region::trim_front (framepos_t new_position)
 {
 {
-       modify_front (new_position, false, src);
+       modify_front (new_position, false);
 }
 
 void
 }
 
 void
-Region::cut_front (framepos_t new_position, void *src)
+Region::cut_front (framepos_t new_position)
 {
 {
-       modify_front (new_position, true, src);
+       modify_front (new_position, true);
 }
 
 void
 }
 
 void
-Region::cut_end (framepos_t new_endpoint, void *src)
+Region::cut_end (framepos_t new_endpoint)
 {
 {
-       modify_end (new_endpoint, true, src);
+       modify_end (new_endpoint, true);
 }
 
 void
 }
 
 void
-Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
+Region::modify_front (framepos_t new_position, bool reset_fade)
 {
        if (locked()) {
                return;
 {
        if (locked()) {
                return;
@@ -801,7 +771,7 @@ Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
        }
 
        if (new_position < end) { /* can't trim it zero or negative length */
        }
 
        if (new_position < end) { /* can't trim it zero or negative length */
-               
+
                framecnt_t newlen = 0;
                framepos_t delta = 0;
 
                framecnt_t newlen = 0;
                framepos_t delta = 0;
 
@@ -809,7 +779,7 @@ Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
                        /* can't trim it back past where source position zero is located */
                        new_position = max (new_position, source_zero);
                }
                        /* 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);
                if (new_position > _position) {
                        newlen = _length - (new_position - _position);
                        delta = -1 * (new_position - _position);
@@ -817,17 +787,17 @@ Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
                        newlen = _length + (_position - new_position);
                        delta = _position - new_position;
                }
                        newlen = _length + (_position - new_position);
                        delta = _position - new_position;
                }
-               
-               trim_to_internal (new_position, newlen, src);
-               
+
+               trim_to_internal (new_position, newlen);
+
                if (reset_fade) {
                        _right_of_split = true;
                }
                if (reset_fade) {
                        _right_of_split = true;
                }
-       
+
                if (!property_changes_suspended()) {
                        recompute_at_start ();
                }
                if (!property_changes_suspended()) {
                        recompute_at_start ();
                }
-               
+
                if (_transients.size() > 0){
                        adjust_transients(delta);
                }
                if (_transients.size() > 0){
                        adjust_transients(delta);
                }
@@ -835,14 +805,14 @@ Region::modify_front (framepos_t new_position, bool reset_fade, void *src)
 }
 
 void
 }
 
 void
-Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
+Region::modify_end (framepos_t new_endpoint, bool reset_fade)
 {
        if (locked()) {
                return;
        }
 
        if (new_endpoint > _position) {
 {
        if (locked()) {
                return;
        }
 
        if (new_endpoint > _position) {
-               trim_to_internal (_position, new_endpoint - _position +1, this);
+               trim_to_internal (_position, new_endpoint - _position);
                if (reset_fade) {
                        _left_of_split = true;
                }
                if (reset_fade) {
                        _left_of_split = true;
                }
@@ -857,19 +827,19 @@ Region::modify_end (framepos_t new_endpoint, bool reset_fade, void* /*src*/)
  */
 
 void
  */
 
 void
-Region::trim_end (framepos_t new_endpoint, void* src)
+Region::trim_end (framepos_t new_endpoint)
 {
 {
-       modify_end (new_endpoint, false, src);
+       modify_end (new_endpoint, false);
 }
 
 void
 }
 
 void
-Region::trim_to (framepos_t position, framecnt_t length, void *src)
+Region::trim_to (framepos_t position, framecnt_t length)
 {
        if (locked()) {
                return;
        }
 
 {
        if (locked()) {
                return;
        }
 
-       trim_to_internal (position, length, src);
+       trim_to_internal (position, length);
 
        if (!property_changes_suspended()) {
                recompute_at_start ();
 
        if (!property_changes_suspended()) {
                recompute_at_start ();
@@ -878,7 +848,7 @@ Region::trim_to (framepos_t position, framecnt_t length, void *src)
 }
 
 void
 }
 
 void
-Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
+Region::trim_to_internal (framepos_t position, framecnt_t length)
 {
        framepos_t new_start;
 
 {
        framepos_t new_start;
 
@@ -915,16 +885,18 @@ Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
        PropertyChange what_changed;
 
        if (_start != new_start) {
        PropertyChange what_changed;
 
        if (_start != new_start) {
-               _start = new_start;
+               set_start_internal (new_start);
                what_changed.add (Properties::start);
        }
                what_changed.add (Properties::start);
        }
-       if (_length != length) {
-               if (!property_changes_suspended()) {
-                       _last_length = _length;
-               }
-               set_length_internal (length);
-               what_changed.add (Properties::length);
-       }
+
+       /* 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 (!property_changes_suspended()) {
                        _last_position = _position;
        if (_position != position) {
                if (!property_changes_suspended()) {
                        _last_position = _position;
@@ -933,6 +905,14 @@ Region::trim_to_internal (framepos_t position, framecnt_t length, void */*src*/)
                what_changed.add (Properties::position);
        }
 
                what_changed.add (Properties::position);
        }
 
+       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;
        _whole_file = false;
 
        PropertyChange start_and_length;
@@ -1023,6 +1003,7 @@ Region::set_sync_position (framepos_t absolute_pos)
                if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
                if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
+
                send_change (Properties::sync_position);
        }
 }
                send_change (Properties::sync_position);
        }
 }
@@ -1035,6 +1016,7 @@ Region::clear_sync_position ()
                if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
                if (!property_changes_suspended()) {
                        maybe_uncopy ();
                }
+
                send_change (Properties::sync_position);
        }
 }
                send_change (Properties::sync_position);
        }
 }
@@ -1132,11 +1114,7 @@ Region::lower_to_bottom ()
 void
 Region::set_layer (layer_t l)
 {
 void
 Region::set_layer (layer_t l)
 {
-       if (_layer != l) {
-               _layer = l;
-
-               send_change (Properties::layer);
-       }
+       _layer = l;
 }
 
 XMLNode&
 }
 
 XMLNode&
@@ -1150,7 +1128,7 @@ Region::state ()
 
        add_properties (*node);
 
 
        add_properties (*node);
 
-       _id.print (buf, sizeof (buf));
+       id().print (buf, sizeof (buf));
        node->add_property ("id", buf);
        node->add_property ("type", _type.to_string());
 
        node->add_property ("id", buf);
        node->add_property ("type", _type.to_string());
 
@@ -1191,6 +1169,28 @@ Region::state ()
                node->add_property (buf2, buf);
        }
 
                node->add_property (buf2, buf);
        }
 
+       /* Only store nested sources for the whole-file region that acts
+          as the parent/root of all regions using it.
+       */
+
+       if (_whole_file && max_source_level() > 0) {
+
+               XMLNode* nested_node = new XMLNode (X_("NestedSource"));
+
+               /* region is compound - get its playlist and
+                  store that before we list the region that
+                  needs it ...
+               */
+
+               for (SourceList::const_iterator s = _sources.begin(); s != _sources.end(); ++s) {
+                       nested_node->add_child_nocopy ((*s)->get_state ());
+               }
+
+               if (nested_node) {
+                       node->add_child_nocopy (*nested_node);
+               }
+       }
+
        if (_extra_xml) {
                node->add_child_copy (*_extra_xml);
        }
        if (_extra_xml) {
                node->add_child_copy (*_extra_xml);
        }
@@ -1216,11 +1216,11 @@ Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_c
 {
        const XMLProperty* prop;
 
 {
        const XMLProperty* prop;
 
+       Stateful::save_extra_xml (node);
+
        what_changed = set_values (node);
 
        what_changed = set_values (node);
 
-       if ((prop = node.property (X_("id")))) {
-               _id = prop->value();
-       }
+       set_id (node);
 
        if (_position_lock_style == MusicTime) {
                if ((prop = node.property ("bbt-position")) == 0) {
 
        if (_position_lock_style == MusicTime) {
                if ((prop = node.property ("bbt-position")) == 0) {
@@ -1242,37 +1242,22 @@ Region::_set_state (const XMLNode& node, int /*version*/, PropertyChange& what_c
        if (_stretch == 0.0f) {
                _stretch = 1.0f;
        }
        if (_stretch == 0.0f) {
                _stretch = 1.0f;
        }
-       
+
        if (_shift == 0.0f) {
                _shift = 1.0f;
        }
 
        if (_shift == 0.0f) {
                _shift = 1.0f;
        }
 
-       const XMLNodeList& nlist = node.children();
-
-       for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-               XMLNode *child;
-
-               child = (*niter);
-
-               if (child->name () == "Extra") {
-                       delete _extra_xml;
-                       _extra_xml = new XMLNode (*child);
-                       break;
-               }
-       }
-
        if (send) {
                send_change (what_changed);
        }
        if (send) {
                send_change (what_changed);
        }
-       
+
        /* 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);
                }
        }
        /* 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);
                }
        }
-       
+
 
        return 0;
 }
 
        return 0;
 }
@@ -1305,8 +1290,8 @@ Region::send_change (const PropertyChange& what_changed)
 
        Stateful::send_change (what_changed);
 
 
        Stateful::send_change (what_changed);
 
-       if (!Stateful::frozen()) {
-               
+       if (!Stateful::property_changes_suspended()) {
+
                /* Try and send a shared_pointer unless this is part of the constructor.
                   If so, do nothing.
                */
                /* Try and send a shared_pointer unless this is part of the constructor.
                   If so, do nothing.
                */
@@ -1320,16 +1305,10 @@ Region::send_change (const PropertyChange& what_changed)
        }
 }
 
        }
 }
 
-void
-Region::set_last_layer_op (uint64_t when)
-{
-       _last_layer_op = when;
-}
-
 bool
 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
 {
 bool
 Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
 {
-       return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
+       return coverage (other->first_frame(), other->last_frame()) != Evoral::OverlapNone;
 }
 
 bool
 }
 
 bool
@@ -1404,7 +1383,7 @@ Region::source_equivalent (boost::shared_ptr<const Region> other) const
 {
        if (!other)
                return false;
 {
        if (!other)
                return false;
-       
+
        if ((_sources.size() != other->_sources.size()) ||
            (_master_sources.size() != other->_master_sources.size())) {
                return false;
        if ((_sources.size() != other->_sources.size()) ||
            (_master_sources.size() != other->_master_sources.size())) {
                return false;
@@ -1428,6 +1407,27 @@ Region::source_equivalent (boost::shared_ptr<const Region> other) const
        return true;
 }
 
        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
 {
 bool
 Region::uses_source (boost::shared_ptr<const Source> source) const
 {
@@ -1435,7 +1435,16 @@ Region::uses_source (boost::shared_ptr<const Source> source) const
                if (*i == source) {
                        return true;
                }
                if (*i == source) {
                        return true;
                }
+
+               boost::shared_ptr<PlaylistSource> ps = boost::dynamic_pointer_cast<PlaylistSource> (*i);
+
+               if (ps) {
+                       if (ps->playlist()->uses_source (source)) {
+                               return true;
+                       }
+               }
        }
        }
+
        return false;
 }
 
        return false;
 }
 
@@ -1541,7 +1550,7 @@ Region::invalidate_transients ()
 {
        _valid_transients = false;
        _transients.clear ();
 {
        _valid_transients = false;
        _transients.clear ();
-       
+
        send_change (PropertyChange (Properties::valid_transients));
 }
 
        send_change (PropertyChange (Properties::valid_transients));
 }
 
@@ -1592,7 +1601,7 @@ Region::can_trim () const
                return ct;
        }
 
                return ct;
        }
 
-       /* if not locked, we can always move the front later, and the end earlier 
+       /* if not locked, we can always move the front later, and the end earlier
         */
 
        ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
         */
 
        ct = CanTrim (ct | FrontTrimLater | EndTrimEarlier);
@@ -1609,4 +1618,65 @@ Region::can_trim () const
 
        return ct;
 }
 
        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;
+}
+
+void
+Region::post_set (const PropertyChange& pc)
+{
+       if (pc.contains (Properties::position)) {
+               recompute_position_from_lock_style ();
+       }
+}
+
+void
+Region::set_start_internal (framecnt_t s)
+{
+       _start = s;
+}
+
+framepos_t
+Region::earliest_possible_position () const
+{
+       if (_start > _position) {
+               return 0;
+       } else {
+               return _position - _start;
+       }
+}
+
+framecnt_t
+Region::latest_possible_frame () const
+{
+       framecnt_t minlen = max_framecnt;
+
+       for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+               /* non-audio regions have a length that may vary based on their
+                * position, so we have to pass it in the call.
+                */
+               minlen = min (minlen, (*i)->length (_position));
+       }
+
+       /* the latest possible last frame is determined by the current
+        * position, plus the shortest source extent past _start.
+        */
+
+       return _position + (minlen - _start) - 1;
+}
+