Fix VST build.
[ardour.git] / libs / ardour / playlist.cc
index e3d1831470764985e1f7b7c9d86a32553d985e21..971ed8b2e836173a56b0c8d91d24aa23c1e15e6d 100644 (file)
@@ -17,6 +17,7 @@
 
 */
 
+#include <stdint.h>
 #include <set>
 #include <fstream>
 #include <algorithm>
 #include <string>
 #include <climits>
 
-#include <sigc++/bind.h>
+#include <boost/lexical_cast.hpp>
 
 #include "pbd/failed_constructor.h"
-#include "pbd/stl_delete.h"
+#include "pbd/stateful_diff_command.h"
 #include "pbd/xml++.h"
 #include "pbd/stacktrace.h"
 
@@ -39,6 +40,7 @@
 #include "ardour/region_factory.h"
 #include "ardour/playlist_factory.h"
 #include "ardour/transient_detector.h"
+#include "ardour/session_playlists.h"
 
 #include "i18n.h"
 
@@ -46,6 +48,12 @@ using namespace std;
 using namespace ARDOUR;
 using namespace PBD;
 
+namespace ARDOUR {
+namespace Properties {
+PBD::PropertyDescriptor<bool> regions;
+}
+}
+
 struct ShowMeTheList {
     ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
     ~ShowMeTheList () {
@@ -90,32 +98,101 @@ struct RegionSortByLastLayerOp {
     }
 };
 
+void
+Playlist::make_property_quarks ()
+{
+        Properties::regions.property_id = g_quark_from_static_string (X_("regions"));
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for regions = %1\n",         Properties::regions.property_id));
+}
+
+RegionListProperty::RegionListProperty (Playlist& pl)
+        : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
+        , _playlist (pl)
+{
+       
+}
+
+RegionListProperty::RegionListProperty (RegionListProperty const & p)
+       : PBD::SequenceProperty<std::list<boost::shared_ptr<Region> > > (p)
+       , _playlist (p._playlist)
+{
+
+}
+
+RegionListProperty *
+RegionListProperty::clone () const
+{
+       return new RegionListProperty (*this);
+}
+
+RegionListProperty *
+RegionListProperty::create () const
+{
+       return new RegionListProperty (_playlist);
+}
+
+void
+RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNode & node) const
+{
+       /* All regions (even those which are deleted) have their state saved by other
+          code, so we can just store ID here.
+       */
+       
+       node.add_property ("id", region->id().to_s ());
+}
+
+boost::shared_ptr<Region>
+RegionListProperty::get_content_from_xml (XMLNode const & node) const
+{
+       XMLProperty const * prop = node.property ("id");
+       assert (prop);
+
+       PBD::ID id (prop->value ());
+
+        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
+        
+        if (!ret) {
+                ret = RegionFactory::region_by_id (id);
+        }
+
+        return ret;
+}
 
 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
        : SessionObject(sess, nom)
+        , regions (*this)
        , _type(type)
 {
        init (hide);
        first_set_state = false;
        _name = nom;
+        _set_sort_id ();
 
 }
 
 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
        : SessionObject(sess, "unnamed playlist")
-       , _type(type)
+        , regions (*this)      
+        , _type(type)
+
 {
+#ifndef NDEBUG
        const XMLProperty* prop = node.property("type");
        assert(!prop || DataType(prop->value()) == _type);
+#endif
 
        init (hide);
        _name = "unnamed"; /* reset by set_state */
+        _set_sort_id ();
 
        /* set state called by derived class */
 }
 
 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
-       : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+       : SessionObject(other->_session, namestr)
+        , regions (*this)
+       , _type(other->_type)
+       , _orig_diskstream_id (other->_orig_diskstream_id)
 {
        init (hide);
 
@@ -146,24 +223,27 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
        freeze_length = other->freeze_length;
 }
 
-Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
-       : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
+       : SessionObject(other->_session, str)
+        , regions (*this)
+       , _type(other->_type)
+       , _orig_diskstream_id (other->_orig_diskstream_id)
 {
        RegionLock rlock2 (const_cast<Playlist*> (other.get()));
 
-       nframes_t end = start + cnt - 1;
+       framepos_t end = start + cnt - 1;
 
        init (hide);
 
        in_set_state++;
 
-       for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
+       for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
 
                boost::shared_ptr<Region> region;
                boost::shared_ptr<Region> new_region;
-               nframes_t offset = 0;
-               nframes_t position = 0;
-               nframes_t len = 0;
+               frameoffset_t offset = 0;
+               framepos_t position = 0;
+               framecnt_t len = 0;
                string    new_name;
                OverlapType overlap;
 
@@ -200,9 +280,16 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nf
                        break;
                }
 
-               _session.region_name (new_name, region->name(), false);
+               RegionFactory::region_name (new_name, region->name(), false);
+
+               PropertyList plist; 
+
+               plist.add (Properties::start, region->start() + offset);
+               plist.add (Properties::length, len);
+               plist.add (Properties::name, new_name);
+               plist.add (Properties::layer, region->layer());
 
-               new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
+               new_region = RegionFactory::RegionFactory::create (region, plist);
 
                add_region_internal (new_region, position);
        }
@@ -238,17 +325,21 @@ Playlist::copy_regions (RegionList& newlist) const
        RegionLock rlock (const_cast<Playlist *> (this));
 
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               newlist.push_back (RegionFactory::RegionFactory::create (*i));
+               newlist.push_back (RegionFactory::RegionFactory::create (*i, true));
        }
 }
 
 void
 Playlist::init (bool hide)
 {
+        add_property (regions);
+        _xml_node_name = X_("Playlist");
+
        g_atomic_int_set (&block_notifications, 0);
        g_atomic_int_set (&ignore_state_changes, 0);
-       pending_modified = false;
+       pending_contents_change = false;
        pending_length = false;
+       pending_layering = false;
        first_set_state = true;
        _refcnt = 0;
        _hidden = hide;
@@ -256,6 +347,7 @@ Playlist::init (bool hide)
        _shuffling = false;
        _nudging = false;
        in_set_state = 0;
+        in_update = false;
        _edit_mode = Config->get_edit_mode();
        in_flush = false;
        in_partition = false;
@@ -266,12 +358,16 @@ Playlist::init (bool hide)
        freeze_length = 0;
        _explicit_relayering = false;
 
-       Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
+       _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
+       _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
+       
+       ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
 }
 
 Playlist::~Playlist ()
 {
        DEBUG_TRACE (DEBUG::Destruction, string_compose ("Playlist %1 destructor\n", _name));
+
        {
                RegionLock rl (this);
 
@@ -283,6 +379,32 @@ Playlist::~Playlist ()
        /* GoingAway must be emitted by derived classes */
 }
 
+void
+Playlist::_set_sort_id ()
+{
+        /*
+          Playlists are given names like <track name>.<id>
+          or <track name>.<edit group name>.<id> where id
+          is an integer. We extract the id and sort by that.
+        */
+        
+        size_t dot_position = _name.val().find_last_of(".");
+
+        if (dot_position == string::npos) {
+                _sort_id = 0;
+        } else {
+                string t = _name.val().substr(dot_position + 1);
+
+                try {
+                        _sort_id = boost::lexical_cast<int>(t);
+                }
+
+                catch (boost::bad_lexical_cast e) {
+                        _sort_id = 0;
+                }
+        }
+}
+
 bool
 Playlist::set_name (const string& str)
 {
@@ -294,9 +416,13 @@ Playlist::set_name (const string& str)
 
        if (_refcnt > 2) {
                return false;
-       } else {
-               return SessionObject::set_name(str);
-       }
+       } 
+
+        bool ret =  SessionObject::set_name(str);
+        if (ret) {
+                _set_sort_id ();
+        }
+        return ret;
 }
 
 /***********************************************************************
@@ -307,6 +433,20 @@ Playlist::set_name (const string& str)
  the lock (e.g. to read from the playlist).
  ***********************************************************************/
 
+void
+Playlist::begin_undo ()
+{
+        in_update = true;
+       freeze ();
+}
+
+void
+Playlist::end_undo ()
+{
+       thaw (true);
+        in_update = false;
+}
+
 void
 Playlist::freeze ()
 {
@@ -314,11 +454,12 @@ Playlist::freeze ()
        g_atomic_int_inc (&ignore_state_changes);
 }
 
+/** @param from_undo true if this thaw is triggered by the end of an undo on this playlist */
 void
-Playlist::thaw ()
+Playlist::thaw (bool from_undo)
 {
        g_atomic_int_dec_and_test (&ignore_state_changes);
-       release_notifications ();
+       release_notifications (from_undo);
 }
 
 
@@ -326,25 +467,38 @@ void
 Playlist::delay_notifications ()
 {
        g_atomic_int_inc (&block_notifications);
-       freeze_length = _get_maximum_extent();
+       freeze_length = _get_extent().second;
 }
 
+/** @param from_undo true if this release is triggered by the end of an undo on this playlist */
 void
-Playlist::release_notifications ()
+Playlist::release_notifications (bool from_undo)
 {
        if (g_atomic_int_dec_and_test (&block_notifications)) {
-               flush_notifications ();
+               flush_notifications (from_undo);
+        }
+
+}
+
+void
+Playlist::notify_contents_changed ()
+{
+       if (holding_state ()) {
+               pending_contents_change = true;
+       } else {
+               pending_contents_change = false;
+               ContentsChanged(); /* EMIT SIGNAL */
        }
 }
 
 void
-Playlist::notify_modified ()
+Playlist::notify_layering_changed ()
 {
        if (holding_state ()) {
-               pending_modified = true;
+               pending_layering = true;
        } else {
-               pending_modified = false;
-               Modified(); /* EMIT SIGNAL */
+               pending_layering = false;
+               LayeringChanged(); /* EMIT SIGNAL */
        }
 }
 
@@ -353,7 +507,7 @@ Playlist::notify_region_removed (boost::shared_ptr<Region> r)
 {
        if (holding_state ()) {
                pending_removes.insert (r);
-               pending_modified = true;
+               pending_contents_change = true;
                pending_length = true;
        } else {
                /* this might not be true, but we have to act
@@ -361,16 +515,16 @@ Playlist::notify_region_removed (boost::shared_ptr<Region> r)
                */
                pending_length = false;
                LengthChanged (); /* EMIT SIGNAL */
-               pending_modified = false;
+               pending_contents_change = false;
                RegionRemoved (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
-               Modified (); /* EMIT SIGNAL */
+               ContentsChanged (); /* EMIT SIGNAL */
        }
 }
 
 void
 Playlist::notify_region_moved (boost::shared_ptr<Region> r)
 {
-       Evoral::RangeMove<nframes_t> const move (r->last_position (), r->length (), r->position ());
+       Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
 
        if (holding_state ()) {
 
@@ -378,9 +532,9 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
 
        } else {
 
-               list< Evoral::RangeMove<nframes_t> > m;
+               list< Evoral::RangeMove<framepos_t> > m;
                m.push_back (move);
-               RangesMoved (m);
+               RangesMoved (m, false);
        }
 
 }
@@ -394,14 +548,15 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
 
        if (holding_state()) {
                pending_adds.insert (r);
-               pending_modified = true;
+               pending_contents_change = true;
                pending_length = true;
        } else {
+                r->clear_changes ();
                pending_length = false;
                LengthChanged (); /* EMIT SIGNAL */
-               pending_modified = false;
+               pending_contents_change = false;
                RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
-               Modified (); /* EMIT SIGNAL */
+               ContentsChanged (); /* EMIT SIGNAL */
        }
 }
 
@@ -413,17 +568,20 @@ Playlist::notify_length_changed ()
        } else {
                pending_length = false;
                LengthChanged(); /* EMIT SIGNAL */
-               pending_modified = false;
-               Modified (); /* EMIT SIGNAL */
+               pending_contents_change = false;
+               ContentsChanged (); /* EMIT SIGNAL */
        }
 }
 
+/** @param from_undo true if this flush is triggered by the end of an undo on this playlist */
 void
-Playlist::flush_notifications ()
+Playlist::flush_notifications (bool from_undo)
 {
        set<boost::shared_ptr<Region> > dependent_checks_needed;
        set<boost::shared_ptr<Region> >::iterator s;
-       uint32_t n = 0;
+       uint32_t regions_changed = false;
+       bool check_length = false;
+       framecnt_t old_length = 0;
 
        if (in_flush) {
                return;
@@ -431,6 +589,14 @@ Playlist::flush_notifications ()
 
        in_flush = true;
 
+       if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
+               regions_changed = true;
+               if (!pending_length) {
+                       old_length = _get_extent ().second;
+                       check_length = true;
+               }
+       }
+
        /* we have no idea what order the regions ended up in pending
           bounds (it could be based on selection order, for example).
           so, to preserve layering in the "most recently moved is higher"
@@ -444,53 +610,74 @@ Playlist::flush_notifications ()
                if (_session.config.get_layer_model() == MoveAddHigher) {
                        timestamp_layer_op (*r);
                }
-
-               pending_length = true;
                dependent_checks_needed.insert (*r);
-
-               n++;
        }
 
        for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
                remove_dependents (*s);
+               // cerr << _name << " sends RegionRemoved\n";
                RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
-               n++;
        }
 
        for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
-               RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+               // cerr << _name << " sends RegionAdded\n";
+                /* don't emit RegionAdded signal until relayering is done,
+                   so that the region is fully setup by the time
+                   anyone hear's that its been added
+                */
                dependent_checks_needed.insert (*s);
-               n++;
        }
 
-       if ((freeze_length != _get_maximum_extent()) || pending_length) {
-               pending_length = 0;
+       if (check_length) {
+               if (old_length != _get_extent().second) {
+                       pending_length = true;
+                       // cerr << _name << " length has changed\n";
+               }
+       }
+
+       if (pending_length || (freeze_length != _get_extent().second)) {
+               pending_length = false;
+               // cerr << _name << " sends LengthChanged\n";
                LengthChanged(); /* EMIT SIGNAL */
-               n++;
        }
 
-       if (n || pending_modified) {
+       if (regions_changed || pending_contents_change) {
                if (!in_set_state) {
                        relayer ();
                }
-               pending_modified = false;
-               Modified (); /* EMIT SIGNAL */
+               pending_contents_change = false;
+               // cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
+               ContentsChanged (); /* EMIT SIGNAL */
+               // cerr << _name << "done contents change @ " << get_microseconds() << endl;
        }
 
+       for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
+                (*s)->clear_changes ();
+               RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+        }
+
        for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
                check_dependents (*s, false);
        }
 
        if (!pending_range_moves.empty ()) {
-               RangesMoved (pending_range_moves);
+               RangesMoved (pending_range_moves, from_undo);
        }
+       
+       clear_pending ();
 
+       in_flush = false;
+}
+
+void
+Playlist::clear_pending ()
+{
        pending_adds.clear ();
        pending_removes.clear ();
        pending_bounds.clear ();
        pending_range_moves.clear ();
-
-       in_flush = false;
+       pending_contents_change = false;
+       pending_length = false;
 }
 
 /*************************************************************
@@ -498,17 +685,17 @@ Playlist::flush_notifications ()
  *************************************************************/
 
 void
-Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times, bool auto_partition)
+Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, float times, bool auto_partition)
 {
        RegionLock rlock (this);
        times = fabs (times);
 
        int itimes = (int) floor (times);
 
-       nframes_t pos = position;
+       framepos_t pos = position;
 
        if (times == 1 && auto_partition){
-               partition(pos, (nframes_t) (pos + region->length()), true);
+               partition(pos - 1, (pos + region->length()), true);
        }
 
        if (itimes >= 1) {
@@ -523,19 +710,29 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa
        */
 
        for (int i = 0; i < itimes; ++i) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region);
+               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                add_region_internal (copy, pos);
                pos += region->length();
        }
 
-       nframes_t length = 0;
+       framecnt_t length = 0;
 
        if (floor (times) != times) {
-               length = (nframes_t) floor (region->length() * (times - floor (times)));
+               length = (framecnt_t) floor (region->length() * (times - floor (times)));
                string name;
-               _session.region_name (name, region->name(), false);
-               boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
-               add_region_internal (sub, pos);
+               RegionFactory::region_name (name, region->name(), false);
+
+               {
+                       PropertyList plist;
+                       
+                       plist.add (Properties::start, region->start());
+                       plist.add (Properties::length, length);
+                       plist.add (Properties::name, name);
+                       plist.add (Properties::layer, region->layer());
+
+                       boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+                       add_region_internal (sub, pos);
+               }
        }
 
        possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
@@ -554,7 +751,7 @@ Playlist::set_region_ownership ()
 }
 
 bool
-Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
+Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
 {
        if (region->data_type() != _type){
                return false;
@@ -562,10 +759,10 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
 
        RegionSortByPosition cmp;
 
-       nframes_t old_length = 0;
+       framecnt_t old_length = 0;
 
        if (!holding_state()) {
-                old_length = _get_maximum_extent();
+                old_length = _get_extent().second;
        }
 
        if (!first_set_state) {
@@ -582,8 +779,8 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
 
        possibly_splice_unlocked (position, region->length(), region);
 
-       if (!holding_state () && !in_set_state) {
-               /* layers get assigned from XML state */
+       if (!holding_state ()) {
+               /* layers get assigned from XML state, and are not reset during undo/redo */
                relayer ();
        }
 
@@ -595,21 +792,18 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
 
                check_dependents (region, false);
 
-               if (old_length != _get_maximum_extent()) {
+               if (old_length != _get_extent().second) {
                        notify_length_changed ();
                }
        }
 
-       region_state_changed_connections.push_back (
-               region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
-                                                         boost::weak_ptr<Region> (region)))
-               );
+       region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
 
        return true;
 }
 
 void
-Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
+Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
 {
        RegionLock rlock (this);
 
@@ -621,7 +815,7 @@ Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Regio
 
        _splicing = old_sp;
 
-       possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
+       possibly_splice_unlocked (pos, old->length() - newr->length());
 }
 
 void
@@ -635,10 +829,11 @@ int
 Playlist::remove_region_internal (boost::shared_ptr<Region> region)
 {
        RegionList::iterator i;
-       nframes_t old_length = 0;
+       framecnt_t old_length = 0;
+       int ret = -1;
 
        if (!holding_state()) {
-               old_length = _get_maximum_extent();
+               old_length = _get_extent().second;
        }
 
        if (!in_set_state) {
@@ -646,32 +841,33 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
                region->set_playlist (boost::weak_ptr<Playlist>());
        }
 
+       /* XXX should probably freeze here .... */
+
        for (i = regions.begin(); i != regions.end(); ++i) {
                if (*i == region) {
 
-                       nframes_t pos = (*i)->position();
-                       nframes64_t distance = (*i)->length();
+                       framepos_t pos = (*i)->position();
+                       framecnt_t distance = (*i)->length();
 
                        regions.erase (i);
 
                        possibly_splice_unlocked (pos, -distance);
 
                        if (!holding_state ()) {
-                               relayer ();
+                                relayer ();
                                remove_dependents (region);
 
-                               if (old_length != _get_maximum_extent()) {
+                               if (old_length != _get_extent().second) {
                                        notify_length_changed ();
                                }
                        }
 
                        notify_region_removed (region);
-                       return 0;
+                       ret = 0;
+                       break;
                }
        }
 
-
-
        return -1;
 }
 
@@ -705,19 +901,19 @@ Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, v
 }
 
 void
-Playlist::partition (nframes_t start, nframes_t end, bool cut)
+Playlist::partition (framepos_t start, framepos_t end, bool cut)
 {
        RegionList thawlist;
 
        partition_internal (start, end, cut, thawlist);
 
        for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
-               (*i)->thaw ("separation");
+               (*i)->resume_property_changes ();
        }
 }
 
 void
-Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
+Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
 {
        RegionList new_regions;
 
@@ -729,7 +925,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
                string new_name;
                RegionList::iterator tmp;
                OverlapType overlap;
-               nframes_t pos1, pos2, pos3, pos4;
+               framepos_t pos1, pos2, pos3, pos4;
 
                in_partition = true;
 
@@ -737,7 +933,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
                   get operated on as well.
                */
 
-               RegionList copy = regions;
+               RegionList copy = regions.rlist();
 
                for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
 
@@ -792,27 +988,46 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
                                if (!cutting) {
                                        /* "middle" ++++++ */
 
-                                       _session.region_name (new_name, current->name(), false);
-                                       region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
-                                                                       regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
+                                       RegionFactory::region_name (new_name, current->name(), false);
+
+                                       PropertyList plist;
+                                       
+                                       plist.add (Properties::start, current->start() + (pos2 - pos1));
+                                       plist.add (Properties::length, pos3 - pos2);
+                                       plist.add (Properties::name, new_name);
+                                       plist.add (Properties::layer, regions.size());
+                                       plist.add (Properties::automatic, true);
+                                       plist.add (Properties::left_of_split, true);
+                                       plist.add (Properties::right_of_split, true);
+
+                                       region = RegionFactory::create (current, plist);
                                        add_region_internal (region, start);
                                        new_regions.push_back (region);
                                }
 
                                /* "end" ====== */
 
-                               _session.region_name (new_name, current->name(), false);
-                               region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
-                                                               regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+                               RegionFactory::region_name (new_name, current->name(), false);
+
+                               PropertyList plist;
+                               
+                               plist.add (Properties::start, current->start() + (pos3 - pos1));
+                               plist.add (Properties::length, pos4 - pos3);
+                               plist.add (Properties::name, new_name);
+                               plist.add (Properties::layer, regions.size());
+                               plist.add (Properties::automatic, true);
+                               plist.add (Properties::right_of_split, true);
+                               
+                               region = RegionFactory::create (current, plist);
 
                                add_region_internal (region, end);
                                new_regions.push_back (region);
 
                                /* "front" ***** */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapEnd) {
 
@@ -830,9 +1045,18 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
 
                                        /* end +++++ */
 
-                                       _session.region_name (new_name, current->name(), false);
-                                       region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
-                                                                       Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
+                                       RegionFactory::region_name (new_name, current->name(), false);
+                                       
+                                       PropertyList plist;
+                                       
+                                       plist.add (Properties::start, current->start() + (pos2 - pos1));
+                                       plist.add (Properties::length, pos4 - pos2);
+                                       plist.add (Properties::name, new_name);
+                                       plist.add (Properties::layer, regions.size());
+                                       plist.add (Properties::automatic, true);
+                                       plist.add (Properties::left_of_split, true);
+
+                                       region = RegionFactory::create (current, plist);
 
                                        add_region_internal (region, start);
                                        new_regions.push_back (region);
@@ -840,9 +1064,9 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
 
                                /* front ****** */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapStart) {
 
@@ -864,9 +1088,18 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
 
                                if (!cutting) {
                                        /* front **** */
-                                       _session.region_name (new_name, current->name(), false);
-                                       region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
-                                                                       regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+                                       RegionFactory::region_name (new_name, current->name(), false);
+
+                                       PropertyList plist;
+                                       
+                                       plist.add (Properties::start, current->start());
+                                       plist.add (Properties::length, pos3 - pos1);
+                                       plist.add (Properties::name, new_name);
+                                       plist.add (Properties::layer, regions.size());
+                                       plist.add (Properties::automatic, true);
+                                       plist.add (Properties::right_of_split, true);
+
+                                       region = RegionFactory::create (current, plist);
 
                                        add_region_internal (region, pos1);
                                        new_regions.push_back (region);
@@ -874,7 +1107,7 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
 
                                /* end */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
                                current->trim_front (pos3, this);
                        } else if (overlap == OverlapExternal) {
@@ -912,11 +1145,11 @@ Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, Regi
 }
 
 boost::shared_ptr<Playlist>
-Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
+Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t, framecnt_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
 {
        boost::shared_ptr<Playlist> ret;
        boost::shared_ptr<Playlist> pl;
-       nframes_t start;
+       framepos_t start;
 
        if (ranges.empty()) {
                return boost::shared_ptr<Playlist>();
@@ -947,19 +1180,19 @@ Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nfra
 boost::shared_ptr<Playlist>
 Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
 {
-       boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
+       boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
        return cut_copy (pmf, ranges, result_is_hidden);
 }
 
 boost::shared_ptr<Playlist>
 Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
 {
-       boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
+       boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
        return cut_copy (pmf, ranges, result_is_hidden);
 }
 
 boost::shared_ptr<Playlist>
-Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
+Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
 {
        boost::shared_ptr<Playlist> the_copy;
        RegionList thawlist;
@@ -977,14 +1210,14 @@ Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
        partition_internal (start, start+cnt-1, true, thawlist);
 
        for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
-               (*i)->thaw ("playlist cut");
+               (*i)->resume_property_changes();
        }
 
        return the_copy;
 }
 
 boost::shared_ptr<Playlist>
-Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
+Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
 {
        char buf[32];
 
@@ -993,37 +1226,36 @@ Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
        new_name += '.';
        new_name += buf;
 
-       cnt = min (_get_maximum_extent() - start, cnt);
+       cnt = min (_get_extent().second - start, cnt);
        return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
 }
 
 int
-Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
+Playlist::paste (boost::shared_ptr<Playlist> other, framepos_t position, float times)
 {
        times = fabs (times);
-       nframes_t old_length;
 
        {
                RegionLock rl1 (this);
                RegionLock rl2 (other.get());
 
-               old_length = _get_maximum_extent();
+               framecnt_t const old_length = _get_extent().second;
 
                int itimes = (int) floor (times);
-               nframes_t pos = position;
-               nframes_t shift = other->_get_maximum_extent();
+               framepos_t pos = position;
+               framecnt_t const shift = other->_get_extent().second;
                layer_t top_layer = regions.size();
 
                while (itimes--) {
                        for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
-                               boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
+                               boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i, true);
 
                                /* put these new regions on top of all existing ones, but preserve
                                   the ordering they had in the original playlist.
                                */
 
                                copy_of_region->set_layer (copy_of_region->layer() + top_layer);
-                               add_region_internal (copy_of_region, copy_of_region->position() + pos);
+                               add_region_internal (copy_of_region, (*i)->position() + pos);
                        }
                        pos += shift;
                }
@@ -1031,7 +1263,7 @@ Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float ti
 
                /* XXX shall we handle fractional cases at some point? */
 
-               if (old_length != _get_maximum_extent()) {
+               if (old_length != _get_extent().second) {
                        notify_length_changed ();
                }
 
@@ -1043,34 +1275,43 @@ Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float ti
 
 
 void
-Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
+Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
 {
        times = fabs (times);
 
        RegionLock rl (this);
        int itimes = (int) floor (times);
-       nframes_t pos = position;
+       framepos_t pos = position + 1;
 
        while (itimes--) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region);
+               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
                add_region_internal (copy, pos);
                pos += region->length();
        }
 
        if (floor (times) != times) {
-               nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
+               framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
                string name;
-               _session.region_name (name, region->name(), false);
-               boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
-               add_region_internal (sub, pos);
+               RegionFactory::region_name (name, region->name(), false);
+               
+               {
+                       PropertyList plist;
+                       
+                       plist.add (Properties::start, region->start());
+                       plist.add (Properties::length, length);
+                       plist.add (Properties::name, name);
+                       
+                       boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+                       add_region_internal (sub, pos);
+               }
        }
 }
 
 void
-Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue)
+Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
 {
        RegionLock rlock (this);
-       RegionList copy (regions);
+       RegionList copy (regions.rlist());
        RegionList fixup;
 
        for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
@@ -1091,7 +1332,7 @@ Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bo
                   has to be done separately.
                */
 
-               if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
+               if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
                        fixup.push_back (*r);
                        continue;
                }
@@ -1105,10 +1346,10 @@ Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bo
 }
 
 void
-Playlist::split (nframes64_t at)
+Playlist::split (framepos_t at)
 {
        RegionLock rlock (this);
-       RegionList copy (regions);
+       RegionList copy (regions.rlist());
 
        /* use a copy since this operation can modify the region list
         */
@@ -1119,14 +1360,14 @@ Playlist::split (nframes64_t at)
 }
 
 void
-Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
 {
        RegionLock rl (this);
        _split_region (region, playlist_position);
 }
 
 void
-Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
 {
        if (!region->covers (playlist_position)) {
                return;
@@ -1139,8 +1380,8 @@ Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_po
 
        boost::shared_ptr<Region> left;
        boost::shared_ptr<Region> right;
-       nframes_t before;
-       nframes_t after;
+       frameoffset_t before;
+       frameoffset_t after;
        string before_name;
        string after_name;
 
@@ -1152,11 +1393,34 @@ Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_po
        before = playlist_position - region->position();
        after = region->length() - before;
 
-       _session.region_name (before_name, region->name(), false);
-       left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
+       RegionFactory::region_name (before_name, region->name(), false);
 
-       _session.region_name (after_name, region->name(), false);
-       right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
+       {
+               PropertyList plist;
+               
+               plist.add (Properties::length, before);
+               plist.add (Properties::name, before_name);
+               plist.add (Properties::left_of_split, true);
+
+               /* note: we must use the version of ::create with an offset here,
+                  since it supplies that offset to the Region constructor, which
+                  is necessary to get audio region gain envelopes right.
+               */
+               left = RegionFactory::create (region, 0, plist);
+       }
+
+       RegionFactory::region_name (after_name, region->name(), false);
+
+       {
+               PropertyList plist;
+               
+               plist.add (Properties::length, after);
+               plist.add (Properties::name, after_name);
+               plist.add (Properties::right_of_split, true);
+
+               /* same note as above */
+               right = RegionFactory::create (region, before, plist);
+       }
 
        add_region_internal (left, region->position());
        add_region_internal (right, region->position() + before);
@@ -1181,7 +1445,7 @@ Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_po
 }
 
 void
-Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::possibly_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
 {
        if (_splicing || in_set_state) {
                /* don't respond to splicing moves or state setting */
@@ -1194,7 +1458,7 @@ Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr
 }
 
 void
-Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::possibly_splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
 {
        if (_splicing || in_set_state) {
                /* don't respond to splicing moves or state setting */
@@ -1207,7 +1471,7 @@ Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::s
 }
 
 void
-Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
 {
        {
                RegionLock rl (this);
@@ -1216,13 +1480,13 @@ Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<R
 }
 
 void
-Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
 {
        core_splice (at, distance, exclude);
 }
 
 void
-Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
 {
        _splicing = true;
 
@@ -1233,11 +1497,11 @@ Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Reg
                }
 
                if ((*i)->position() >= at) {
-                       nframes64_t new_pos = (*i)->position() + distance;
+                       framepos_t new_pos = (*i)->position() + distance;
                        if (new_pos < 0) {
                                new_pos = 0;
-                       } else if (new_pos >= max_frames - (*i)->length()) {
-                               new_pos = max_frames - (*i)->length();
+                       } else if (new_pos >= max_framepos - (*i)->length()) {
+                               new_pos = max_framepos - (*i)->length();
                        }
 
                        (*i)->set_position (new_pos, this);
@@ -1250,13 +1514,13 @@ Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Reg
 }
 
 void
-Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
+Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
 {
        if (in_set_state || _splicing || _nudging || _shuffling) {
                return;
        }
 
-       if (what_changed & ARDOUR::PositionChanged) {
+       if (what_changed.contains (Properties::position)) {
 
                /* remove it from the list then add it back in
                   the right place again.
@@ -1267,9 +1531,10 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region>
                RegionList::iterator i = find (regions.begin(), regions.end(), region);
 
                if (i == regions.end()) {
-                       warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
-                                           _name, region->name())
-                               << endmsg;
+                        /* the region bounds are being modified but its not currently
+                           in the region list. we will use its bounds correctly when/if
+                           it is added
+                        */
                        return;
                }
 
@@ -1277,16 +1542,16 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region>
                regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
        }
 
-       if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
+       if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
 
-               nframes64_t delta = 0;
+               frameoffset_t delta = 0;
 
-               if (what_changed & ARDOUR::PositionChanged) {
-                       delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
+               if (what_changed.contains (Properties::position)) {
+                       delta = region->position() - region->last_position();
                }
 
-               if (what_changed & ARDOUR::LengthChanged) {
-                       delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
+               if (what_changed.contains (Properties::length)) {
+                       delta += region->length() - region->last_length();
                }
 
                if (delta) {
@@ -1309,7 +1574,7 @@ Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region>
 }
 
 void
-Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
+Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
 {
        boost::shared_ptr<Region> region (weak_region.lock());
 
@@ -1317,37 +1582,53 @@ Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> wea
                return;
        }
 
-
        /* this makes a virtual call to the right kind of playlist ... */
 
        region_changed (what_changed, region);
 }
 
 bool
-Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
 {
-       Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
+       PropertyChange our_interests;
+       PropertyChange bounds;
+       PropertyChange pos_and_length;
        bool save = false;
 
        if (in_set_state || in_flush) {
                return false;
        }
 
-       if (what_changed & BoundsChanged) {
+       our_interests.add (Properties::muted);
+       our_interests.add (Properties::layer);
+       our_interests.add (Properties::opaque);
+
+       bounds.add (Properties::start);
+       bounds.add (Properties::position);
+       bounds.add (Properties::length);
+
+       pos_and_length.add (Properties::position);
+       pos_and_length.add (Properties::length);
+
+       if (what_changed.contains (bounds)) {
                region_bounds_changed (what_changed, region);
                save = !(_splicing || _nudging);
        }
 
-       if ((what_changed & our_interests) &&
-           !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
+       if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
                check_dependents (region, false);
        }
 
-       if (what_changed & Change (ARDOUR::PositionChanged)) {
+       if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
                notify_region_moved (region);
        }
 
-       if (what_changed & our_interests) {
+
+       /* don't notify about layer changes, since we are the only object that can initiate
+          them, and we notify in ::relayer()
+       */
+
+       if (what_changed.contains (our_interests)) {
                save = true;
        }
 
@@ -1368,26 +1649,30 @@ Playlist::clear (bool with_signals)
        {
                RegionLock rl (this);
 
-               for (
-                       std::list<sigc::connection>::iterator i = region_state_changed_connections.begin ();
-                       i != region_state_changed_connections.end ();
-                       ++i
-               ) {
-                       i->disconnect ();
-               }
+               region_state_changed_connections.drop_connections ();
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                        pending_removes.insert (*i);
                }
 
                regions.clear ();
+
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        remove_dependents (*s);
+                }
        }
 
        if (with_signals) {
+
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+                }
+
+                pending_removes.clear ();
                pending_length = false;
                LengthChanged ();
-               pending_modified = false;
-               Modified ();
+               pending_contents_change = false;
+               ContentsChanged ();
        }
 
 }
@@ -1397,15 +1682,30 @@ Playlist::clear (bool with_signals)
  **********************************************************************/
 
 Playlist::RegionList *
-Playlist::regions_at (nframes_t frame)
+Playlist::regions_at (framepos_t frame)
 
 {
        RegionLock rlock (this);
        return find_regions_at (frame);
 }
 
+uint32_t
+Playlist::count_regions_at (framepos_t frame)
+{
+       RegionLock rlock (this);
+       uint32_t cnt = 0;
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->covers (frame)) {
+                       cnt++;
+               }
+       }
+
+       return cnt;
+}
+
 boost::shared_ptr<Region>
-Playlist::top_region_at (nframes_t frame)
+Playlist::top_region_at (framepos_t frame)
 
 {
        RegionLock rlock (this);
@@ -1423,7 +1723,7 @@ Playlist::top_region_at (nframes_t frame)
 }
 
 boost::shared_ptr<Region>
-Playlist::top_unmuted_region_at (nframes_t frame)
+Playlist::top_unmuted_region_at (framepos_t frame)
 
 {
        RegionLock rlock (this);
@@ -1454,18 +1754,19 @@ Playlist::top_unmuted_region_at (nframes_t frame)
 }
 
 Playlist::RegionList*
-Playlist::regions_to_read (nframes_t start, nframes_t end)
+Playlist::regions_to_read (framepos_t start, framepos_t end)
 {
        /* Caller must hold lock */
 
        RegionList covering;
-       set<nframes_t> to_check;
+       set<framepos_t> to_check;
        set<boost::shared_ptr<Region> > unique;
-       RegionList here;
 
        to_check.insert (start);
        to_check.insert (end);
 
+        DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
+
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
                /* find all/any regions that span start+end */
@@ -1476,22 +1777,38 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
 
                case OverlapInternal:
                        covering.push_back (*i);
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
                        break;
 
                case OverlapStart:
                        to_check.insert ((*i)->position());
+                        if ((*i)->position() != 0) {
+                                to_check.insert ((*i)->position()-1);
+                        }
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
                        covering.push_back (*i);
                        break;
 
                case OverlapEnd:
                        to_check.insert ((*i)->last_frame());
+                       to_check.insert ((*i)->last_frame()+1);
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OEnd)\n", (*i)->name()));
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
                        covering.push_back (*i);
                        break;
 
                case OverlapExternal:
                        covering.push_back (*i);
                        to_check.insert ((*i)->position());
+                        if ((*i)->position() != 0) {
+                                to_check.insert ((*i)->position()-1);
+                        }
                        to_check.insert ((*i)->last_frame());
+                       to_check.insert ((*i)->last_frame()+1);
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OExt)\n", (*i)->name()));
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->position(), (*i)->name()));
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\ttoread: will check %1 for %2\n", (*i)->last_frame(), (*i)->name()));
                        break;
                }
 
@@ -1509,18 +1826,30 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
        if (covering.size() == 1) {
 
                rlist->push_back (covering.front());
+                DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
 
        } else {
 
-               for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
+               RegionList here;
+               for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
 
                        here.clear ();
 
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
+
                        for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
 
                                if ((*x)->covers (*t)) {
                                        here.push_back (*x);
-                               }
+                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 covers %2\n",
+                                                                                           (*x)->name(),
+                                                                                           (*t)));
+                               } else {
+                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("region %1 does NOT covers %2\n",
+                                                                                           (*x)->name(),
+                                                                                           (*t)));
+                                }
+                                        
                        }
 
                        RegionSortByLayer cmp;
@@ -1535,7 +1864,8 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
                                if ((*c)->opaque()) {
 
                                        /* the other regions at this position are hidden by this one */
-
+                                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 is opaque, ignore all others\n",
+                                                                                           (*c)->name()));
                                        break;
                                }
                        }
@@ -1553,11 +1883,13 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
                }
        }
 
+        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+
        return rlist;
 }
 
 Playlist::RegionList *
-Playlist::find_regions_at (nframes_t frame)
+Playlist::find_regions_at (framepos_t frame)
 {
        /* Caller must hold lock */
 
@@ -1573,7 +1905,7 @@ Playlist::find_regions_at (nframes_t frame)
 }
 
 Playlist::RegionList *
-Playlist::regions_touched (nframes_t start, nframes_t end)
+Playlist::regions_touched (framepos_t start, framepos_t end)
 {
        RegionLock rlock (this);
        RegionList *rlist = new RegionList;
@@ -1587,8 +1919,8 @@ Playlist::regions_touched (nframes_t start, nframes_t end)
        return rlist;
 }
 
-nframes64_t
-Playlist::find_next_transient (nframes64_t from, int dir)
+framepos_t
+Playlist::find_next_transient (framepos_t from, int dir)
 {
        RegionLock rlock (this);
        AnalysisFeatureList points;
@@ -1648,11 +1980,11 @@ Playlist::find_next_transient (nframes64_t from, int dir)
 }
 
 boost::shared_ptr<Region>
-Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
+Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
 {
        RegionLock rlock (this);
        boost::shared_ptr<Region> ret;
-       nframes_t closest = max_frames;
+       framepos_t closest = max_framepos;
 
        bool end_iter = false;
 
@@ -1660,9 +1992,9 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
 
                if(end_iter) break;
 
-               nframes_t distance;
+               frameoffset_t distance;
                boost::shared_ptr<Region> r = (*i);
-               nframes_t pos = 0;
+               framepos_t pos = 0;
 
                switch (point) {
                case Start:
@@ -1673,7 +2005,6 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
                        break;
                case SyncPoint:
                        pos = r->sync_position ();
-                       // r->adjust_to_sync (r->first_frame());
                        break;
                }
 
@@ -1709,20 +2040,20 @@ Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
        return ret;
 }
 
-nframes64_t
-Playlist::find_next_region_boundary (nframes64_t frame, int dir)
+framepos_t
+Playlist::find_next_region_boundary (framepos_t frame, int dir)
 {
        RegionLock rlock (this);
 
-       nframes64_t closest = max_frames;
-       nframes64_t ret = -1;
+       framepos_t closest = max_framepos;
+       framepos_t ret = -1;
 
        if (dir > 0) {
 
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
                        boost::shared_ptr<Region> r = (*i);
-                       nframes64_t distance;
+                       frameoffset_t distance;
 
                        if (r->first_frame() > frame) {
 
@@ -1750,7 +2081,7 @@ Playlist::find_next_region_boundary (nframes64_t frame, int dir)
                for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
 
                        boost::shared_ptr<Region> r = (*i);
-                       nframes64_t distance;
+                       frameoffset_t distance;
 
                        if (r->last_frame() < frame) {
 
@@ -1777,6 +2108,7 @@ Playlist::find_next_region_boundary (nframes64_t frame, int dir)
        return ret;
 }
 
+
 /***********************************************************************/
 
 
@@ -1790,6 +2122,39 @@ Playlist::mark_session_dirty ()
        }
 }
 
+void
+Playlist::rdiff (vector<Command*>& cmds) const
+{
+       RegionLock rlock (const_cast<Playlist *> (this));
+       Stateful::rdiff (cmds);
+}
+
+void
+Playlist::clear_owned_changes ()
+{
+       RegionLock rlock (this);
+       Stateful::clear_owned_changes ();
+}
+
+void
+Playlist::update (const RegionListProperty::ChangeRecord& change)
+{
+        DEBUG_TRACE (DEBUG::Properties, string_compose ("Playlist %1 updates from a change record with %2 adds %3 removes\n", 
+                                                        name(), change.added.size(), change.removed.size()));
+        
+        freeze ();
+        /* add the added regions */
+        for (RegionListProperty::ChangeContainer::iterator i = change.added.begin(); i != change.added.end(); ++i) {
+                add_region ((*i), (*i)->position());
+        }
+        /* remove the removed regions */
+        for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
+                remove_region (*i);
+        }
+
+        thaw ();
+}
+
 int
 Playlist::set_state (const XMLNode& node, int version)
 {
@@ -1819,6 +2184,9 @@ Playlist::set_state (const XMLNode& node, int version)
 
                if (prop->name() == X_("name")) {
                        _name = prop->value();
+                        _set_sort_id ();
+               } else if (prop->name() == X_("id")) {
+                        _id = prop->value();
                } else if (prop->name() == X_("orig_diskstream_id")) {
                        _orig_diskstream_id = prop->value ();
                } else if (prop->name() == X_("frozen")) {
@@ -1826,7 +2194,7 @@ Playlist::set_state (const XMLNode& node, int version)
                }
        }
 
-       clear (false);
+       clear (true);
 
        nlist = node.children();
 
@@ -1840,35 +2208,34 @@ Playlist::set_state (const XMLNode& node, int version)
                                error << _("region state node has no ID, ignored") << endmsg;
                                continue;
                        }
-
+                       
                        ID id = prop->value ();
 
                        if ((region = region_by_id (id))) {
 
-                               Change what_changed = Change (0);
+                               region->suspend_property_changes ();
 
-                               if (region->set_live_state (*child, version, what_changed, true)) {
-                                       error << _("Playlist: cannot reset region state from XML") << endmsg;
+                               if (region->set_state (*child, version)) {
+                                       region->resume_property_changes ();
                                        continue;
                                }
-
-                       } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
+                               
+                       } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
+                               region->suspend_property_changes ();
+                       } else {
                                error << _("Playlist: cannot create region from XML") << endmsg;
                                continue;
                        }
 
+
                        add_region (region, region->position(), 1.0);
 
                        // So that layer_op ordering doesn't get screwed up
                        region->set_last_layer_op( region->layer());
-
+                       region->resume_property_changes ();
                }
        }
 
-       notify_modified ();
-
-       thaw ();
-
        /* update dependents, which was not done during add_region_internal
           due to in_set_state being true
        */
@@ -1877,6 +2244,9 @@ Playlist::set_state (const XMLNode& node, int version)
                check_dependents (*r, false);
        }
 
+       thaw ();
+       notify_contents_changed ();
+
        in_set_state--;
        first_set_state = false;
        return 0;
@@ -1900,8 +2270,9 @@ XMLNode&
 Playlist::state (bool full_state)
 {
        XMLNode *node = new XMLNode (X_("Playlist"));
-       char buf[64];
+       char buf[64] = "";
 
+       node->add_property (X_("id"), id().to_s());
        node->add_property (X_("name"), _name);
        node->add_property (X_("type"), _type.to_string());
 
@@ -1911,6 +2282,7 @@ Playlist::state (bool full_state)
 
        if (full_state) {
                RegionLock rlock (this, false);
+
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
                        node->add_child_nocopy ((*i)->get_state());
                }
@@ -1937,27 +2309,29 @@ Playlist::n_regions() const
        return regions.size();
 }
 
-nframes_t
-Playlist::get_maximum_extent () const
+pair<framepos_t, framepos_t>
+Playlist::get_extent () const
 {
        RegionLock rlock (const_cast<Playlist *>(this), false);
-       return _get_maximum_extent ();
+       return _get_extent ();
 }
 
-ARDOUR::nframes_t
-Playlist::_get_maximum_extent () const
+pair<framepos_t, framepos_t>
+Playlist::_get_extent () const
 {
-       RegionList::const_iterator i;
-       nframes_t max_extent = 0;
-       nframes_t end = 0;
+       pair<framepos_t, framepos_t> ext (max_framepos, 0);
 
-       for (i = regions.begin(); i != regions.end(); ++i) {
-               if ((end = (*i)->position() + (*i)->length()) > max_extent) {
-                       max_extent = end;
+       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               pair<framepos_t, framepos_t> const e ((*i)->position(), (*i)->position() + (*i)->length());
+               if (e.first < ext.first) {
+                       ext.first = e.first;
+               }
+               if (e.second > ext.second) {
+                       ext.second = e.second;
                }
        }
 
-       return max_extent;
+       return ext;
 }
 
 string
@@ -1966,8 +2340,8 @@ Playlist::bump_name (string name, Session &session)
        string newname = name;
 
        do {
-               newname = bump_name_once (newname);
-       } while (session.playlists.by_name (newname)!=NULL);
+               newname = bump_name_once (newname, '.');
+       } while (session.playlists->by_name (newname)!=NULL);
 
        return newname;
 }
@@ -1998,11 +2372,13 @@ Playlist::set_edit_mode (EditMode mode)
 void
 Playlist::relayer ()
 {
-       /* don't send multiple Modified notifications
-          when multiple regions are relayered.
-       */
+        /* never compute layers when changing state for undo/redo or setting from XML */
 
-       freeze ();
+        if (in_update || in_set_state) {
+                return;
+        }
+
+       bool changed = false;
 
        /* Build up a new list of regions on each layer, stored in a set of lists
           each of which represent some period of time on some layer.  The idea
@@ -2013,8 +2389,8 @@ Playlist::relayer ()
        int const divisions = 512;
 
        /* find the start and end positions of the regions on this playlist */
-       nframes_t start = UINT_MAX;
-       nframes_t end = 0;
+       framepos_t start = INT64_MAX;
+       framepos_t end = 0;
        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
                start = min (start, (*i)->position());
                end = max (end, (*i)->position() + (*i)->length());
@@ -2030,7 +2406,7 @@ Playlist::relayer ()
           which depends on the layer model
        */
 
-       RegionList copy = regions;
+       RegionList copy = regions.rlist();
 
        /* sort according to the model and the layering mode that we're in */
 
@@ -2044,19 +2420,28 @@ Playlist::relayer ()
 
        }
 
+
        for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
 
                /* reset the pending explicit relayer flag for every region, now that we're relayering */
                (*i)->set_pending_explicit_relayer (false);
 
-               /* find the time divisions that this region covers */
-               int const start_division = floor ( ((*i)->position() - start) / division_size);
-               int end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
-               if (end_division == divisions) {
-                       end_division--;
+               /* find the time divisions that this region covers; if there are no regions on the list,
+                  division_size will equal 0 and in this case we'll just say that
+                  start_division = end_division = 0.
+               */
+               int start_division = 0;
+               int end_division = 0;
+
+               if (division_size > 0) {
+                       start_division = floor ( ((*i)->position() - start) / division_size);
+                       end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
+                       if (end_division == divisions) {
+                               end_division--;
+                       }
                }
 
-               assert (end_division < divisions);
+               assert (divisions == 0 || end_division < divisions);
 
                /* find the lowest layer that this region can go on */
                size_t j = layers.size();
@@ -2098,21 +2483,17 @@ Playlist::relayer ()
                for (int k = start_division; k <= end_division; ++k) {
                        layers[j][k].push_back (*i);
                }
+               
+               if ((*i)->layer() != j) {
+                       changed = true;
+               }
 
                (*i)->set_layer (j);
        }
 
-       /* sending Modified means that various kinds of layering
-          models operate correctly at the GUI
-          level. slightly inefficient, but only slightly.
-
-          We force a Modified signal here in case no layers actually
-          changed.
-       */
-
-       notify_modified ();
-
-       thaw ();
+       if (changed) {
+               notify_layering_changed ();
+       }
 }
 
 /* XXX these layer functions are all deprecated */
@@ -2120,10 +2501,10 @@ Playlist::relayer ()
 void
 Playlist::raise_region (boost::shared_ptr<Region> region)
 {
-       uint32_t rsz = regions.size();
+       uint32_t top = regions.size() - 1;
        layer_t target = region->layer() + 1U;
 
-       if (target >= rsz) {
+       if (target >= top) {
                /* its already at the effective top */
                return;
        }
@@ -2148,22 +2529,48 @@ void
 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
 {
        /* does nothing useful if layering mode is later=higher */
-       if ((_session.config.get_layer_model() == MoveAddHigher) ||
-           (_session.config.get_layer_model() == AddHigher)) {
-               timestamp_layer_op (region);
-               relayer ();
+       switch (_session.config.get_layer_model()) {
+       case LaterHigher:
+               return;
+       default:
+               break;
        }
+
+       layer_t top = regions.size() - 1;
+
+       if (region->layer() >= top) {
+               /* already on the top */
+               return;
+       }
+
+       move_region_to_layer (top, region, 1);
+       /* mark the region's last_layer_op as now, so that it remains on top when
+          doing future relayers (until something else takes over)
+        */
+       timestamp_layer_op (region);
 }
 
 void
 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
 {
        /* does nothing useful if layering mode is later=higher */
-       if ((_session.config.get_layer_model() == MoveAddHigher) ||
-           (_session.config.get_layer_model() == AddHigher)) {
-               region->set_last_layer_op (0);
-               relayer ();
+       switch (_session.config.get_layer_model()) {
+       case LaterHigher:
+               return;
+       default:
+               break;
        }
+
+       if (region->layer() == 0) {
+               /* already on the bottom */
+               return;
+       }
+
+       move_region_to_layer (0, region, -1);
+       /* force region's last layer op to zero so that it stays at the bottom
+          when doing future relayers
+       */
+       region->set_last_layer_op (0);
 }
 
 int
@@ -2172,7 +2579,6 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
        RegionList::iterator i;
        typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
        list<LayerInfo> layerinfo;
-       layer_t dest;
 
        {
                RegionLock rlock (const_cast<Playlist *> (this));
@@ -2183,6 +2589,8 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
                                continue;
                        }
 
+                       layer_t dest;
+
                        if (dir > 0) {
 
                                /* region is moving up, move all regions on intermediate layers
@@ -2218,6 +2626,8 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
                }
        }
 
+        freeze ();
+
        /* now reset the layers without holding the region lock */
 
        for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
@@ -2226,24 +2636,24 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
 
        region->set_layer (target_layer);
 
-#if 0
-       /* now check all dependents */
+        /* now check all dependents, since we changed the layering */
 
        for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
                check_dependents (x->first, false);
        }
 
        check_dependents (region, false);
-#endif
+        notify_layering_changed ();
+
+        thaw ();
 
        return 0;
 }
 
 void
-Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
+Playlist::nudge_after (framepos_t start, framecnt_t distance, bool forwards)
 {
        RegionList::iterator i;
-       nframes_t new_pos;
        bool moved = false;
 
        _nudging = true;
@@ -2255,10 +2665,12 @@ Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
 
                        if ((*i)->position() >= start) {
 
+                               framepos_t new_pos;
+
                                if (forwards) {
 
-                                       if ((*i)->last_frame() > max_frames - distance) {
-                                               new_pos = max_frames - (*i)->length();
+                                       if ((*i)->last_frame() > max_framepos - distance) {
+                                               new_pos = max_framepos - (*i)->length();
                                        } else {
                                                new_pos = (*i)->position() + distance;
                                        }
@@ -2301,8 +2713,23 @@ Playlist::find_region (const ID& id) const
        return boost::shared_ptr<Region> ();
 }
 
+uint32_t
+Playlist::region_use_count (boost::shared_ptr<Region> r) const
+{
+       RegionLock rlock (const_cast<Playlist*> (this));
+        uint32_t cnt = 0;
+
+       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i) == r) {
+                        cnt++;
+               }
+       }
+
+       return cnt;
+}
+
 boost::shared_ptr<Region>
-Playlist::region_by_id (ID id)
+Playlist::region_by_id (const ID& id) const
 {
        /* searches all regions ever added to this playlist */
 
@@ -2344,8 +2771,6 @@ Playlist::set_frozen (bool yn)
 void
 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
 {
-//     struct timeval tv;
-//     gettimeofday (&tv, 0);
        region->set_last_layer_op (++layer_op_counter);
 }
 
@@ -2354,7 +2779,6 @@ void
 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
 {
        bool moved = false;
-       nframes_t new_pos;
 
        if (region->locked()) {
                return;
@@ -2381,6 +2805,8 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
                                                        break;
                                                }
 
+                                               framepos_t new_pos;
+
                                                if ((*next)->position() != region->last_frame() + 1) {
                                                        /* they didn't used to touch, so after shuffle,
                                                           just have them swap positions.
@@ -2422,6 +2848,7 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
                                                        break;
                                                }
 
+                                               framepos_t new_pos;
                                                if (region->position() != (*prev)->last_frame() + 1) {
                                                        /* they didn't used to touch, so after shuffle,
                                                           just have them swap positions.
@@ -2459,7 +2886,7 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
                relayer ();
                check_dependents (region, false);
 
-               notify_modified();
+               notify_contents_changed();
        }
 
 }
@@ -2480,7 +2907,7 @@ void
 Playlist::update_after_tempo_map_change ()
 {
        RegionLock rlock (const_cast<Playlist*> (this));
-       RegionList copy (regions);
+       RegionList copy (regions.rlist());
 
        freeze ();
 
@@ -2492,7 +2919,7 @@ Playlist::update_after_tempo_map_change ()
 }
 
 void
-Playlist::foreach_region (sigc::slot<void, boost::shared_ptr<Region> > s)
+Playlist::foreach_region (boost::function<void(boost::shared_ptr<Region>)> s)
 {
        RegionLock rl (this, false);
        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
@@ -2524,7 +2951,7 @@ Playlist::set_explicit_relayering (bool e)
 
 
 bool
-Playlist::has_region_at (nframes64_t const p) const
+Playlist::has_region_at (framepos_t const p) const
 {
        RegionLock (const_cast<Playlist *> (this));
        
@@ -2535,3 +2962,46 @@ Playlist::has_region_at (nframes64_t const p) const
 
        return (i != regions.end());
 }
+
+/** Remove any region that uses a given source */
+void
+Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
+{
+       RegionLock rl (this);
+       
+       RegionList::iterator i = regions.begin();
+       while (i != regions.end()) {
+               RegionList::iterator j = i;
+               ++j;
+               
+               if ((*i)->uses_source (s)) {
+                       remove_region_internal (*i);
+               }
+
+               i = j;
+       }
+}
+
+/** Look from a session frame time and find the start time of the next region
+ *  which is on the top layer of this playlist.
+ *  @param t Time to look from.
+ *  @return Position of next top-layered region, or max_framepos if there isn't one.
+ */
+framepos_t
+Playlist::find_next_top_layer_position (framepos_t t) const
+{
+       RegionLock rlock (const_cast<Playlist *> (this));
+       
+       layer_t const top = top_layer ();
+
+       RegionList copy = regions.rlist ();
+       copy.sort (RegionSortByPosition ());
+
+       for (RegionList::const_iterator i = copy.begin(); i != copy.end(); ++i) {
+               if ((*i)->position() >= t && (*i)->layer() == top) {
+                       return (*i)->position();
+               }
+       }
+
+       return max_framepos;
+}