No-op: rename a few variables and add/fix some comments.
[ardour.git] / libs / ardour / playlist.cc
index e3d1831470764985e1f7b7c9d86a32553d985e21..c71d53deca6dc009d6ac769b66be5ed695847b64 100644 (file)
 #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"
 
 #include "ardour/debug.h"
 #include "ardour/playlist.h"
@@ -39,6 +38,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 +46,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 +96,89 @@ 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)
+{
+}
+
+boost::shared_ptr<Region>
+RegionListProperty::lookup_id (const ID& id)
+{
+        boost::shared_ptr<Region> ret =  _playlist.region_by_id (id);
+        
+        if (!ret) {
+                ret = RegionFactory::region_by_id (id);
+        }
+
+        return ret;
+}
+
+RegionListProperty*
+RegionListProperty::copy_for_history () const
+{
+        RegionListProperty* copy = new RegionListProperty (_playlist);
+        /* this is all we need */
+        copy->_change = _change;
+        return copy;
+}
+
+void 
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
+{
+        if (changed()) {
+               /* list of the removed/added regions since clear_history() was last called */
+                RegionListProperty* a = copy_for_history ();
+
+               /* the same list, but with removed/added lists swapped (for undo purposes) */
+                RegionListProperty* b = copy_for_history ();
+                b->invert_changes ();
+
+                undo.add (b);
+                redo.add (a);
+        }
+}
 
 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)
+
 {
        const XMLProperty* prop = node.property("type");
        assert(!prop || DataType(prop->value()) == _type);
 
        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 +209,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 +266,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);
 
-               new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
+               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, plist);
 
                add_region_internal (new_region, position);
        }
@@ -245,10 +318,14 @@ Playlist::copy_regions (RegionList& newlist) const
 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 +333,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 +344,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 +365,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 +402,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 +419,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 ();
+        in_update = false;
+}
+
 void
 Playlist::freeze ()
 {
@@ -334,17 +460,29 @@ Playlist::release_notifications ()
 {
        if (g_atomic_int_dec_and_test (&block_notifications)) {
                flush_notifications ();
+        }
+
+}
+
+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 +491,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 +499,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,7 +516,7 @@ 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);
        }
@@ -391,17 +529,17 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
        /* the length change might not be true, but we have to act
           as though it could be.
        */
-
+        
        if (holding_state()) {
                pending_adds.insert (r);
-               pending_modified = true;
+               pending_contents_change = true;
                pending_length = true;
        } else {
                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,8 +551,8 @@ Playlist::notify_length_changed ()
        } else {
                pending_length = false;
                LengthChanged(); /* EMIT SIGNAL */
-               pending_modified = false;
-               Modified (); /* EMIT SIGNAL */
+               pending_contents_change = false;
+               ContentsChanged (); /* EMIT SIGNAL */
        }
 }
 
@@ -423,7 +561,9 @@ Playlist::flush_notifications ()
 {
        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 +571,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_maximum_extent ();
+                       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,37 +592,42 @@ 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) {
+               // cerr << _name << " sends RegionAdded\n";
                RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
                dependent_checks_needed.insert (*s);
-               n++;
        }
 
-       if ((freeze_length != _get_maximum_extent()) || pending_length) {
-               pending_length = 0;
+       if (check_length) {
+               if (old_length != _get_maximum_extent()) {
+                       pending_length = true;
+                       // cerr << _name << " length has changed\n";
+               }
+       }
+
+       if (pending_length || (freeze_length != _get_maximum_extent())) {
+               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 = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
@@ -482,15 +635,24 @@ Playlist::flush_notifications ()
        }
 
        if (!pending_range_moves.empty ()) {
+               // cerr << _name << " sends RangesMoved\n";
                RangesMoved (pending_range_moves);
        }
+       
+       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 +660,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, (pos + region->length()), true);
        }
 
        if (itimes >= 1) {
@@ -528,14 +690,24 @@ Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, floa
                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 +726,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,7 +734,7 @@ 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();
@@ -582,8 +754,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 ();
        }
 
@@ -600,16 +772,13 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
                }
        }
 
-       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 +790,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,7 +804,8 @@ 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();
@@ -646,18 +816,20 @@ 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()) {
@@ -666,13 +838,14 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
                        }
 
                        notify_region_removed (region);
-                       return 0;
+                       ret = 0;
+                       break;
                }
        }
 
+       /* XXX and thaw ... */
 
-
-       return -1;
+       return ret;
 }
 
 void
@@ -705,19 +878,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 +902,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 +910,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,25 +965,44 @@ 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);
 
@@ -830,9 +1022,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,7 +1041,7 @@ 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);
 
@@ -864,9 +1065,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 +1084,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 +1122,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 +1157,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 +1187,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];
 
@@ -998,20 +1208,19 @@ Playlist::copy (nframes_t start, nframes_t cnt, bool 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 old_length = _get_maximum_extent();
 
                int itimes = (int) floor (times);
-               nframes_t pos = position;
-               nframes_t shift = other->_get_maximum_extent();
+               framepos_t pos = position;
+               framecnt_t shift = other->_get_maximum_extent();
                layer_t top_layer = regions.size();
 
                while (itimes--) {
@@ -1043,13 +1252,13 @@ 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;
 
        while (itimes--) {
                boost::shared_ptr<Region> copy = RegionFactory::create (region);
@@ -1058,19 +1267,28 @@ Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float
        }
 
        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) {
@@ -1105,10 +1323,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 +1337,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 +1357,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 +1370,31 @@ 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::start, region->start());
+               plist.add (Properties::length, before);
+               plist.add (Properties::name, before_name);
+               plist.add (Properties::left_of_split, true);
+               
+               left = RegionFactory::create (region, plist);
+       }
+
+       RegionFactory::region_name (after_name, region->name(), false);
+
+       {
+               PropertyList plist;
+               
+               plist.add (Properties::start, region->start() + before);
+               plist.add (Properties::length, after);
+               plist.add (Properties::name, after_name);
+               plist.add (Properties::right_of_split, true);
+
+               right = RegionFactory::create (region, plist);
+       }
 
        add_region_internal (left, region->position());
        add_region_internal (right, region->position() + before);
@@ -1181,7 +1419,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 +1432,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 +1445,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 +1454,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,7 +1471,7 @@ 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()) {
@@ -1250,13 +1488,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 +1505,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 +1516,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 +1548,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 +1556,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)) {
                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 +1623,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,7 +1656,7 @@ Playlist::clear (bool with_signals)
  **********************************************************************/
 
 Playlist::RegionList *
-Playlist::regions_at (nframes_t frame)
+Playlist::regions_at (framepos_t frame)
 
 {
        RegionLock rlock (this);
@@ -1405,7 +1664,7 @@ Playlist::regions_at (nframes_t frame)
 }
 
 boost::shared_ptr<Region>
-Playlist::top_region_at (nframes_t frame)
+Playlist::top_region_at (framepos_t frame)
 
 {
        RegionLock rlock (this);
@@ -1423,7 +1682,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,14 +1713,13 @@ 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);
@@ -1512,7 +1770,8 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
 
        } 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 ();
 
@@ -1557,7 +1816,7 @@ Playlist::regions_to_read (nframes_t start, nframes_t end)
 }
 
 Playlist::RegionList *
-Playlist::find_regions_at (nframes_t frame)
+Playlist::find_regions_at (framepos_t frame)
 {
        /* Caller must hold lock */
 
@@ -1573,7 +1832,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 +1846,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 +1907,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_frames;
 
        bool end_iter = false;
 
@@ -1660,9 +1919,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:
@@ -1709,20 +1968,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_frames;
+       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 +2009,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) {
 
@@ -1790,6 +2049,85 @@ Playlist::mark_session_dirty ()
        }
 }
 
+bool
+Playlist::set_property (const PropertyBase& prop)
+{
+        if (prop == Properties::regions.property_id) {
+                const RegionListProperty::ChangeRecord& change (dynamic_cast<const RegionListProperty*>(&prop)->change());
+                regions.update (change);
+                return (!change.added.empty() && !change.removed.empty());
+        }
+        return false;
+}
+
+void
+Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
+{
+       RegionLock rlock (const_cast<Playlist *> (this));
+
+       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->changed ()) {
+                        StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
+                        cmds.push_back (sdc);
+                }
+       }
+}
+
+void
+Playlist::clear_owned_history ()
+{
+       RegionLock rlock (this);
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                (*i)->clear_history ();
+        }
+}
+
+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 ();
+}
+
+PropertyList*
+Playlist::property_factory (const XMLNode& history_node) const
+{
+        const XMLNodeList& children (history_node.children());
+        PropertyList* prop_list = 0;
+
+        for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+
+                if ((*i)->name() == capitalize (regions.property_name())) {
+                        
+                        RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
+
+                        if (rlp->load_history_state (**i)) {
+                                if (!prop_list) {
+                                        prop_list = new PropertyList();
+                                }
+                                prop_list->add (rlp);
+                        } else {
+                                delete rlp;
+                        }
+                }
+        }
+
+        return prop_list;
+}
+
 int
 Playlist::set_state (const XMLNode& node, int version)
 {
@@ -1819,6 +2157,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 +2167,7 @@ Playlist::set_state (const XMLNode& node, int version)
                }
        }
 
-       clear (false);
+       clear (true);
 
        nlist = node.children();
 
@@ -1840,35 +2181,33 @@ 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 +2216,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;
@@ -1902,6 +2244,7 @@ Playlist::state (bool full_state)
        XMLNode *node = new XMLNode (X_("Playlist"));
        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());
 
@@ -1937,19 +2280,19 @@ Playlist::n_regions() const
        return regions.size();
 }
 
-nframes_t
+framecnt_t
 Playlist::get_maximum_extent () const
 {
        RegionLock rlock (const_cast<Playlist *>(this), false);
        return _get_maximum_extent ();
 }
 
-ARDOUR::nframes_t
+framecnt_t
 Playlist::_get_maximum_extent () const
 {
        RegionList::const_iterator i;
-       nframes_t max_extent = 0;
-       nframes_t end = 0;
+       framecnt_t max_extent = 0;
+       framepos_t end = 0;
 
        for (i = regions.begin(); i != regions.end(); ++i) {
                if ((end = (*i)->position() + (*i)->length()) > max_extent) {
@@ -1967,7 +2310,7 @@ Playlist::bump_name (string name, Session &session)
 
        do {
                newname = bump_name_once (newname);
-       } while (session.playlists.by_name (newname)!=NULL);
+       } while (session.playlists->by_name (newname)!=NULL);
 
        return newname;
 }
@@ -1998,11 +2341,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 +2358,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 = UINT_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 +2375,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,6 +2389,7 @@ 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 */
@@ -2098,21 +2444,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 */
@@ -2172,7 +2514,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 +2524,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
@@ -2240,10 +2583,9 @@ Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region>
 }
 
 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,6 +2597,8 @@ 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) {
@@ -2302,7 +2646,7 @@ Playlist::find_region (const ID& id) const
 }
 
 boost::shared_ptr<Region>
-Playlist::region_by_id (ID id)
+Playlist::region_by_id (const ID& id)
 {
        /* searches all regions ever added to this playlist */
 
@@ -2354,7 +2698,6 @@ void
 Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
 {
        bool moved = false;
-       nframes_t new_pos;
 
        if (region->locked()) {
                return;
@@ -2381,6 +2724,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 +2767,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 +2805,7 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
                relayer ();
                check_dependents (region, false);
 
-               notify_modified();
+               notify_contents_changed();
        }
 
 }
@@ -2480,7 +2826,7 @@ void
 Playlist::update_after_tempo_map_change ()
 {
        RegionLock rlock (const_cast<Playlist*> (this));
-       RegionList copy (regions);
+       RegionList copy (regions.rlist());
 
        freeze ();
 
@@ -2492,7 +2838,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 +2870,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));