Sort session routes before trying to do the group tabs
[ardour.git] / libs / ardour / playlist.cc
index 6dd230c1ab9b8bed77f116ca520d492ab8914e07..3bc5f57577d7349d0647880245adf7d05dfd67d9 100644 (file)
@@ -80,7 +80,7 @@ RegionListProperty::RegionListProperty (Playlist& pl)
        : SequenceProperty<std::list<boost::shared_ptr<Region> > > (Properties::regions.property_id, boost::bind (&Playlist::update, &pl, _1))
        , _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)
 }
 
 RegionListProperty::RegionListProperty (RegionListProperty const & p)
@@ -108,7 +108,7 @@ RegionListProperty::get_content_as_xml (boost::shared_ptr<Region> region, XMLNod
        /* All regions (even those which are deleted) have their state saved by other
           code, so we can just store ID here.
        */
        /* 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 ());
 }
 
        node.add_property ("id", region->id().to_s ());
 }
 
@@ -121,7 +121,7 @@ RegionListProperty::get_content_from_xml (XMLNode const & node) const
        PBD::ID id (prop->value ());
 
        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
        PBD::ID id (prop->value ());
 
        boost::shared_ptr<Region> ret = _playlist.region_by_id (id);
-        
+
        if (!ret) {
                ret = RegionFactory::region_by_id (id);
        }
        if (!ret) {
                ret = RegionFactory::region_by_id (id);
        }
@@ -142,7 +142,7 @@ Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
 
 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
        : SessionObject(sess, "unnamed playlist")
 
 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
        : SessionObject(sess, "unnamed playlist")
-       , regions (*this)       
+       , regions (*this)
        , _type(type)
 {
 #ifndef NDEBUG
        , _type(type)
 {
 #ifndef NDEBUG
@@ -185,7 +185,6 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
        in_flush = false;
        in_partition = false;
        subcnt = 0;
        in_flush = false;
        in_partition = false;
        subcnt = 0;
-       _read_data_count = 0;
        _frozen = other->_frozen;
 
        layer_op_counter = other->layer_op_counter;
        _frozen = other->_frozen;
 
        layer_op_counter = other->layer_op_counter;
@@ -251,7 +250,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
 
                RegionFactory::region_name (new_name, region->name(), false);
 
 
                RegionFactory::region_name (new_name, region->name(), false);
 
-               PropertyList plist; 
+               PropertyList plist;
 
                plist.add (Properties::start, region->start() + offset);
                plist.add (Properties::length, len);
 
                plist.add (Properties::start, region->start() + offset);
                plist.add (Properties::length, len);
@@ -319,7 +318,6 @@ Playlist::init (bool hide)
        in_flush = false;
        in_partition = false;
        subcnt = 0;
        in_flush = false;
        in_partition = false;
        subcnt = 0;
-       _read_data_count = 0;
        _frozen = false;
        layer_op_counter = 0;
        freeze_length = 0;
        _frozen = false;
        layer_op_counter = 0;
        freeze_length = 0;
@@ -328,7 +326,7 @@ Playlist::init (bool hide)
 
        _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));
 
        _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));
 }
 
        ContentsChanged.connect_same_thread (*this, boost::bind (&Playlist::mark_session_dirty, this));
 }
 
@@ -355,7 +353,7 @@ Playlist::_set_sort_id ()
          or <track name>.<edit group name>.<id> where id
          is an integer. We extract the id and sort by that.
        */
          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) {
        size_t dot_position = _name.val().find_last_of(".");
 
        if (dot_position == string::npos) {
@@ -384,7 +382,7 @@ Playlist::set_name (const string& str)
 
        if (_refcnt > 2) {
                return false;
 
        if (_refcnt > 2) {
                return false;
-       } 
+       }
 
        bool ret =  SessionObject::set_name(str);
        if (ret) {
 
        bool ret =  SessionObject::set_name(str);
        if (ret) {
@@ -515,17 +513,17 @@ Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
        }
 
        Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
        }
 
        Evoral::Range<framepos_t> const extra (r->position(), r->last_position());
-       
+
        if (holding_state ()) {
        if (holding_state ()) {
-               
+
                pending_region_extensions.push_back (extra);
                pending_region_extensions.push_back (extra);
-               
+
        } else {
        } else {
-               
+
                list<Evoral::Range<framepos_t> > r;
                r.push_back (extra);
                RegionsExtended (r);
                list<Evoral::Range<framepos_t> > r;
                r.push_back (extra);
                RegionsExtended (r);
-               
+
        }
 }
 
        }
 }
 
@@ -632,1662 +630,1668 @@ Playlist::flush_notifications (bool from_undo)
        }
 
        for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
        }
 
        for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
-               // 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);
-       }
-
-       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 */
-       }
-
-       if (regions_changed || pending_contents_change) {
-               if (!in_set_state) {
-                       relayer ();
-               }
-               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, from_undo);
-       }
-
-       if (!pending_region_extensions.empty ()) {
-               RegionsExtended (pending_region_extensions);
-       }
-       
-       clear_pending ();
-
-       in_flush = false;
-}
-
-void
-Playlist::clear_pending ()
-{
-       pending_adds.clear ();
-       pending_removes.clear ();
-       pending_bounds.clear ();
-       pending_range_moves.clear ();
-       pending_region_extensions.clear ();
-       pending_contents_change = false;
-       pending_length = false;
-}
-
-/*************************************************************
-  PLAYLIST OPERATIONS
- *************************************************************/
-
-void
-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);
-
-       framepos_t pos = position;
-
-       if (times == 1 && auto_partition){
-               partition(pos - 1, (pos + region->length()), true);
-       }
-
-       if (itimes >= 1) {
-               add_region_internal (region, pos);
-               pos += region->length();
-               --itimes;
-       }
-
-
-       /* note that itimes can be zero if we being asked to just
-          insert a single fraction of the region.
-       */
-
-       for (int i = 0; i < itimes; ++i) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
-               add_region_internal (copy, pos);
-               pos += region->length();
-       }
-
-       framecnt_t length = 0;
-
-       if (floor (times) != times) {
-               length = (framecnt_t) floor (region->length() * (times - floor (times)));
-               string name;
-               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>());
-}
-
-void
-Playlist::set_region_ownership ()
-{
-       RegionLock rl (this);
-       RegionList::iterator i;
-       boost::weak_ptr<Playlist> pl (shared_from_this());
-
-       for (i = regions.begin(); i != regions.end(); ++i) {
-               (*i)->set_playlist (pl);
-       }
-}
-
-bool
-Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
-{
-       if (region->data_type() != _type){
-               return false;
-       }
-
-       RegionSortByPosition cmp;
-
-       framecnt_t old_length = 0;
-
-       if (!holding_state()) {
-                old_length = _get_extent().second;
-       }
-
-       if (!first_set_state) {
-               boost::shared_ptr<Playlist> foo (shared_from_this());
-               region->set_playlist (boost::weak_ptr<Playlist>(foo));
-       }
-
-       region->set_position (position);
-
-       timestamp_layer_op (region);
-
-       regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
-       all_regions.insert (region);
-
-       possibly_splice_unlocked (position, region->length(), region);
-
-       if (!holding_state ()) {
-               /* layers get assigned from XML state, and are not reset during undo/redo */
-               relayer ();
-       }
-
-       /* we need to notify the existence of new region before checking dependents. Ick. */
-
-       notify_region_added (region);
-
-
-       if (!holding_state ()) {
-
-               check_dependents (region, false);
-
-               if (old_length != _get_extent().second) {
-                       notify_length_changed ();
-               }
-       }
-
-       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, framepos_t pos)
-{
-       RegionLock rlock (this);
-
-       bool old_sp = _splicing;
-       _splicing = true;
-
-       remove_region_internal (old);
-       add_region_internal (newr, pos);
-
-       _splicing = old_sp;
-
-       possibly_splice_unlocked (pos, old->length() - newr->length());
-}
-
-void
-Playlist::remove_region (boost::shared_ptr<Region> region)
-{
-       RegionLock rlock (this);
-       remove_region_internal (region);
-}
-
-int
-Playlist::remove_region_internal (boost::shared_ptr<Region> region)
-{
-       RegionList::iterator i;
-       framecnt_t old_length = 0;
-       int ret = -1;
-
-       if (!holding_state()) {
-               old_length = _get_extent().second;
-       }
-
-       if (!in_set_state) {
-               /* unset playlist */
-               region->set_playlist (boost::weak_ptr<Playlist>());
-       }
-
-       /* XXX should probably freeze here .... */
-
-       for (i = regions.begin(); i != regions.end(); ++i) {
-               if (*i == region) {
-
-                       framepos_t pos = (*i)->position();
-                       framecnt_t distance = (*i)->length();
-
-                       regions.erase (i);
-
-                       possibly_splice_unlocked (pos, -distance);
-
-                       if (!holding_state ()) {
-                               relayer ();
-                               remove_dependents (region);
-
-                               if (old_length != _get_extent().second) {
-                                       notify_length_changed ();
-                               }
-                       }
-
-                       notify_region_removed (region);
-                       ret = 0;
-                       break;
-               }
-       }
-
-       return -1;
-}
-
-void
-Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
-{
-       if (Config->get_use_overlap_equivalency()) {
-               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                       if ((*i)->overlap_equivalent (other)) {
-                               results.push_back ((*i));
-                       }
-               }
-       } else {
-               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                       if ((*i)->equivalent (other)) {
-                               results.push_back ((*i));
-                       }
-               }
-       }
-}
-
-void
-Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
-{
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-               if ((*i) && (*i)->region_list_equivalent (other)) {
-                       results.push_back (*i);
-               }
-       }
-}
-
-void
-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)->resume_property_changes ();
-       }
-}
-
-void
-Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
-{
-       RegionList new_regions;
-
-       {
-               RegionLock rlock (this);
-
-               boost::shared_ptr<Region> region;
-               boost::shared_ptr<Region> current;
-               string new_name;
-               RegionList::iterator tmp;
-               OverlapType overlap;
-               framepos_t pos1, pos2, pos3, pos4;
-
-               in_partition = true;
-
-               /* need to work from a copy, because otherwise the regions we add during the process
-                  get operated on as well.
-               */
-
-               RegionList copy = regions.rlist();
-
-               for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
-
-                       tmp = i;
-                       ++tmp;
-
-                       current = *i;
-
-                       if (current->first_frame() >= start && current->last_frame() < end) {
-
-                               if (cutting) {
-                                       remove_region_internal (current);
-                               }
-
-                               continue;
-                       }
-
-                       /* coverage will return OverlapStart if the start coincides
-                          with the end point. we do not partition such a region,
-                          so catch this special case.
-                       */
-
-                       if (current->first_frame() >= end) {
-                               continue;
-                       }
-
-                       if ((overlap = current->coverage (start, end)) == OverlapNone) {
-                               continue;
-                       }
-
-                       pos1 = current->position();
-                       pos2 = start;
-                       pos3 = end;
-                       pos4 = current->last_frame();
-
-                       if (overlap == OverlapInternal) {
-                               /* split: we need 3 new regions, the front, middle and end.
-                                  cut:   we need 2 regions, the front and end.
-                               */
-
-                               /*
-                                        start                 end
-                         ---------------*************************------------
-                                        P1  P2              P3  P4
-                         SPLIT:
-                         ---------------*****++++++++++++++++====------------
-                         CUT
-                         ---------------*****----------------====------------
-
-                               */
-
-                               if (!cutting) {
-                                       /* "middle" ++++++ */
-
-                                       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" ====== */
-
-                               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->suspend_property_changes ();
-                               thawlist.push_back (current);
-                               current->cut_end (pos2 - 1);
-
-                       } else if (overlap == OverlapEnd) {
-
-                               /*
-                                                             start           end
-                                   ---------------*************************------------
-                                                  P1           P2         P4   P3
-                                    SPLIT:
-                                   ---------------**************+++++++++++------------
-                                    CUT:
-                                   ---------------**************-----------------------
-                               */
-
-                               if (!cutting) {
-
-                                       /* end +++++ */
-
-                                       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);
-                               }
-
-                               /* front ****** */
-
-                               current->suspend_property_changes ();
-                               thawlist.push_back (current);
-                               current->cut_end (pos2 - 1);
-
-                       } else if (overlap == OverlapStart) {
-
-                               /* split: we need 2 regions: the front and the end.
-                                  cut: just trim current to skip the cut area
-                               */
-
-                               /*
-                                                       start           end
-                                   ---------------*************************------------
-                                      P2          P1 P3                   P4
-
-                                   SPLIT:
-                                   ---------------****+++++++++++++++++++++------------
-                                   CUT:
-                                   -------------------*********************------------
-
-                               */
-
-                               if (!cutting) {
-                                       /* front **** */
-                                       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);
-                               }
-
-                               /* end */
-
-                               current->suspend_property_changes ();
-                               thawlist.push_back (current);
-                               current->trim_front (pos3);
-                       } else if (overlap == OverlapExternal) {
-
-                               /* split: no split required.
-                                  cut: remove the region.
-                               */
-
-                               /*
-                                      start                                      end
-                                   ---------------*************************------------
-                                      P2          P1 P3                   P4
-
-                                   SPLIT:
-                                   ---------------*************************------------
-                                   CUT:
-                                   ----------------------------------------------------
-
-                               */
-
-                               if (cutting) {
-                                       remove_region_internal (current);
-                               }
-
-                               new_regions.push_back (current);
-                       }
-               }
-
-               in_partition = false;
-       }
-
-       for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
-               check_dependents (*i, false);
-       }
-}
-
-boost::shared_ptr<Playlist>
-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;
-       framepos_t start;
-
-       if (ranges.empty()) {
-               return boost::shared_ptr<Playlist>();
-       }
-
-       start = ranges.front().start;
-
-       for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
-
-               pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
-
-               if (i == ranges.begin()) {
-                       ret = pl;
-               } else {
-
-                       /* paste the next section into the nascent playlist,
-                          offset to reflect the start of the first range we
-                          chopped.
-                       */
-
-                       ret->paste (pl, (*i).start - start, 1.0f);
-               }
-       }
-
-       return ret;
-}
-
-boost::shared_ptr<Playlist>
-Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
-{
-       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)(framepos_t,framecnt_t,bool) = &Playlist::copy;
-       return cut_copy (pmf, ranges, result_is_hidden);
-}
-
-boost::shared_ptr<Playlist>
-Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
-{
-       boost::shared_ptr<Playlist> the_copy;
-       RegionList thawlist;
-       char buf[32];
-
-       snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
-       string new_name = _name;
-       new_name += '.';
-       new_name += buf;
-
-       if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
-               return boost::shared_ptr<Playlist>();
-       }
-
-       partition_internal (start, start+cnt-1, true, thawlist);
-
-       for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
-               (*i)->resume_property_changes();
-       }
-
-       return the_copy;
-}
-
-boost::shared_ptr<Playlist>
-Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
-{
-       char buf[32];
-
-       snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
-       string new_name = _name;
-       new_name += '.';
-       new_name += buf;
-
-       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, framepos_t position, float times)
-{
-       times = fabs (times);
-
-       {
-               RegionLock rl1 (this);
-               RegionLock rl2 (other.get());
-
-               framecnt_t const old_length = _get_extent().second;
-
-               int itimes = (int) floor (times);
-               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, 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, (*i)->position() + pos);
-                       }
-                       pos += shift;
-               }
-
+                // 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);
+        }
+
+        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 */
+        }
+
+        if (regions_changed || pending_contents_change) {
+                if (!in_set_state) {
+                        relayer ();
+                }
+                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, from_undo);
+        }
+
+        if (!pending_region_extensions.empty ()) {
+                RegionsExtended (pending_region_extensions);
+        }
+
+        clear_pending ();
+
+        in_flush = false;
+ }
+
+ void
+ Playlist::clear_pending ()
+ {
+        pending_adds.clear ();
+        pending_removes.clear ();
+        pending_bounds.clear ();
+        pending_range_moves.clear ();
+        pending_region_extensions.clear ();
+        pending_contents_change = false;
+        pending_length = false;
+ }
+
+ /*************************************************************
+   PLAYLIST OPERATIONS
+  *************************************************************/
+
+ void
+ 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);
+
+        framepos_t pos = position;
+
+        if (times == 1 && auto_partition){
+                partition(pos - 1, (pos + region->length()), true);
+        }
+
+        if (itimes >= 1) {
+                add_region_internal (region, pos);
+                pos += region->length();
+                --itimes;
+        }
+
+
+        /* note that itimes can be zero if we being asked to just
+           insert a single fraction of the region.
+        */
 
 
-               /* XXX shall we handle fractional cases at some point? */
+        for (int i = 0; i < itimes; ++i) {
+                boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
+                add_region_internal (copy, pos);
+                pos += region->length();
+        }
 
 
-               if (old_length != _get_extent().second) {
-                       notify_length_changed ();
-               }
+        framecnt_t length = 0;
 
 
+        if (floor (times) != times) {
+                length = (framecnt_t) floor (region->length() * (times - floor (times)));
+                string name;
+                RegionFactory::region_name (name, region->name(), false);
 
 
-       }
+                {
+                        PropertyList plist;
 
 
-       return 0;
-}
+                        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);
+                }
+        }
 
 
-void
-Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
-{
-       times = fabs (times);
+        possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
+ }
 
 
-       RegionLock rl (this);
-       int itimes = (int) floor (times);
-       framepos_t pos = position + 1;
+ void
+ Playlist::set_region_ownership ()
+ {
+        RegionLock rl (this);
+        RegionList::iterator i;
+        boost::weak_ptr<Playlist> pl (shared_from_this());
 
 
-       while (itimes--) {
-               boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
-               add_region_internal (copy, pos);
-               pos += region->length();
-       }
+        for (i = regions.begin(); i != regions.end(); ++i) {
+                (*i)->set_playlist (pl);
+        }
+ }
 
 
-       if (floor (times) != times) {
-               framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
-               string name;
-               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);
-               }
-       }
-}
+ bool
+ Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t position)
+ {
+        if (region->data_type() != _type){
+                return false;
+        }
 
 
-void
-Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
-{
-       RegionLock rlock (this);
-       RegionList copy (regions.rlist());
-       RegionList fixup;
+        RegionSortByPosition cmp;
 
 
-       for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+        framecnt_t old_length = 0;
 
 
-               if ((*r)->last_frame() < at) {
-                       /* too early */
-                       continue;
-               }
+        if (!holding_state()) {
+                 old_length = _get_extent().second;
+        }
 
 
-               if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
-                       /* intersected region */
-                       if (!move_intersected) {
-                               continue;
-                       }
-               }
+        if (!first_set_state) {
+                boost::shared_ptr<Playlist> foo (shared_from_this());
+                region->set_playlist (boost::weak_ptr<Playlist>(foo));
+        }
 
 
-               /* do not move regions glued to music time - that
-                  has to be done separately.
-               */
+        region->set_position (position);
 
 
-               if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
-                       fixup.push_back (*r);
-                       continue;
-               }
+        timestamp_layer_op (region);
 
 
-               (*r)->set_position ((*r)->position() + distance);
-       }
+        regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
+        all_regions.insert (region);
 
 
-       for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
-               (*r)->recompute_position_from_lock_style ();
-       }
-}
+        possibly_splice_unlocked (position, region->length(), region);
 
 
-void
-Playlist::split (framepos_t at)
-{
-       RegionLock rlock (this);
-       RegionList copy (regions.rlist());
+        if (!holding_state ()) {
+                /* layers get assigned from XML state, and are not reset during undo/redo */
+                relayer ();
+        }
 
 
-       /* use a copy since this operation can modify the region list
-        */
+        /* we need to notify the existence of new region before checking dependents. Ick. */
 
 
-       for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
-               _split_region (*r, at);
-       }
-}
-
-void
-Playlist::split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
-{
-       RegionLock rl (this);
-       _split_region (region, playlist_position);
-}
+        notify_region_added (region);
 
 
-void
-Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_position)
-{
-       if (!region->covers (playlist_position)) {
-               return;
-       }
 
 
-       if (region->position() == playlist_position ||
-           region->last_frame() == playlist_position) {
-               return;
-       }
+        if (!holding_state ()) {
 
 
-       boost::shared_ptr<Region> left;
-       boost::shared_ptr<Region> right;
-       frameoffset_t before;
-       frameoffset_t after;
-       string before_name;
-       string after_name;
+                check_dependents (region, false);
 
 
-       /* split doesn't change anything about length, so don't try to splice */
+                if (old_length != _get_extent().second) {
+                        notify_length_changed ();
+                }
+        }
 
 
-       bool old_sp = _splicing;
-       _splicing = true;
+        region->PropertyChanged.connect_same_thread (region_state_changed_connections, boost::bind (&Playlist::region_changed_proxy, this, _1, boost::weak_ptr<Region> (region)));
 
 
-       before = playlist_position - region->position();
-       after = region->length() - before;
+        return true;
+ }
 
 
-       RegionFactory::region_name (before_name, region->name(), false);
+ void
+ Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, framepos_t pos)
+ {
+        RegionLock rlock (this);
 
 
-       {
-               PropertyList plist;
+        bool old_sp = _splicing;
+        _splicing = true;
 
 
-               plist.add (Properties::position, region->position ());
-               plist.add (Properties::length, before);
-               plist.add (Properties::name, before_name);
-               plist.add (Properties::left_of_split, true);
+        remove_region_internal (old);
+        add_region_internal (newr, pos);
 
 
-               /* 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);
-       }
+        _splicing = old_sp;
 
 
-       RegionFactory::region_name (after_name, region->name(), false);
+        possibly_splice_unlocked (pos, old->length() - newr->length());
+ }
 
 
-       {
-               PropertyList plist;
+ void
+ Playlist::remove_region (boost::shared_ptr<Region> region)
+ {
+        RegionLock rlock (this);
+        remove_region_internal (region);
+ }
 
 
-               plist.add (Properties::position, region->position() + before);
-               plist.add (Properties::length, after);
-               plist.add (Properties::name, after_name);
-               plist.add (Properties::right_of_split, true);
+ int
+ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
+ {
+        RegionList::iterator i;
+        framecnt_t old_length = 0;
 
 
-               /* same note as above */
-               right = RegionFactory::create (region, before, plist);
-       }
-
-       add_region_internal (left, region->position());
-       add_region_internal (right, region->position() + before);
-
-       uint64_t orig_layer_op = region->last_layer_op();
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->last_layer_op() > orig_layer_op) {
-                       (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
-               }
-       }
-
-       left->set_last_layer_op ( orig_layer_op );
-       right->set_last_layer_op ( orig_layer_op + 1);
-
-       layer_op_counter++;
-
-       finalize_split_region (region, left, right);
-
-       remove_region_internal (region);
-
-       _splicing = old_sp;
-}
-
-void
-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 */
-               return;
-       }
-
-       if (_edit_mode == Splice) {
-               splice_locked (at, distance, exclude);
-       }
-}
-
-void
-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 */
-               return;
-       }
-
-       if (_edit_mode == Splice) {
-               splice_unlocked (at, distance, exclude);
-       }
-}
-
-void
-Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
-{
-       {
-               RegionLock rl (this);
-               core_splice (at, distance, exclude);
-       }
-}
-
-void
-Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
-{
-       core_splice (at, distance, exclude);
-}
-
-void
-Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
-{
-       _splicing = true;
-
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-
-               if (exclude && (*i) == exclude) {
-                       continue;
-               }
+        if (!holding_state()) {
+                old_length = _get_extent().second;
+        }
+
+        if (!in_set_state) {
+                /* unset playlist */
+                region->set_playlist (boost::weak_ptr<Playlist>());
+        }
+
+        /* XXX should probably freeze here .... */
+
+        for (i = regions.begin(); i != regions.end(); ++i) {
+                if (*i == region) {
+
+                        framepos_t pos = (*i)->position();
+                        framecnt_t distance = (*i)->length();
+
+                        regions.erase (i);
+
+                        possibly_splice_unlocked (pos, -distance);
+
+                        if (!holding_state ()) {
+                                relayer ();
+                                remove_dependents (region);
+
+                                if (old_length != _get_extent().second) {
+                                        notify_length_changed ();
+                                }
+                        }
+
+                        notify_region_removed (region);
+                        break;
+                }
+        }
+
+        return -1;
+ }
+
+ void
+ Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
+ {
+        if (Config->get_use_overlap_equivalency()) {
+                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                        if ((*i)->overlap_equivalent (other)) {
+                                results.push_back ((*i));
+                        }
+                }
+        } else {
+                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                        if ((*i)->equivalent (other)) {
+                                results.push_back ((*i));
+                        }
+                }
+        }
+ }
+
+ void
+ Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
+ {
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+                if ((*i) && (*i)->region_list_equivalent (other)) {
+                        results.push_back (*i);
+                }
+        }
+ }
+
+ void
+ 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)->resume_property_changes ();
+        }
+ }
+
+ void
+ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
+ {
+        RegionList new_regions;
+
+        {
+                RegionLock rlock (this);
+
+                boost::shared_ptr<Region> region;
+                boost::shared_ptr<Region> current;
+                string new_name;
+                RegionList::iterator tmp;
+                OverlapType overlap;
+                framepos_t pos1, pos2, pos3, pos4;
+
+                in_partition = true;
+
+                /* need to work from a copy, because otherwise the regions we add during the process
+                   get operated on as well.
+                */
 
 
-               if ((*i)->position() >= at) {
-                       framepos_t new_pos = (*i)->position() + distance;
-                       if (new_pos < 0) {
-                               new_pos = 0;
-                       } else if (new_pos >= max_framepos - (*i)->length()) {
-                               new_pos = max_framepos - (*i)->length();
-                       }
+                RegionList copy = regions.rlist();
 
 
-                       (*i)->set_position (new_pos);
-               }
-       }
+                for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
 
 
-       _splicing = false;
+                        tmp = i;
+                        ++tmp;
 
 
-       notify_length_changed ();
-}
+                        current = *i;
 
 
-void
-Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
-{
-       if (in_set_state || _splicing || _nudging || _shuffling) {
-               return;
-       }
+                        if (current->first_frame() >= start && current->last_frame() < end) {
 
 
-       if (what_changed.contains (Properties::position)) {
+                                if (cutting) {
+                                        remove_region_internal (current);
+                                }
 
 
-               /* remove it from the list then add it back in
-                  the right place again.
-               */
+                                continue;
+                        }
 
 
-               RegionSortByPosition cmp;
+                        /* coverage will return OverlapStart if the start coincides
+                           with the end point. we do not partition such a region,
+                           so catch this special case.
+                        */
 
 
-               RegionList::iterator i = find (regions.begin(), regions.end(), region);
+                        if (current->first_frame() >= end) {
+                                continue;
+                        }
 
 
-               if (i == regions.end()) {
-                       /* 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;
-               }
+                        if ((overlap = current->coverage (start, end)) == OverlapNone) {
+                                continue;
+                        }
 
 
-               regions.erase (i);
-               regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
-       }
+                        pos1 = current->position();
+                        pos2 = start;
+                        pos3 = end;
+                        pos4 = current->last_frame();
 
 
-       if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
+                        if (overlap == OverlapInternal) {
+                                /* split: we need 3 new regions, the front, middle and end.
+                                   cut:   we need 2 regions, the front and end.
+                                */
 
 
-               frameoffset_t delta = 0;
+                                /*
+                                         start                 end
+                          ---------------*************************------------
+                                         P1  P2              P3  P4
+                          SPLIT:
+                          ---------------*****++++++++++++++++====------------
+                          CUT
+                          ---------------*****----------------====------------
 
 
-               if (what_changed.contains (Properties::position)) {
-                       delta = region->position() - region->last_position();
-               }
+                                */
 
 
-               if (what_changed.contains (Properties::length)) {
-                       delta += region->length() - region->last_length();
-               }
+                                if (!cutting) {
+                                        /* "middle" ++++++ */
 
 
-               if (delta) {
-                       possibly_splice (region->last_position() + region->last_length(), delta, region);
-               }
+                                        RegionFactory::region_name (new_name, current->name(), false);
 
 
-               if (holding_state ()) {
-                       pending_bounds.push_back (region);
-               } else {
-                       if (_session.config.get_layer_model() == MoveAddHigher) {
-                               /* it moved or changed length, so change the timestamp */
-                               timestamp_layer_op (region);
-                       }
+                                        PropertyList plist;
 
 
-                       notify_length_changed ();
-                       relayer ();
-                       check_dependents (region, false);
-               }
-       }
-}
+                                        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);
 
 
-void
-Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
-{
-       boost::shared_ptr<Region> region (weak_region.lock());
+                                        region = RegionFactory::create (current, plist);
+                                        add_region_internal (region, start);
+                                        new_regions.push_back (region);
+                                }
 
 
-       if (!region) {
-               return;
-       }
+                                /* "end" ====== */
 
 
-       /* this makes a virtual call to the right kind of playlist ... */
+                                RegionFactory::region_name (new_name, current->name(), false);
 
 
-       region_changed (what_changed, region);
-}
+                                PropertyList plist;
 
 
-bool
-Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
-{
-       PropertyChange our_interests;
-       PropertyChange bounds;
-       PropertyChange pos_and_length;
-       bool save = false;
+                                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);
 
 
-       if (in_set_state || in_flush) {
-               return false;
-       }
+                                region = RegionFactory::create (current, plist);
 
 
-       our_interests.add (Properties::muted);
-       our_interests.add (Properties::layer);
-       our_interests.add (Properties::opaque);
+                                add_region_internal (region, end);
+                                new_regions.push_back (region);
 
 
-       bounds.add (Properties::start);
-       bounds.add (Properties::position);
-       bounds.add (Properties::length);
+                                /* "front" ***** */
 
 
-       pos_and_length.add (Properties::position);
-       pos_and_length.add (Properties::length);
+                                current->suspend_property_changes ();
+                                thawlist.push_back (current);
+                                current->cut_end (pos2 - 1);
 
 
-       if (what_changed.contains (bounds)) {
-               region_bounds_changed (what_changed, region);
-               save = !(_splicing || _nudging);
-       }
+                        } else if (overlap == OverlapEnd) {
 
 
-       if (what_changed.contains (our_interests) && !what_changed.contains (pos_and_length)) {
-               check_dependents (region, false);
-       }
+                                /*
+                                                              start           end
+                                    ---------------*************************------------
+                                                   P1           P2         P4   P3
+                                    SPLIT:
+                                    ---------------**************+++++++++++------------
+                                    CUT:
+                                    ---------------**************-----------------------
+                                */
 
 
-       if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
-               notify_region_moved (region);
-       } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
-               notify_region_end_trimmed (region);
-       } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
-               notify_region_start_trimmed (region);
-       }
+                                if (!cutting) {
 
 
-       /* don't notify about layer changes, since we are the only object that can initiate
-          them, and we notify in ::relayer()
-       */
+                                        /* end +++++ */
 
 
-       if (what_changed.contains (our_interests)) {
-               save = true;
-       }
+                                        RegionFactory::region_name (new_name, current->name(), false);
 
 
-       return save;
-}
+                                        PropertyList plist;
 
 
-void
-Playlist::drop_regions ()
-{
-       RegionLock rl (this);
-       regions.clear ();
-       all_regions.clear ();
-}
+                                        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);
 
 
-void
-Playlist::sync_all_regions_with_regions ()
-{
-       RegionLock rl (this);
+                                        region = RegionFactory::create (current, plist);
 
 
-       all_regions.clear ();
+                                        add_region_internal (region, start);
+                                        new_regions.push_back (region);
+                                }
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               all_regions.insert (*i);
-       }
-}
+                                /* front ****** */
 
 
-void
-Playlist::clear (bool with_signals)
-{
-       {
-               RegionLock rl (this);
+                                current->suspend_property_changes ();
+                                thawlist.push_back (current);
+                                current->cut_end (pos2 - 1);
 
 
-               region_state_changed_connections.drop_connections ();
+                        } else if (overlap == OverlapStart) {
 
 
-               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                       pending_removes.insert (*i);
-               }
+                                /* split: we need 2 regions: the front and the end.
+                                   cut: just trim current to skip the cut area
+                                */
 
 
-               regions.clear ();
+                                /*
+                                                        start           end
+                                    ---------------*************************------------
+                                       P2          P1 P3                   P4
 
 
-               for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
-                       remove_dependents (*s);
-               }
-       }
+                                    SPLIT:
+                                    ---------------****+++++++++++++++++++++------------
+                                    CUT:
+                                    -------------------*********************------------
 
 
-       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 */
-               }
+                                if (!cutting) {
+                                        /* front **** */
+                                        RegionFactory::region_name (new_name, current->name(), false);
 
 
-               pending_removes.clear ();
-               pending_length = false;
-               LengthChanged ();
-               pending_contents_change = false;
-               ContentsChanged ();
-       }
+                                        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);
 
 
-/***********************************************************************
- FINDING THINGS
- **********************************************************************/
+                                        region = RegionFactory::create (current, plist);
 
 
-Playlist::RegionList *
-Playlist::regions_at (framepos_t frame)
+                                        add_region_internal (region, pos1);
+                                        new_regions.push_back (region);
+                                }
 
 
-{
-       RegionLock rlock (this);
-       return find_regions_at (frame);
-}
+                                /* end */
 
 
-uint32_t
-Playlist::count_regions_at (framepos_t frame) const
-{
-       RegionLock rlock (const_cast<Playlist*>(this));
-       uint32_t cnt = 0;
+                                current->suspend_property_changes ();
+                                thawlist.push_back (current);
+                                current->trim_front (pos3);
+                        } else if (overlap == OverlapExternal) {
 
 
-       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->covers (frame)) {
-                       cnt++;
-               }
-       }
+                                /* split: no split required.
+                                   cut: remove the region.
+                                */
 
 
-       return cnt;
-}
+                                /*
+                                       start                                      end
+                                    ---------------*************************------------
+                                       P2          P1 P3                   P4
 
 
-boost::shared_ptr<Region>
-Playlist::top_region_at (framepos_t frame)
+                                    SPLIT:
+                                    ---------------*************************------------
+                                    CUT:
+                                    ----------------------------------------------------
 
 
-{
-       RegionLock rlock (this);
-       RegionList *rlist = find_regions_at (frame);
-       boost::shared_ptr<Region> region;
+                                */
 
 
-       if (rlist->size()) {
-               RegionSortByLayer cmp;
-               rlist->sort (cmp);
-               region = rlist->back();
-       }
+                                if (cutting) {
+                                        remove_region_internal (current);
+                                }
 
 
-       delete rlist;
-       return region;
-}
+                                new_regions.push_back (current);
+                        }
+                }
 
 
-boost::shared_ptr<Region>
-Playlist::top_unmuted_region_at (framepos_t frame)
+                in_partition = false;
+        }
 
 
-{
-       RegionLock rlock (this);
-       RegionList *rlist = find_regions_at (frame);
+        for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
+                check_dependents (*i, false);
+        }
+ }
 
 
-       for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
+ boost::shared_ptr<Playlist>
+ 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;
+        framepos_t start;
 
 
-               RegionList::iterator tmp = i;
-               ++tmp;
+        if (ranges.empty()) {
+                return boost::shared_ptr<Playlist>();
+        }
 
 
-               if ((*i)->muted()) {
-                       rlist->erase (i);
-               }
+        start = ranges.front().start;
 
 
-               i = tmp;
-       }
+        for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
 
 
-       boost::shared_ptr<Region> region;
+                pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
 
 
-       if (rlist->size()) {
-               RegionSortByLayer cmp;
-               rlist->sort (cmp);
-               region = rlist->back();
-       }
+                if (i == ranges.begin()) {
+                        ret = pl;
+                } else {
 
 
-       delete rlist;
-       return region;
-}
+                        /* paste the next section into the nascent playlist,
+                           offset to reflect the start of the first range we
+                           chopped.
+                        */
 
 
-Playlist::RegionList*
-Playlist::regions_to_read (framepos_t start, framepos_t end)
-{
-       /* Caller must hold lock */
+                        ret->paste (pl, (*i).start - start, 1.0f);
+                }
+        }
 
 
-       RegionList covering;
-       set<framepos_t> to_check;
-       set<boost::shared_ptr<Region> > unique;
+        return ret;
+ }
 
 
-       to_check.insert (start);
-       to_check.insert (end);
+ boost::shared_ptr<Playlist>
+ Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
+ {
+        boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::cut;
+        return cut_copy (pmf, ranges, result_is_hidden);
+ }
 
 
-       DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
+ boost::shared_ptr<Playlist>
+ Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
+ {
+        boost::shared_ptr<Playlist> (Playlist::*pmf)(framepos_t,framecnt_t,bool) = &Playlist::copy;
+        return cut_copy (pmf, ranges, result_is_hidden);
+ }
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ boost::shared_ptr<Playlist>
+ Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
+ {
+        boost::shared_ptr<Playlist> the_copy;
+        RegionList thawlist;
+        char buf[32];
 
 
-               /* find all/any regions that span start+end */
+        snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+        string new_name = _name;
+        new_name += '.';
+        new_name += buf;
 
 
-               switch ((*i)->coverage (start, end)) {
-               case OverlapNone:
-                       break;
+        if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
+                return boost::shared_ptr<Playlist>();
+        }
 
 
-               case OverlapInternal:
-                       covering.push_back (*i);
-                       DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
-                       break;
+        partition_internal (start, start+cnt-1, true, thawlist);
 
 
-               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;
+        for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
+                (*i)->resume_property_changes();
+        }
+
+        return the_copy;
+ }
 
 
-               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;
+ boost::shared_ptr<Playlist>
+ Playlist::copy (framepos_t start, framecnt_t cnt, bool result_is_hidden)
+ {
+        char buf[32];
 
 
-               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;
-               }
+        snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+        string new_name = _name;
+        new_name += '.';
+        new_name += buf;
+
+        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, framepos_t position, float times)
+ {
+        times = fabs (times);
+
+        {
+                RegionLock rl1 (this);
+                RegionLock rl2 (other.get());
+
+                framecnt_t const old_length = _get_extent().second;
+
+                int itimes = (int) floor (times);
+                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, 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, (*i)->position() + pos);
+                        }
+                        pos += shift;
+                }
+
+
+                /* XXX shall we handle fractional cases at some point? */
+
+                if (old_length != _get_extent().second) {
+                        notify_length_changed ();
+                }
+
+
+        }
+
+        return 0;
+ }
+
+
+ void
+ Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, float times)
+ {
+        times = fabs (times);
+
+        RegionLock rl (this);
+        int itimes = (int) floor (times);
+        framepos_t pos = position + 1;
+
+        while (itimes--) {
+                boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
+                add_region_internal (copy, pos);
+                pos += region->length();
+        }
 
 
-               /* don't go too far */
+        if (floor (times) != times) {
+                framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
+                string name;
+                RegionFactory::region_name (name, region->name(), false);
 
 
-               if ((*i)->position() > end) {
-                       break;
-               }
-       }
+                {
+                        PropertyList plist;
 
 
-       RegionList* rlist = new RegionList;
+                        plist.add (Properties::start, region->start());
+                        plist.add (Properties::length, length);
+                        plist.add (Properties::name, name);
 
 
-       /* find all the regions that cover each position .... */
+                        boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
+                        add_region_internal (sub, pos);
+                }
+        }
+ }
 
 
-       if (covering.size() == 1) {
+ void
+ Playlist::shift (framepos_t at, frameoffset_t distance, bool move_intersected, bool ignore_music_glue)
+ {
+        RegionLock rlock (this);
+        RegionList copy (regions.rlist());
+        RegionList fixup;
 
 
-               rlist->push_back (covering.front());
-               DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
+        for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
 
 
-       } else {
+                if ((*r)->last_frame() < at) {
+                        /* too early */
+                        continue;
+                }
 
 
-               RegionList here;
-               for (set<framepos_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
+                if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
+                        /* intersected region */
+                        if (!move_intersected) {
+                                continue;
+                        }
+                }
 
 
-                       here.clear ();
+                /* do not move regions glued to music time - that
+                   has to be done separately.
+                */
 
 
-                       DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("++++ Considering %1\n", *t));
+                if (!ignore_music_glue && (*r)->position_lock_style() != AudioTime) {
+                        fixup.push_back (*r);
+                        continue;
+                }
+
+                (*r)->set_position ((*r)->position() + distance);
+        }
+
+        for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
+                (*r)->recompute_position_from_lock_style ();
+        }
+ }
+
+ void
+ Playlist::split (framepos_t at)
+ {
+        RegionLock rlock (this);
+        RegionList copy (regions.rlist());
+
+        /* use a copy since this operation can modify the region list
+         */
+
+        for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+                _split_region (*r, at);
+        }
+ }
+
+ void
+ 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, framepos_t playlist_position)
+ {
+        if (!region->covers (playlist_position)) {
+                return;
+        }
+
+        if (region->position() == playlist_position ||
+            region->last_frame() == playlist_position) {
+                return;
+        }
+
+        boost::shared_ptr<Region> left;
+        boost::shared_ptr<Region> right;
+        frameoffset_t before;
+        frameoffset_t after;
+        string before_name;
+        string after_name;
+
+        /* split doesn't change anything about length, so don't try to splice */
+
+        bool old_sp = _splicing;
+        _splicing = true;
+
+        before = playlist_position - region->position();
+        after = region->length() - before;
+
+        RegionFactory::region_name (before_name, region->name(), false);
+
+        {
+                PropertyList plist;
+
+                plist.add (Properties::position, region->position ());
+                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::position, region->position() + before);
+                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);
+
+        uint64_t orig_layer_op = region->last_layer_op();
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                if ((*i)->last_layer_op() > orig_layer_op) {
+                        (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
+                }
+        }
+
+        left->set_last_layer_op ( orig_layer_op );
+        right->set_last_layer_op ( orig_layer_op + 1);
+
+        layer_op_counter++;
+
+        finalize_split_region (region, left, right);
+
+        remove_region_internal (region);
+
+        _splicing = old_sp;
+ }
+
+ void
+ 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 */
+                return;
+        }
+
+        if (_edit_mode == Splice) {
+                splice_locked (at, distance, exclude);
+        }
+ }
+
+ void
+ 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 */
+                return;
+        }
+
+        if (_edit_mode == Splice) {
+                splice_unlocked (at, distance, exclude);
+        }
+ }
+
+ void
+ Playlist::splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+        {
+                RegionLock rl (this);
+                core_splice (at, distance, exclude);
+        }
+ }
+
+ void
+ Playlist::splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+        core_splice (at, distance, exclude);
+ }
+
+ void
+ Playlist::core_splice (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude)
+ {
+        _splicing = true;
+
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+                if (exclude && (*i) == exclude) {
+                        continue;
+                }
+
+                if ((*i)->position() >= at) {
+                        framepos_t new_pos = (*i)->position() + distance;
+                        if (new_pos < 0) {
+                                new_pos = 0;
+                        } else if (new_pos >= max_framepos - (*i)->length()) {
+                                new_pos = max_framepos - (*i)->length();
+                        }
+
+                        (*i)->set_position (new_pos);
+                }
+        }
+
+        _splicing = false;
+
+        notify_length_changed ();
+ }
+
+ void
+ Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
+ {
+        if (in_set_state || _splicing || _nudging || _shuffling) {
+                return;
+        }
+
+        if (what_changed.contains (Properties::position)) {
+
+                /* remove it from the list then add it back in
+                   the right place again.
+                */
 
 
-                       for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
+                RegionSortByPosition cmp;
 
 
-                               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)));
-                               }
-                                        
-                       }
+                RegionList::iterator i = find (regions.begin(), regions.end(), region);
 
 
-                       RegionSortByLayer cmp;
-                       here.sort (cmp);
+                if (i == regions.end()) {
+                        /* 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;
+                }
+
+                regions.erase (i);
+                regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
+        }
+
+        if (what_changed.contains (Properties::position) || what_changed.contains (Properties::length)) {
+
+                frameoffset_t delta = 0;
+
+                if (what_changed.contains (Properties::position)) {
+                        delta = region->position() - region->last_position();
+                }
+
+                if (what_changed.contains (Properties::length)) {
+                        delta += region->length() - region->last_length();
+                }
+
+                if (delta) {
+                        possibly_splice (region->last_position() + region->last_length(), delta, region);
+                }
+
+                if (holding_state ()) {
+                        pending_bounds.push_back (region);
+                } else {
+                        if (_session.config.get_layer_model() == MoveAddHigher) {
+                                /* it moved or changed length, so change the timestamp */
+                                timestamp_layer_op (region);
+                        }
+
+                        notify_length_changed ();
+                        relayer ();
+                        check_dependents (region, false);
+                }
+        }
+ }
+
+ void
+ Playlist::region_changed_proxy (const PropertyChange& what_changed, boost::weak_ptr<Region> weak_region)
+ {
+        boost::shared_ptr<Region> region (weak_region.lock());
+
+        if (!region) {
+                return;
+        }
+
+        /* this makes a virtual call to the right kind of playlist ... */
+
+        region_changed (what_changed, region);
+ }
+
+ bool
+ Playlist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
+ {
+        PropertyChange our_interests;
+        PropertyChange bounds;
+        PropertyChange pos_and_length;
+        bool save = false;
+
+        if (in_set_state || in_flush) {
+                return false;
+        }
+
+        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.contains (our_interests) && !what_changed.contains (pos_and_length)) {
+                check_dependents (region, false);
+        }
+
+        if (what_changed.contains (Properties::position) && !what_changed.contains (Properties::length)) {
+                notify_region_moved (region);
+        } else if (!what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
+                notify_region_end_trimmed (region);
+        } else if (what_changed.contains (Properties::position) && what_changed.contains (Properties::length)) {
+                notify_region_start_trimmed (region);
+        }
+
+        /* don't notify about layer changes, since we are the only object that can initiate
+           them, and we notify in ::relayer()
+        */
 
 
-                       /* ... and get the top/transparent regions at "here" */
+        if (what_changed.contains (our_interests)) {
+                save = true;
+        }
+
+        return save;
+ }
+
+ void
+ Playlist::drop_regions ()
+ {
+        RegionLock rl (this);
+        regions.clear ();
+        all_regions.clear ();
+ }
 
 
-                       for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
+ void
+ Playlist::sync_all_regions_with_regions ()
+ {
+        RegionLock rl (this);
 
 
-                               unique.insert (*c);
+        all_regions.clear ();
 
 
-                               if ((*c)->opaque()) {
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                all_regions.insert (*i);
+        }
+ }
 
 
-                                       /* 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;
-                               }
-                       }
-               }
+ void
+ Playlist::clear (bool with_signals)
+ {
+        {
+                RegionLock rl (this);
 
 
-               for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
-                       rlist->push_back (*s);
-               }
+                region_state_changed_connections.drop_connections ();
 
 
-               if (rlist->size() > 1) {
-                       /* now sort by time order */
+                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                        pending_removes.insert (*i);
+                }
 
 
-                       RegionSortByPosition cmp;
-                       rlist->sort (cmp);
-               }
-       }
+                regions.clear ();
 
 
-       DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        remove_dependents (*s);
+                }
+        }
 
 
-       return rlist;
-}
+        if (with_signals) {
 
 
-Playlist::RegionList *
-Playlist::find_regions_at (framepos_t frame)
-{
-       /* Caller must hold lock */
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+                }
 
 
-       RegionList *rlist = new RegionList;
+                pending_removes.clear ();
+                pending_length = false;
+                LengthChanged ();
+                pending_contents_change = false;
+                ContentsChanged ();
+        }
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->covers (frame)) {
-                       rlist->push_back (*i);
-               }
-       }
+ }
 
 
-       return rlist;
-}
+ /***********************************************************************
+  FINDING THINGS
+  **********************************************************************/
 
 
-Playlist::RegionList *
-Playlist::regions_touched (framepos_t start, framepos_t end)
-{
-       RegionLock rlock (this);
-       RegionList *rlist = new RegionList;
+ Playlist::RegionList *
+ Playlist::regions_at (framepos_t frame)
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               if ((*i)->coverage (start, end) != OverlapNone) {
-                       rlist->push_back (*i);
-               }
-       }
+ {
+        RegionLock rlock (this);
+        return find_regions_at (frame);
+ }
 
 
-       return rlist;
-}
+ uint32_t
+ Playlist::count_regions_at (framepos_t frame) const
+ {
+        RegionLock rlock (const_cast<Playlist*>(this));
+        uint32_t cnt = 0;
+
+        for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+                if ((*i)->covers (frame)) {
+                        cnt++;
+                }
+        }
+
+        return cnt;
+ }
+
+ boost::shared_ptr<Region>
+ Playlist::top_region_at (framepos_t frame)
+
+ {
+        RegionLock rlock (this);
+        RegionList *rlist = find_regions_at (frame);
+        boost::shared_ptr<Region> region;
+
+        if (rlist->size()) {
+                RegionSortByLayer cmp;
+                rlist->sort (cmp);
+                region = rlist->back();
+        }
+
+        delete rlist;
+        return region;
+ }
+
+ boost::shared_ptr<Region>
+ Playlist::top_unmuted_region_at (framepos_t frame)
+
+ {
+        RegionLock rlock (this);
+        RegionList *rlist = find_regions_at (frame);
+
+        for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ) {
 
 
-framepos_t
-Playlist::find_next_transient (framepos_t from, int dir)
-{
-       RegionLock rlock (this);
-       AnalysisFeatureList points;
-       AnalysisFeatureList these_points;
+                RegionList::iterator tmp = i;
+                ++tmp;
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               if (dir > 0) {
-                       if ((*i)->last_frame() < from) {
-                               continue;
-                       }
-               } else {
-                       if ((*i)->first_frame() > from) {
-                               continue;
-                       }
-               }
+                if ((*i)->muted()) {
+                        rlist->erase (i);
+                }
 
 
-               (*i)->get_transients (these_points);
+                i = tmp;
+        }
 
 
-               /* add first frame, just, err, because */
+        boost::shared_ptr<Region> region;
 
 
-               these_points.push_back ((*i)->first_frame());
+        if (rlist->size()) {
+                RegionSortByLayer cmp;
+                rlist->sort (cmp);
+                region = rlist->back();
+        }
 
 
-               points.insert (points.end(), these_points.begin(), these_points.end());
-               these_points.clear ();
      }
+        delete rlist;
+        return region;
+ }
 
 
-       if (points.empty()) {
-               return -1;
-       }
+ Playlist::RegionList*
+ Playlist::regions_to_read (framepos_t start, framepos_t end)
+ {
+        /* Caller must hold lock */
 
 
-       TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
-       bool reached = false;
+        RegionList covering;
+        set<framepos_t> to_check;
+        set<boost::shared_ptr<Region> > unique;
 
 
-       if (dir > 0) {
-               for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
-                       if ((*x) >= from) {
-                               reached = true;
-                       }
+        to_check.insert (start);
+        to_check.insert (end);
 
 
-                       if (reached && (*x) > from) {
-                               return *x;
-                       }
-               }
-       } else {
-               for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
-                       if ((*x) <= from) {
-                               reached = true;
-                       }
+        DEBUG_TRACE (DEBUG::AudioPlayback, ">>>>> REGIONS TO READ\n");
 
 
-                       if (reached && (*x) < from) {
-                               return *x;
-                       }
-               }
-       }
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
 
 
-       return -1;
-}
+                /* find all/any regions that span start+end */
 
 
-boost::shared_ptr<Region>
-Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
-{
-       RegionLock rlock (this);
-       boost::shared_ptr<Region> ret;
-       framepos_t closest = max_framepos;
+                switch ((*i)->coverage (start, end)) {
+                case OverlapNone:
+                        break;
 
 
-       bool end_iter = false;
+                case OverlapInternal:
+                        covering.push_back (*i);
+                        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("toread: will cover %1 (OInternal)\n", (*i)->name()));
+                        break;
 
 
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                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;
 
 
-               if(end_iter) 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;
 
 
-               frameoffset_t distance;
-               boost::shared_ptr<Region> r = (*i);
-               framepos_t pos = 0;
+                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;
+                }
 
 
-               switch (point) {
-               case Start:
-                       pos = r->first_frame ();
-                       break;
-               case End:
-                       pos = r->last_frame ();
-                       break;
-               case SyncPoint:
-                       pos = r->sync_position ();
-                       break;
-               }
+                /* don't go too far */
 
 
-               switch (dir) {
-               case 1: /* forwards */
+                if ((*i)->position() > end) {
+                        break;
+                }
+        }
 
 
-                       if (pos > frame) {
-                               if ((distance = pos - frame) < closest) {
-                                       closest = distance;
-                                       ret = r;
-                                       end_iter = true;
-                               }
-                       }
+        RegionList* rlist = new RegionList;
 
 
-                       break;
+        /* find all the regions that cover each position .... */
 
 
-               default: /* backwards */
+        if (covering.size() == 1) {
 
 
-                       if (pos < frame) {
-                               if ((distance = frame - pos) < closest) {
-                                       closest = distance;
-                                       ret = r;
-                               }
-                       }
-                       else {
-                               end_iter = true;
-                       }
+                rlist->push_back (covering.front());
+                DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Just one covering region (%1)\n", covering.front()->name()));
 
 
-                       break;
-               }
-       }
+        } else {
 
 
-       return ret;
-}
+                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)));
+                                }
+
+                        }
 
 
-framepos_t
-Playlist::find_next_region_boundary (framepos_t frame, int dir)
-{
-       RegionLock rlock (this);
+                        RegionSortByLayer cmp;
+                        here.sort (cmp);
 
 
-       framepos_t closest = max_framepos;
-       framepos_t ret = -1;
+                        /* ... and get the top/transparent regions at "here" */
 
 
-       if (dir > 0) {
+                        for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
+
+                                unique.insert (*c);
 
 
-               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                                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;
+                                }
+                        }
+                }
+
+                for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
+                        rlist->push_back (*s);
+                }
+
+                if (rlist->size() > 1) {
+                        /* now sort by time order */
+
+                        RegionSortByPosition cmp;
+                        rlist->sort (cmp);
+                }
+        }
+
+        DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("<<<<< REGIONS TO READ returns %1\n", rlist->size()));
+
+        return rlist;
+ }
+
+ Playlist::RegionList *
+ Playlist::find_regions_at (framepos_t frame)
+ {
+        /* Caller must hold lock */
+
+        RegionList *rlist = new RegionList;
+
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                if ((*i)->covers (frame)) {
+                        rlist->push_back (*i);
+                }
+        }
+
+        return rlist;
+ }
+
+ Playlist::RegionList *
+ Playlist::regions_touched (framepos_t start, framepos_t end)
+ {
+        RegionLock rlock (this);
+        RegionList *rlist = new RegionList;
 
 
-                       boost::shared_ptr<Region> r = (*i);
-                       frameoffset_t distance;
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                if ((*i)->coverage (start, end) != OverlapNone) {
+                        rlist->push_back (*i);
+                }
+        }
+
+        return rlist;
+ }
 
 
-                       if (r->first_frame() > frame) {
+ framepos_t
+ Playlist::find_next_transient (framepos_t from, int dir)
+ {
+        RegionLock rlock (this);
+        AnalysisFeatureList points;
+        AnalysisFeatureList these_points;
+
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                if (dir > 0) {
+                        if ((*i)->last_frame() < from) {
+                                continue;
+                        }
+                } else {
+                        if ((*i)->first_frame() > from) {
+                                continue;
+                        }
+                }
+
+                (*i)->get_transients (these_points);
+
+                /* add first frame, just, err, because */
+
+                these_points.push_back ((*i)->first_frame());
+
+                points.insert (points.end(), these_points.begin(), these_points.end());
+                these_points.clear ();
+        }
+
+        if (points.empty()) {
+                return -1;
+        }
+
+        TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+        bool reached = false;
+
+        if (dir > 0) {
+                for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+                        if ((*x) >= from) {
+                                reached = true;
+                        }
+
+                        if (reached && (*x) > from) {
+                                return *x;
+                        }
+                }
+        } else {
+                for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+                        if ((*x) <= from) {
+                                reached = true;
+                        }
+
+                        if (reached && (*x) < from) {
+                                return *x;
+                        }
+                }
+        }
+
+        return -1;
+ }
+
+ boost::shared_ptr<Region>
+ Playlist::find_next_region (framepos_t frame, RegionPoint point, int dir)
+ {
+        RegionLock rlock (this);
+        boost::shared_ptr<Region> ret;
+        framepos_t closest = max_framepos;
+
+        bool end_iter = false;
+
+        for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+                if(end_iter) break;
+
+                frameoffset_t distance;
+                boost::shared_ptr<Region> r = (*i);
+                framepos_t pos = 0;
+
+                switch (point) {
+                case Start:
+                        pos = r->first_frame ();
+                        break;
+                case End:
+                        pos = r->last_frame ();
+                        break;
+                case SyncPoint:
+                        pos = r->sync_position ();
+                        break;
+                }
+
+                switch (dir) {
+                case 1: /* forwards */
 
 
-                               distance = r->first_frame() - frame;
+                        if (pos > frame) {
+                                if ((distance = pos - frame) < closest) {
+                                        closest = distance;
+                                        ret = r;
+                                        end_iter = true;
+                                }
+                        }
 
 
-                               if (distance < closest) {
-                                       ret = r->first_frame();
-                                       closest = distance;
-                               }
-                       }
+                        break;
 
 
-                       if (r->last_frame () > frame) {
+                default: /* backwards */
 
 
-                               distance = r->last_frame () - frame;
+                        if (pos < frame) {
+                                if ((distance = frame - pos) < closest) {
+                                        closest = distance;
+                                        ret = r;
+                                }
+                        }
+                        else {
+                                end_iter = true;
+                        }
 
 
-                               if (distance < closest) {
-                                       ret = r->last_frame ();
-                                       closest = distance;
-                               }
-                       }
-               }
+                        break;
+                }
+        }
 
 
-       } else {
+        return ret;
+ }
 
 
-               for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
+ framepos_t
+ Playlist::find_next_region_boundary (framepos_t frame, int dir)
+ {
+        RegionLock rlock (this);
 
 
-                       boost::shared_ptr<Region> r = (*i);
-                       frameoffset_t distance;
+        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);
+                        frameoffset_t distance;
+
+                        if (r->first_frame() > frame) {
+
+                                distance = r->first_frame() - frame;
+
+                                if (distance < closest) {
+                                        ret = r->first_frame();
+                                        closest = distance;
+                                }
+                        }
+
+                        if (r->last_frame () > frame) {
+
+                                distance = r->last_frame () - frame;
+
+                                if (distance < closest) {
+                                        ret = r->last_frame ();
+                                        closest = distance;
+                                }
+                        }
+                }
 
 
-                       if (r->last_frame() < frame) {
+        } else {
 
 
-                               distance = frame - r->last_frame();
+                for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
 
 
-                               if (distance < closest) {
-                                       ret = r->last_frame();
-                                       closest = distance;
-                               }
-                       }
+                        boost::shared_ptr<Region> r = (*i);
+                        frameoffset_t distance;
 
 
-                       if (r->first_frame() < frame) {
+                        if (r->last_frame() < frame) {
 
 
-                               distance = frame - r->first_frame();
+                                distance = frame - r->last_frame();
 
 
-                               if (distance < closest) {
-                                       ret = r->first_frame();
-                                       closest = distance;
-                               }
-                       }
-               }
-       }
+                                if (distance < closest) {
+                                        ret = r->last_frame();
+                                        closest = distance;
+                                }
+                        }
+
+                        if (r->first_frame() < frame) {
+
+                                distance = frame - r->first_frame();
+
+                                if (distance < closest) {
+                                        ret = r->first_frame();
+                                        closest = distance;
+                                }
+                        }
+                }
+        }
 
 
-       return ret;
-}
+        return ret;
+ }
 
 
 
 
-/***********************************************************************/
+ /***********************************************************************/
 
 
 
 
 
 
 
 
-void
-Playlist::mark_session_dirty ()
-{
-       if (!in_set_state && !holding_state ()) {
-               _session.set_dirty();
-       }
-}
+ void
+ Playlist::mark_session_dirty ()
+ {
+        if (!in_set_state && !holding_state ()) {
+                _session.set_dirty();
+        }
+ }
 
 
-void
-Playlist::rdiff (vector<Command*>& cmds) const
-{
-       RegionLock rlock (const_cast<Playlist *> (this));
-       Stateful::rdiff (cmds);
-}
+ 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::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);
-       }
+ 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()));
 
 
-       thaw ();
-}
+        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);
+        }
 
 
-int
-Playlist::set_state (const XMLNode& node, int version)
-{
-       XMLNode *child;
-       XMLNodeList nlist;
-       XMLNodeConstIterator niter;
-       XMLPropertyList plist;
-       XMLPropertyConstIterator piter;
-       XMLProperty *prop;
-       boost::shared_ptr<Region> region;
-       string region_name;
+        thaw ();
+ }
 
 
-       in_set_state++;
+ int
+ Playlist::set_state (const XMLNode& node, int version)
+ {
+        XMLNode *child;
+        XMLNodeList nlist;
+        XMLNodeConstIterator niter;
+        XMLPropertyList plist;
+        XMLPropertyConstIterator piter;
+        XMLProperty *prop;
+        boost::shared_ptr<Region> region;
+        string region_name;
+        bool seen_region_nodes = false;
+        int ret = 0;
 
 
-       if (node.name() != "Playlist") {
-               in_set_state--;
-               return -1;
-       }
+        in_set_state++;
 
 
-       freeze ();
+        if (node.name() != "Playlist") {
+                in_set_state--;
+                return -1;
+        }
 
 
-       plist = node.properties();
+        freeze ();
 
 
-       for (piter = plist.begin(); piter != plist.end(); ++piter) {
+        plist = node.properties();
 
 
-               prop = *piter;
+        set_id (node);
 
 
-               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")) {
-                       _frozen = string_is_affirmative (prop->value());
-               } else if (prop->name() == X_("combine-ops")) {
-                       _combine_ops = atoi (prop->value());
-               }
-       }
+        for (piter = plist.begin(); piter != plist.end(); ++piter) {
 
 
-       clear (true);
+                prop = *piter;
 
 
-       nlist = node.children();
+                if (prop->name() == X_("name")) {
+                        _name = prop->value();
+                        _set_sort_id ();
+                } else if (prop->name() == X_("orig-diskstream-id")) {
+                        _orig_diskstream_id = prop->value ();
+                } else if (prop->name() == X_("frozen")) {
+                        _frozen = string_is_affirmative (prop->value());
+                } else if (prop->name() == X_("combine-ops")) {
+                        _combine_ops = atoi (prop->value());
+                }
+        }
 
 
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+        clear (true);
 
 
-               child = *niter;
+        nlist = node.children();
 
 
-               if (child->name() == "Region") {
+        for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
 
 
-                       if ((prop = child->property ("id")) == 0) {
-                               error << _("region state node has no ID, ignored") << endmsg;
-                               continue;
-                       }
-                       
-                       ID id = prop->value ();
+                child = *niter;
 
 
-                       if ((region = region_by_id (id))) {
+                if (child->name() == "Region") {
 
 
-                               region->suspend_property_changes ();
+                        seen_region_nodes = true;
 
 
-                               if (region->set_state (*child, version)) {
-                                       region->resume_property_changes ();
-                                       continue;
-                               }
-                               
-                       } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
-                               region->suspend_property_changes ();
-                       } else {
-                               error << _("Playlist: cannot create region from XML") << endmsg;
-                               continue;
+                        if ((prop = child->property ("id")) == 0) {
+                                error << _("region state node has no ID, ignored") << endmsg;
+                                continue;
+                        }
+
+                        ID id = prop->value ();
+
+                        if ((region = region_by_id (id))) {
+
+                                region->suspend_property_changes ();
+
+                                if (region->set_state (*child, version)) {
+                                        region->resume_property_changes ();
+                                        continue;
+                                }
+
+                        } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
+                                region->suspend_property_changes ();
+                        } else {
+                                error << _("Playlist: cannot create region from XML") << endmsg;
+                               return -1;
                        }
                        }
-                       
 
                        add_region (region, region->position(), 1.0);
 
                        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 ();
 
                        // So that layer_op ordering doesn't get screwed up
                        region->set_last_layer_op( region->layer());
                        region->resume_property_changes ();
 
-               } 
+               }
        }
 
        }
 
-       /* update dependents, which was not done during add_region_internal
-          due to in_set_state being true
-       */
+       if (seen_region_nodes && regions.empty()) {
+               ret = -1;
+       } else {
 
 
-       for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
-               check_dependents (*r, false);
+               /* update dependents, which was not done during add_region_internal
+                  due to in_set_state being true
+               */
+               
+               for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
+                       check_dependents (*r, false);
+               }
        }
        }
-
+               
        thaw ();
        notify_contents_changed ();
 
        in_set_state--;
        first_set_state = false;
        thaw ();
        notify_contents_changed ();
 
        in_set_state--;
        first_set_state = false;
-       return 0;
+       return ret;
 }
 
 XMLNode&
 }
 
 XMLNode&
@@ -2362,6 +2366,11 @@ Playlist::_get_extent () const
 {
        pair<framepos_t, framepos_t> ext (max_framepos, 0);
 
 {
        pair<framepos_t, framepos_t> ext (max_framepos, 0);
 
+       if (regions.empty()) {
+               ext.first = 0;
+               return ext;
+       }
+
        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) {
        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) {
@@ -2524,7 +2533,7 @@ Playlist::relayer ()
                for (int k = start_division; k <= end_division; ++k) {
                        layers[j][k].push_back (*i);
                }
                for (int k = start_division; k <= end_division; ++k) {
                        layers[j][k].push_back (*i);
                }
-               
+
                if ((*i)->layer() != j) {
                        changed = true;
                }
                if ((*i)->layer() != j) {
                        changed = true;
                }
@@ -3009,7 +3018,7 @@ bool
 Playlist::has_region_at (framepos_t const p) const
 {
        RegionLock (const_cast<Playlist *> (this));
 Playlist::has_region_at (framepos_t const p) const
 {
        RegionLock (const_cast<Playlist *> (this));
-       
+
        RegionList::const_iterator i = regions.begin ();
        while (i != regions.end() && !(*i)->covers (p)) {
                ++i;
        RegionList::const_iterator i = regions.begin ();
        while (i != regions.end() && !(*i)->covers (p)) {
                ++i;
@@ -3023,12 +3032,12 @@ void
 Playlist::remove_region_by_source (boost::shared_ptr<Source> s)
 {
        RegionLock rl (this);
 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;
        RegionList::iterator i = regions.begin();
        while (i != regions.end()) {
                RegionList::iterator j = i;
                ++j;
-               
+
                if ((*i)->uses_source (s)) {
                        remove_region_internal (*i);
                }
                if ((*i)->uses_source (s)) {
                        remove_region_internal (*i);
                }
@@ -3046,7 +3055,7 @@ framepos_t
 Playlist::find_next_top_layer_position (framepos_t t) const
 {
        RegionLock rlock (const_cast<Playlist *> (this));
 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 ();
        layer_t const top = top_layer ();
 
        RegionList copy = regions.rlist ();
@@ -3064,7 +3073,7 @@ Playlist::find_next_top_layer_position (framepos_t t) const
 boost::shared_ptr<Region>
 Playlist::combine (const RegionList& r)
 {
 boost::shared_ptr<Region>
 Playlist::combine (const RegionList& r)
 {
-       PropertyList plist; 
+       PropertyList plist;
        uint32_t channels = 0;
        uint32_t layer = 0;
        framepos_t earliest_position = max_framepos;
        uint32_t channels = 0;
        uint32_t layer = 0;
        framepos_t earliest_position = max_framepos;
@@ -3112,7 +3121,7 @@ Playlist::combine (const RegionList& r)
                /* make position relative to zero */
 
                pl->add_region (copied_region, original_region->position() - earliest_position);
                /* make position relative to zero */
 
                pl->add_region (copied_region, original_region->position() - earliest_position);
-               
+
                /* use the maximum number of channels for any region */
 
                channels = max (channels, original_region->n_channels());
                /* use the maximum number of channels for any region */
 
                channels = max (channels, original_region->n_channels());
@@ -3134,12 +3143,12 @@ Playlist::combine (const RegionList& r)
 
        SourceList sources;
        pair<framepos_t,framepos_t> extent = pl->get_extent();
 
        SourceList sources;
        pair<framepos_t,framepos_t> extent = pl->get_extent();
-       
+
        for (uint32_t chn = 0; chn < channels; ++chn) {
                sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
        for (uint32_t chn = 0; chn < channels; ++chn) {
                sources.push_back (SourceFactory::createFromPlaylist (_type, _session, pl, id(), parent_name, chn, 0, extent.second, false, false));
-       
+
        }
        }
-       
+
        /* now a new whole-file region using the list of sources */
 
        plist.add (Properties::start, 0);
        /* now a new whole-file region using the list of sources */
 
        plist.add (Properties::start, 0);
@@ -3150,7 +3159,7 @@ Playlist::combine (const RegionList& r)
        boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
 
        /* now the non-whole-file region that we will actually use in the
        boost::shared_ptr<Region> parent_region = RegionFactory::create (sources, plist, true);
 
        /* now the non-whole-file region that we will actually use in the
-        * playlist 
+        * playlist
         */
 
        plist.clear ();
         */
 
        plist.clear ();
@@ -3165,19 +3174,19 @@ Playlist::combine (const RegionList& r)
         */
 
        freeze ();
         */
 
        freeze ();
-       
+
        for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
                remove_region (*i);
        }
 
        /* do type-specific stuff with the originals and the new compound
        for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
                remove_region (*i);
        }
 
        /* do type-specific stuff with the originals and the new compound
-          region 
+          region
        */
        */
-       
+
        post_combine (originals, compound_region);
 
        /* add the new region at the right location */
        post_combine (originals, compound_region);
 
        /* add the new region at the right location */
-       
+
        add_region (compound_region, earliest_position);
 
        _combine_ops++;
        add_region (compound_region, earliest_position);
 
        _combine_ops++;
@@ -3193,9 +3202,10 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
        boost::shared_ptr<PlaylistSource> pls;
        boost::shared_ptr<const Playlist> pl;
        vector<boost::shared_ptr<Region> > originals;
        boost::shared_ptr<PlaylistSource> pls;
        boost::shared_ptr<const Playlist> pl;
        vector<boost::shared_ptr<Region> > originals;
+       vector<TwoRegions> old_and_new_regions;
 
        // (1) check that its really a compound region
 
        // (1) check that its really a compound region
-       
+
        if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
                return;
        }
        if ((pls = boost::dynamic_pointer_cast<PlaylistSource>(target->source (0))) == 0) {
                return;
        }
@@ -3208,23 +3218,23 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
        /* the leftmost (earliest) edge of the compound region
           starts at zero in its source, or larger if it
           has been trimmed or content-scrolled.
        /* the leftmost (earliest) edge of the compound region
           starts at zero in its source, or larger if it
           has been trimmed or content-scrolled.
-          
+
           the rightmost (latest) edge of the compound region
           relative to its source is the starting point plus
           the length of the region.
        */
           the rightmost (latest) edge of the compound region
           relative to its source is the starting point plus
           the length of the region.
        */
-       
+
        // (2) get all the original regions
 
        const RegionList& rl (pl->region_list().rlist());
        RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
        frameoffset_t move_offset = 0;
        // (2) get all the original regions
 
        const RegionList& rl (pl->region_list().rlist());
        RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
        frameoffset_t move_offset = 0;
-       
+
        /* there are two possibilities here:
           1) the playlist that the playlist source was based on
           is us, so just add the originals (which belonged to
           us anyway) back in the right place.
        /* there are two possibilities here:
           1) the playlist that the playlist source was based on
           is us, so just add the originals (which belonged to
           us anyway) back in the right place.
-          
+
           2) the playlist that the playlist source was based on
           is NOT us, so we need to make copies of each of
           the original regions that we find, and add them
           2) the playlist that the playlist source was based on
           is NOT us, so we need to make copies of each of
           the original regions that we find, and add them
@@ -3271,7 +3281,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
 
                switch (original->coverage (adjusted_start, adjusted_end)) {
                case OverlapNone:
 
                switch (original->coverage (adjusted_start, adjusted_end)) {
                case OverlapNone:
-                       /* original region does not cover any part 
+                       /* original region does not cover any part
                           of the current state of the compound region
                        */
                        continue;
                           of the current state of the compound region
                        */
                        continue;
@@ -3283,7 +3293,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                        original->trim_to (adjusted_start, adjusted_end - adjusted_start);
                        modified_region = true;
                        break;
                        original->trim_to (adjusted_start, adjusted_end - adjusted_start);
                        modified_region = true;
                        break;
-                               
+
                case OverlapExternal:
                        /* overlap fully covers original, so leave it
                           as is
                case OverlapExternal:
                        /* overlap fully covers original, so leave it
                           as is
@@ -3297,7 +3307,7 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                        original->trim_front (adjusted_start);
                        modified_region = true;
                        break;
                        original->trim_front (adjusted_start);
                        modified_region = true;
                        break;
-                               
+
                case OverlapStart:
                        /* overlap covers start but ends within, so
                         * trim the end of the region.
                case OverlapStart:
                        /* overlap covers start but ends within, so
                         * trim the end of the region.
@@ -3319,10 +3329,11 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                }
 
                /* and add to the list of regions waiting to be
                }
 
                /* and add to the list of regions waiting to be
-                * re-inserted 
+                * re-inserted
                 */
 
                originals.push_back (original);
                 */
 
                originals.push_back (original);
+               old_and_new_regions.push_back (TwoRegions (*i, original));
        }
 
        pre_uncombine (originals, target);
        }
 
        pre_uncombine (originals, target);
@@ -3340,6 +3351,10 @@ Playlist::uncombine (boost::shared_ptr<Region> target)
                add_region ((*i), (*i)->position());
        }
 
                add_region ((*i), (*i)->position());
        }
 
+       /* now move dependent regions back from the compound to this playlist */
+
+       pl->copy_dependents (old_and_new_regions, this);
+
        in_partition = false;
        thaw ();
 }
        in_partition = false;
        thaw ();
 }