commits 6001-6525 inclusive from 2.X applied to 3.0
[ardour.git] / libs / ardour / playlist.cc
index 1aef4b8d922620b55271e5202d1974d0b7cb7258..6fc7fdc2cffa9d92020485a07aa77d672b850647 100644 (file)
 #include <string>
 #include <climits>
 
+#include <boost/lexical_cast.hpp>
 
 #include "pbd/failed_constructor.h"
-#include "pbd/stl_delete.h"
+#include "pbd/stateful_diff_command.h"
 #include "pbd/xml++.h"
-#include "pbd/stacktrace.h"
 
 #include "ardour/debug.h"
 #include "ardour/playlist.h"
@@ -114,13 +114,10 @@ RegionListProperty::lookup_id (const ID& id)
 {
         boost::shared_ptr<Region> ret =  _playlist.region_by_id (id);
         
-        if (!ret) {
-                ret = _playlist.session().region_by_id (id);
-        }
-
         if (!ret) {
                 ret = RegionFactory::region_by_id (id);
         }
+
         return ret;
 }
 
@@ -134,22 +131,18 @@ RegionListProperty::copy_for_history () const
 }
 
 void 
-RegionListProperty::diff (PropertyList& before, PropertyList& after) const
+RegionListProperty::diff (PropertyList& undo, PropertyList& redo) const
 {
-        if (_have_old) {
+        if (changed()) {
+               /* list of the removed/added regions since clear_history() was last called */
                 RegionListProperty* a = copy_for_history ();
-                RegionListProperty* b = copy_for_history ();
 
+               /* the same list, but with removed/added lists swapped (for undo purposes) */
+                RegionListProperty* b = copy_for_history ();
                 b->invert_changes ();
 
-                before.add (b);
-                after.add (a);
-
-                cerr << "pdiff on " << _playlist.name() << " before contains "
-                     << b->change().added.size() << " adds and " << b->change().removed.size() << " removes\n";
-                cerr << "pdiff on " << _playlist.name() << " after contains "
-                     << a->change().added.size() << " adds and " << a->change().removed.size() << " removes\n";
-
+                undo.add (b);
+                redo.add (a);
         }
 }
 
@@ -161,6 +154,7 @@ Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
        init (hide);
        first_set_state = false;
        _name = nom;
+        _set_sort_id ();
 
 }
 
@@ -175,6 +169,7 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide
 
        init (hide);
        _name = "unnamed"; /* reset by set_state */
+        _set_sort_id ();
 
        /* set state called by derived class */
 }
@@ -271,11 +266,11 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
                        break;
                }
 
-               _session.region_name (new_name, region->name(), false);
+               RegionFactory::region_name (new_name, region->name(), false);
 
                PropertyList plist; 
 
-               plist.add (Properties::start, offset);
+               plist.add (Properties::start, region->start() + offset);
                plist.add (Properties::length, len);
                plist.add (Properties::name, new_name);
                plist.add (Properties::layer, region->layer());
@@ -338,6 +333,7 @@ Playlist::init (bool hide)
        _shuffling = false;
        _nudging = false;
        in_set_state = 0;
+        in_update = false;
        _edit_mode = Config->get_edit_mode();
        in_flush = false;
        in_partition = false;
@@ -369,6 +365,32 @@ Playlist::~Playlist ()
        /* GoingAway must be emitted by derived classes */
 }
 
+void
+Playlist::_set_sort_id ()
+{
+        /*
+          Playlists are given names like <track name>.<id>
+          or <track name>.<edit group name>.<id> where id
+          is an integer. We extract the id and sort by that.
+        */
+        
+        size_t dot_position = _name.val().find_last_of(".");
+
+        if (dot_position == string::npos) {
+                _sort_id = 0;
+        } else {
+                string t = _name.val().substr(dot_position + 1);
+
+                try {
+                        _sort_id = boost::lexical_cast<int>(t);
+                }
+
+                catch (boost::bad_lexical_cast e) {
+                        _sort_id = 0;
+                }
+        }
+}
+
 bool
 Playlist::set_name (const string& str)
 {
@@ -380,9 +402,13 @@ Playlist::set_name (const string& str)
 
        if (_refcnt > 2) {
                return false;
-       } else {
-               return SessionObject::set_name(str);
-       }
+       } 
+
+        bool ret =  SessionObject::set_name(str);
+        if (ret) {
+                _set_sort_id ();
+        }
+        return ret;
 }
 
 /***********************************************************************
@@ -396,6 +422,7 @@ Playlist::set_name (const string& str)
 void
 Playlist::begin_undo ()
 {
+        in_update = true;
        freeze ();
 }
 
@@ -403,6 +430,7 @@ void
 Playlist::end_undo ()
 {
        thaw ();
+        in_update = false;
 }
 
 void
@@ -433,6 +461,7 @@ Playlist::release_notifications ()
        if (g_atomic_int_dec_and_test (&block_notifications)) {
                flush_notifications ();
         }
+
 }
 
 void
@@ -500,7 +529,7 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
        /* the length change might not be true, but we have to act
           as though it could be.
        */
-
+        
        if (holding_state()) {
                pending_adds.insert (r);
                pending_contents_change = true;
@@ -666,12 +695,12 @@ Playlist::add_region (boost::shared_ptr<Region> region, framepos_t position, flo
        if (floor (times) != times) {
                length = (framecnt_t) floor (region->length() * (times - floor (times)));
                string name;
-               _session.region_name (name, region->name(), false);
+               RegionFactory::region_name (name, region->name(), false);
 
                {
                        PropertyList plist;
                        
-                       plist.add (Properties::start, 0);
+                       plist.add (Properties::start, region->start());
                        plist.add (Properties::length, length);
                        plist.add (Properties::name, name);
                        plist.add (Properties::layer, region->layer());
@@ -725,8 +754,8 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, framepos_t posi
 
        possibly_splice_unlocked (position, region->length(), region);
 
-       if (!holding_state () && !in_set_state) {
-               /* layers get assigned from XML state */
+       if (!holding_state ()) {
+               /* layers get assigned from XML state, and are not reset during undo/redo */
                relayer ();
        }
 
@@ -800,7 +829,7 @@ Playlist::remove_region_internal (boost::shared_ptr<Region> region)
                        possibly_splice_unlocked (pos, -distance);
 
                        if (!holding_state ()) {
-                               relayer ();
+                                relayer ();
                                remove_dependents (region);
 
                                if (old_length != _get_maximum_extent()) {
@@ -856,7 +885,7 @@ Playlist::partition (framepos_t start, framepos_t end, bool cut)
        partition_internal (start, end, cut, thawlist);
 
        for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
-               (*i)->thaw ();
+               (*i)->resume_property_changes ();
        }
 }
 
@@ -936,11 +965,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
                                if (!cutting) {
                                        /* "middle" ++++++ */
 
-                                       _session.region_name (new_name, current->name(), false);
+                                       RegionFactory::region_name (new_name, current->name(), false);
 
                                        PropertyList plist;
                                        
-                                       plist.add (Properties::start, pos2 - pos1);
+                                       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());
@@ -955,11 +984,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                /* "end" ====== */
 
-                               _session.region_name (new_name, current->name(), false);
+                               RegionFactory::region_name (new_name, current->name(), false);
 
                                PropertyList plist;
                                
-                               plist.add (Properties::start, pos3 - pos1);
+                               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());
@@ -973,9 +1002,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                /* "front" ***** */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapEnd) {
 
@@ -993,11 +1022,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                        /* end +++++ */
 
-                                       _session.region_name (new_name, current->name(), false);
+                                       RegionFactory::region_name (new_name, current->name(), false);
                                        
                                        PropertyList plist;
                                        
-                                       plist.add (Properties::start, pos2 - pos1);
+                                       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());
@@ -1012,9 +1041,9 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                /* front ****** */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapStart) {
 
@@ -1036,11 +1065,11 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                if (!cutting) {
                                        /* front **** */
-                                       _session.region_name (new_name, current->name(), false);
+                                       RegionFactory::region_name (new_name, current->name(), false);
 
                                        PropertyList plist;
                                        
-                                       plist.add (Properties::start, 0);
+                                       plist.add (Properties::start, current->start());
                                        plist.add (Properties::length, pos3 - pos1);
                                        plist.add (Properties::name, new_name);
                                        plist.add (Properties::layer, regions.size());
@@ -1055,7 +1084,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                /* end */
 
-                               current->freeze ();
+                               current->suspend_property_changes ();
                                thawlist.push_back (current);
                                current->trim_front (pos3, this);
                        } else if (overlap == OverlapExternal) {
@@ -1158,7 +1187,7 @@ Playlist::cut (framepos_t start, framecnt_t cnt, bool result_is_hidden)
        partition_internal (start, start+cnt-1, true, thawlist);
 
        for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
-               (*i)->thaw ();
+               (*i)->resume_property_changes();
        }
 
        return the_copy;
@@ -1240,12 +1269,12 @@ Playlist::duplicate (boost::shared_ptr<Region> region, framepos_t position, floa
        if (floor (times) != times) {
                framecnt_t length = (framecnt_t) floor (region->length() * (times - floor (times)));
                string name;
-               _session.region_name (name, region->name(), false);
+               RegionFactory::region_name (name, region->name(), false);
                
                {
                        PropertyList plist;
                        
-                       plist.add (Properties::start, 0);
+                       plist.add (Properties::start, region->start());
                        plist.add (Properties::length, length);
                        plist.add (Properties::name, name);
                        
@@ -1341,12 +1370,12 @@ Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_p
        before = playlist_position - region->position();
        after = region->length() - before;
 
-       _session.region_name (before_name, region->name(), false);
+       RegionFactory::region_name (before_name, region->name(), false);
 
        {
                PropertyList plist;
                
-               plist.add (Properties::start, 0);
+               plist.add (Properties::start, region->start());
                plist.add (Properties::length, before);
                plist.add (Properties::name, before_name);
                plist.add (Properties::left_of_split, true);
@@ -1354,12 +1383,12 @@ Playlist::_split_region (boost::shared_ptr<Region> region, framepos_t playlist_p
                left = RegionFactory::create (region, plist);
        }
 
-       _session.region_name (after_name, region->name(), false);
+       RegionFactory::region_name (after_name, region->name(), false);
 
        {
                PropertyList plist;
                
-               plist.add (Properties::start, before);
+               plist.add (Properties::start, region->start() + before);
                plist.add (Properties::length, after);
                plist.add (Properties::name, after_name);
                plist.add (Properties::right_of_split, true);
@@ -1601,9 +1630,19 @@ Playlist::clear (bool with_signals)
                }
 
                regions.clear ();
+
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        remove_dependents (*s);
+                }
        }
 
        if (with_signals) {
+
+                for (set<boost::shared_ptr<Region> >::iterator s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+                        RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
+                }
+
+                pending_removes.clear ();
                pending_length = false;
                LengthChanged ();
                pending_contents_change = false;
@@ -2021,6 +2060,29 @@ Playlist::set_property (const PropertyBase& prop)
         return false;
 }
 
+void
+Playlist::rdiff (vector<StatefulDiffCommand*>& cmds) const
+{
+       RegionLock rlock (const_cast<Playlist *> (this));
+
+       for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+               if ((*i)->changed ()) {
+                        StatefulDiffCommand* sdc = new StatefulDiffCommand (*i);
+                        cmds.push_back (sdc);
+                }
+       }
+}
+
+void
+Playlist::clear_owned_history ()
+{
+       RegionLock rlock (this);
+
+       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                (*i)->clear_history ();
+        }
+}
+
 void
 Playlist::update (const RegionListProperty::ChangeRecord& change)
 {
@@ -2036,6 +2098,7 @@ Playlist::update (const RegionListProperty::ChangeRecord& change)
         for (RegionListProperty::ChangeContainer::iterator i = change.removed.begin(); i != change.removed.end(); ++i) {
                 remove_region (*i);
         }
+
         thaw ();
 }
 
@@ -2047,9 +2110,7 @@ Playlist::property_factory (const XMLNode& history_node) const
 
         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
 
-                /* XXX property name needs capitalizing */
-
-                if ((*i)->name() == regions.property_name()) {
+                if ((*i)->name() == capitalize (regions.property_name())) {
                         
                         RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
 
@@ -2096,6 +2157,7 @@ Playlist::set_state (const XMLNode& node, int version)
 
                if (prop->name() == X_("name")) {
                        _name = prop->value();
+                        _set_sort_id ();
                } else if (prop->name() == X_("id")) {
                         _id = prop->value();
                } else if (prop->name() == X_("orig_diskstream_id")) {
@@ -2105,7 +2167,7 @@ Playlist::set_state (const XMLNode& node, int version)
                }
        }
 
-       clear (false);
+       clear (true);
 
        nlist = node.children();
 
@@ -2124,25 +2186,25 @@ Playlist::set_state (const XMLNode& node, int version)
 
                        if ((region = region_by_id (id))) {
 
-                               region->freeze ();
+                               region->suspend_property_changes ();
 
                                if (region->set_state (*child, version)) {
-                                       region->thaw ();
+                                       region->resume_property_changes ();
                                        continue;
                                }
                                
                        } else if ((region = RegionFactory::create (_session, *child, true)) != 0) {
-                               region->freeze ();
+                               region->suspend_property_changes ();
                        } else {
                                error << _("Playlist: cannot create region from XML") << endmsg;
                                continue;
                        }
-
+                        
                        add_region (region, region->position(), 1.0);
 
                        // So that layer_op ordering doesn't get screwed up
                        region->set_last_layer_op( region->layer());
-                       region->thaw ();
+                       region->resume_property_changes ();
                }
        }
 
@@ -2154,7 +2216,6 @@ Playlist::set_state (const XMLNode& node, int version)
                check_dependents (*r, false);
        }
 
-       clear_pending (); // this makes thaw() do nothing
        thaw ();
        notify_contents_changed ();
 
@@ -2280,6 +2341,12 @@ Playlist::set_edit_mode (EditMode mode)
 void
 Playlist::relayer ()
 {
+        /* never compute layers when changing state for undo/redo or setting from XML */
+
+        if (in_update || in_set_state) {
+                return;
+        }
+
        bool changed = false;
 
        /* Build up a new list of regions on each layer, stored in a set of lists
@@ -2423,22 +2490,48 @@ void
 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
 {
        /* does nothing useful if layering mode is later=higher */
-       if ((_session.config.get_layer_model() == MoveAddHigher) ||
-           (_session.config.get_layer_model() == AddHigher)) {
-               timestamp_layer_op (region);
-               relayer ();
+       switch (_session.config.get_layer_model()) {
+       case LaterHigher:
+               return;
+       default:
+               break;
        }
+
+       layer_t top = regions.size() - 1;
+
+       if (region->layer() >= top) {
+               /* already on the top */
+               return;
+       }
+
+       move_region_to_layer (top, region, 1);
+       /* mark the region's last_layer_op as now, so that it remains on top when
+          doing future relayers (until something else takes over)
+        */
+       timestamp_layer_op (region);
 }
 
 void
 Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
 {
        /* does nothing useful if layering mode is later=higher */
-       if ((_session.config.get_layer_model() == MoveAddHigher) ||
-           (_session.config.get_layer_model() == AddHigher)) {
-               region->set_last_layer_op (0);
-               relayer ();
+       switch (_session.config.get_layer_model()) {
+       case LaterHigher:
+               return;
+       default:
+               break;
        }
+
+       if (region->layer() == 0) {
+               /* already on the bottom */
+               return;
+       }
+
+       move_region_to_layer (0, region, -1);
+       /* force region's last layer op to zero so that it stays at the bottom
+          when doing future relayers
+       */
+       region->set_last_layer_op (0);
 }
 
 int
@@ -2621,8 +2714,6 @@ Playlist::set_frozen (bool yn)
 void
 Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
 {
-//     struct timeval tv;
-//     gettimeofday (&tv, 0);
        region->set_last_layer_op (++layer_op_counter);
 }