commits 6001-6525 inclusive from 2.X applied to 3.0
[ardour.git] / libs / ardour / playlist.cc
index 7e6371a366e64680819368f2742a62fb679e3b92..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"
@@ -113,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;
 }
 
@@ -133,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);
         }
 }
 
@@ -160,6 +154,7 @@ Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
        init (hide);
        first_set_state = false;
        _name = nom;
+        _set_sort_id ();
 
 }
 
@@ -174,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 */
 }
@@ -270,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());
@@ -337,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;
@@ -368,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)
 {
@@ -379,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;
 }
 
 /***********************************************************************
@@ -395,6 +422,7 @@ Playlist::set_name (const string& str)
 void
 Playlist::begin_undo ()
 {
+        in_update = true;
        freeze ();
 }
 
@@ -402,6 +430,7 @@ void
 Playlist::end_undo ()
 {
        thaw ();
+        in_update = false;
 }
 
 void
@@ -432,6 +461,7 @@ Playlist::release_notifications ()
        if (g_atomic_int_dec_and_test (&block_notifications)) {
                flush_notifications ();
         }
+
 }
 
 void
@@ -499,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;
@@ -665,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());
@@ -724,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 ();
        }
 
@@ -799,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()) {
@@ -935,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());
@@ -954,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());
@@ -974,7 +1004,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapEnd) {
 
@@ -992,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());
@@ -1013,7 +1043,7 @@ Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, Re
 
                                current->suspend_property_changes ();
                                thawlist.push_back (current);
-                               current->trim_end (pos2, this);
+                               current->cut_end (pos2 - 1, this);
 
                        } else if (overlap == OverlapStart) {
 
@@ -1035,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());
@@ -1239,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);
                        
@@ -1340,7 +1370,7 @@ 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;
@@ -1353,7 +1383,7 @@ 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;
@@ -1600,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;
@@ -2020,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)
 {
@@ -2035,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 ();
 }
 
@@ -2046,8 +2110,6 @@ 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() == capitalize (regions.property_name())) {
                         
                         RegionListProperty* rlp = new RegionListProperty (*const_cast<Playlist*> (this));
@@ -2095,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")) {
@@ -2104,7 +2167,7 @@ Playlist::set_state (const XMLNode& node, int version)
                }
        }
 
-       clear (false);
+       clear (true);
 
        nlist = node.children();
 
@@ -2136,7 +2199,7 @@ Playlist::set_state (const XMLNode& node, int version)
                                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
@@ -2153,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 ();
 
@@ -2279,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
@@ -2422,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
@@ -2620,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);
 }