slightly improved fixes for MIDI issues
[ardour.git] / libs / ardour / playlist.cc
index 2becdc19b42b2ff4b083da571f17c6a0fb38ffb7..4af7e2b907febb489496a3199acaf9e9e8cf3c81 100644 (file)
@@ -15,7 +15,6 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
-    $Id$
 */
 
 #include <set>
@@ -72,16 +71,17 @@ struct RegionSortByLastLayerOp {
 };
 
 Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
-       : _session (sess)
+       : SessionObject(sess, nom)
        , _type(type)
 {
        init (hide);
+       first_set_state = false;
        _name = nom;
        
 }
 
 Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
-       : _session (sess)
+       : SessionObject(sess, "unnamed playlist")
        , _type(type)
 {
        const XMLProperty* prop = node.property("type");
@@ -94,7 +94,7 @@ Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide
 }
 
 Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
-       : _name (namestr), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+       : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
 {
        init (hide);
 
@@ -114,6 +114,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
        _edit_mode = other->_edit_mode;
 
        in_set_state = 0;
+       first_set_state = false;
        in_flush = false;
        in_partition = false;
        subcnt = 0;
@@ -125,7 +126,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
 }
 
 Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
-       : _name (str), _session (other->_session), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+       : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
 {
        RegionLock rlock2 (const_cast<Playlist*> (other.get()));
 
@@ -186,6 +187,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nf
        }
        
        in_set_state--;
+       first_set_state = false;
 
        /* this constructor does NOT notify others (session) */
 }
@@ -209,7 +211,6 @@ Playlist::release ()
        }
 }
 
-
 void
 Playlist::copy_regions (RegionList& newlist) const
 {
@@ -227,6 +228,7 @@ Playlist::init (bool hide)
        g_atomic_int_set (&ignore_state_changes, 0);
        pending_modified = false;
        pending_length = false;
+       first_set_state = true;
        _refcnt = 0;
        _hidden = hide;
        _splicing = false;
@@ -245,14 +247,14 @@ Playlist::init (bool hide)
 }
 
 Playlist::Playlist (const Playlist& pl)
-       : _session (pl._session)
+       : SessionObject(pl._session, pl._name)
        , _type(pl.data_type())
 {
        fatal << _("playlist const copy constructor called") << endmsg;
 }
 
 Playlist::Playlist (Playlist& pl)
-       : _session (pl._session)
+       : SessionObject(pl._session, pl._name)
        , _type(pl.data_type())
 {
        fatal << _("playlist non-const copy constructor called") << endmsg;
@@ -271,8 +273,8 @@ Playlist::~Playlist ()
        /* GoingAway must be emitted by derived classes */
 }
 
-void
-Playlist::set_name (string str)
+bool
+Playlist::set_name (const string& str)
 {
        /* in a typical situation, a playlist is being used
           by one diskstream and also is referenced by the
@@ -281,11 +283,10 @@ Playlist::set_name (string str)
        */
 
        if (_refcnt > 2) {
-               return;
+               return false;
+       } else {
+               return SessionObject::set_name(str);
        }
-
-       _name = str; 
-       NameChanged(); /* EMIT SIGNAL */
 }
 
 /***********************************************************************
@@ -517,10 +518,10 @@ Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t posit
                 old_length = _get_maximum_extent();
        }
 
-       if (!in_set_state) {
+       if (!first_set_state) {
                boost::shared_ptr<Playlist> foo (shared_from_this());
                region->set_playlist (boost::weak_ptr<Playlist>(foo));
-       }
+       } 
 
        region->set_position (position, this);
 
@@ -583,6 +584,11 @@ Playlist::remove_region_internal (boost::shared_ptr<Region>region)
                old_length = _get_maximum_extent();
        }
 
+       if (!in_set_state) {
+               /* unset playlist */
+               region->set_playlist (boost::weak_ptr<Playlist>());
+       }
+
        for (i = regions.begin(); i != regions.end(); ++i) {
                if (*i == region) {
 
@@ -607,11 +613,15 @@ Playlist::remove_region_internal (boost::shared_ptr<Region>region)
 void
 Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
 {
-       for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-               if (Config->get_use_overlap_equivalency()) {
+       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 if ((*i)->equivalent (other)) {
+                       }
+               }
+       } else {
+               for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+                       if ((*i)->equivalent (other)) {
                                results.push_back ((*i));
                        }
                }
@@ -1179,6 +1189,14 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
        return save;
 }
 
+void
+Playlist::drop_regions ()
+{
+       RegionLock rl (this);
+       regions.clear ();
+       all_regions.clear ();
+}
+
 void
 Playlist::clear (bool with_signals)
 {
@@ -1410,7 +1428,7 @@ Playlist::set_state (const XMLNode& node)
        }
 
        in_set_state--;
-
+       first_set_state = false;
        return 0;
 }
 
@@ -1441,12 +1459,7 @@ Playlist::state (bool full_state)
 
        if (full_state) {
                RegionLock rlock (this, false);
-
-               cerr << _name << " getting region state for " << regions.size() << endl;
-
                for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
-                       cerr << "\t" << " now at " << (*i) << endl;
-                       cerr << "\t\t" << (*i)->name() << endl;
                        node->add_child_nocopy ((*i)->get_state());
                }
        }
@@ -1514,17 +1527,33 @@ Playlist::bump_name_once (string name)
        string newname;
 
        if ((period = name.find_last_of ('.')) == string::npos) {
-               newname = name;
+               newname  = name;
                newname += ".1";
        } else {
-               char buf[32];
-               int version;
-               
-               sscanf (name.substr (period+1).c_str(), "%d", &version);
-               snprintf (buf, sizeof(buf), "%d", version+1);
+               int isnumber = 1;
+               const char *last_element = name.c_str() + period + 1;
+               for (size_t i = 0; i < strlen(last_element); i++) {
+                       if (!isdigit(last_element[i])) {
+                               isnumber = 0;
+                               break;
+                       }
+               }
+
+               errno = 0;
+               long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
+
+               if (isnumber == 0 || errno != 0) {
+                       // last_element is not a number, or is too large
+                       newname  = name;
+                       newname += ".1";
+               } else {
+                       char buf[32];
+
+                       snprintf (buf, sizeof(buf), "%ld", version+1);
                
-               newname = name.substr (0, period+1);
-               newname += buf;
+                       newname  = name.substr (0, period+1);
+                       newname += buf;
+               }
        }
 
        return newname;
@@ -1555,33 +1584,66 @@ Playlist::set_edit_mode (EditMode mode)
 void
 Playlist::relayer ()
 {
-       RegionList::iterator i;
-       uint32_t layer = 0;
-
        /* don't send multiple Modified notifications
           when multiple regions are relayered.
        */
 
        freeze ();
 
-       if (Config->get_layer_model() == MoveAddHigher || 
-           Config->get_layer_model() == AddHigher) {
+       /* build up a new list of regions on each layer */
 
-               RegionSortByLastLayerOp cmp;
-               RegionList copy = regions;
+       std::vector<RegionList> layers;
+
+       /* we want to go through regions from desired lowest to desired highest layer,
+          which depends on the layer model
+       */
 
+       RegionList copy = regions;
+
+       /* sort according to the model */
+
+       if (Config->get_layer_model() == MoveAddHigher || Config->get_layer_model() == AddHigher) {
+               RegionSortByLastLayerOp cmp;
                copy.sort (cmp);
+       }
+       
+       
+       for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+
+               /* find the lowest layer that this region can go on */
+               size_t j = layers.size();
+               while (j > 0) {
+                       /* try layer j - 1; it can go on if it overlaps no other region
+                          that is already on that layer
+                       */
+                       RegionList::iterator k = layers[j - 1].begin();
+                       while (k != layers[j - 1].end()) {
+                               if ((*k)->overlap_equivalent (*i)) {
+                                       break;
+                               }
+                               k++;
+                       }
 
-               for (i = copy.begin(); i != copy.end(); ++i) {
-                       (*i)->set_layer (layer++);
+                       if (k != layers[j - 1].end()) {
+                               /* no overlap, so we can use this layer */
+                               break;
+                       }
+                                       
+                       j--;
                }
 
-       } else {
-               
-               /* Session::LaterHigher model */
+               if (j == layers.size()) {
+                       /* we need a new layer for this region */
+                       layers.push_back (RegionList ());
+               }
 
-               for (i = regions.begin(); i != regions.end(); ++i) {
-                       (*i)->set_layer (layer++);
+               layers[j].push_back (*i);
+       }
+
+       /* first pass: set up the layer numbers in the regions */
+       for (size_t j = 0; j < layers.size(); ++j) {
+               for (RegionList::iterator i = layers[j].begin(); i != layers[j].end(); ++i) {
+                       (*i)->set_layer (j);
                }
        }
 
@@ -1600,33 +1662,6 @@ Playlist::relayer ()
 
 /* XXX these layer functions are all deprecated */
 
-void
-Playlist::raise_region (boost::shared_ptr<Region> region)
-{
-       uint32_t rsz = regions.size();
-       layer_t target = region->layer() + 1U;
-
-       if (target >= rsz) {
-               /* its already at the effective top */
-               return;
-       }
-
-       move_region_to_layer (target, region, 1);
-}
-
-void
-Playlist::lower_region (boost::shared_ptr<Region> region)
-{
-       if (region->layer() == 0) {
-               /* its already at the bottom */
-               return;
-       }
-
-       layer_t target = region->layer() - 1U;
-
-       move_region_to_layer (target, region, -1);
-}
-
 void
 Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
 {
@@ -1649,77 +1684,6 @@ Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
        }
 }
 
-int
-Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
-{
-       RegionList::iterator i;
-       typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
-       list<LayerInfo> layerinfo;
-       layer_t dest;
-
-       {
-               RegionLock rlock (const_cast<Playlist *> (this));
-               
-               for (i = regions.begin(); i != regions.end(); ++i) {
-                       
-                       if (region == *i) {
-                               continue;
-                       }
-
-                       if (dir > 0) {
-
-                               /* region is moving up, move all regions on intermediate layers
-                                  down 1
-                               */
-                               
-                               if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
-                                       dest = (*i)->layer() - 1;
-                               } else {
-                                       /* not affected */
-                                       continue;
-                               }
-                       } else {
-
-                               /* region is moving down, move all regions on intermediate layers
-                                  up 1
-                               */
-
-                               if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
-                                       dest = (*i)->layer() + 1;
-                               } else {
-                                       /* not affected */
-                                       continue;
-                               }
-                       }
-
-                       LayerInfo newpair;
-                       
-                       newpair.first = *i;
-                       newpair.second = dest;
-                       
-                       layerinfo.push_back (newpair);
-               } 
-       }
-
-       /* now reset the layers without holding the region lock */
-
-       for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
-               x->first->set_layer (x->second);
-       }
-
-       region->set_layer (target_layer);
-
-       /* now check all dependents */
-
-       for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
-               check_dependents (x->first, false);
-       }
-       
-       check_dependents (region, false);
-       
-       return 0;
-}
-
 void
 Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
 {