enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / ardour / session_playlists.cc
index 362d812c36cbbd4551d8331a270e960b97e7a874..95982b6158932bfc0f0c842f7af7d79cebc2ebef 100644 (file)
@@ -1,12 +1,31 @@
-#include "pbd/xml++.h"
-#include "pbd/compose.h"
+/*
+    Copyright (C) 2009 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#include <vector>
+
 #include "ardour/debug.h"
-#include "ardour/session_playlists.h"
 #include "ardour/playlist.h"
-#include "ardour/region.h"
 #include "ardour/playlist_factory.h"
-#include "ardour/session.h"
-#include "i18n.h"
+#include "ardour/session_playlists.h"
+#include "ardour/track.h"
+#include "pbd/i18n.h"
+#include "pbd/compose.h"
+#include "pbd/xml++.h"
 
 using namespace std;
 using namespace PBD;
@@ -15,7 +34,7 @@ using namespace ARDOUR;
 SessionPlaylists::~SessionPlaylists ()
 {
        DEBUG_TRACE (DEBUG::Destruction, "delete playlists\n");
-       
+
        for (List::iterator i = playlists.begin(); i != playlists.end(); ) {
                SessionPlaylists::List::iterator tmp;
 
@@ -23,6 +42,7 @@ SessionPlaylists::~SessionPlaylists ()
                ++tmp;
 
                DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
+               boost::shared_ptr<Playlist> keeper (*i);
                (*i)->drop_references ();
 
                i = tmp;
@@ -36,6 +56,7 @@ SessionPlaylists::~SessionPlaylists ()
                ++tmp;
 
                DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
+               boost::shared_ptr<Playlist> keeper (*i);
                (*i)->drop_references ();
 
                i = tmp;
@@ -48,22 +69,34 @@ SessionPlaylists::~SessionPlaylists ()
 bool
 SessionPlaylists::add (boost::shared_ptr<Playlist> playlist)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
 
        bool const existing = find (playlists.begin(), playlists.end(), playlist) != playlists.end();
 
        if (!existing) {
                playlists.insert (playlists.begin(), playlist);
-               playlist->InUse.connect (sigc::bind (mem_fun (*this, &SessionPlaylists::track), boost::weak_ptr<Playlist>(playlist)));
+               playlist->InUse.connect_same_thread (*this, boost::bind (&SessionPlaylists::track, this, _1, boost::weak_ptr<Playlist>(playlist)));
+               playlist->DropReferences.connect_same_thread (
+                       *this, boost::bind (&SessionPlaylists::remove_weak, this, boost::weak_ptr<Playlist> (playlist))
+                       );
        }
 
        return existing;
 }
 
+void
+SessionPlaylists::remove_weak (boost::weak_ptr<Playlist> playlist)
+{
+       boost::shared_ptr<Playlist> p = playlist.lock ();
+       if (p) {
+               remove (p);
+       }
+}
+
 void
 SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
 
        List::iterator i;
 
@@ -77,7 +110,7 @@ SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
                unused_playlists.erase (i);
        }
 }
-       
+
 
 void
 SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
@@ -96,7 +129,7 @@ SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
        }
 
        {
-               Glib::Mutex::Lock lm (lock);
+               Glib::Threads::Mutex::Lock lm (lock);
 
                if (!inuse) {
 
@@ -121,21 +154,21 @@ SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
 uint32_t
 SessionPlaylists::n_playlists () const
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
        return playlists.size();
 }
 
 boost::shared_ptr<Playlist>
 SessionPlaylists::by_name (string name)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
 
        for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
                if ((*i)->name() == name) {
                        return* i;
                }
        }
-       
+
        for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
                if ((*i)->name() == name) {
                        return* i;
@@ -145,38 +178,72 @@ SessionPlaylists::by_name (string name)
        return boost::shared_ptr<Playlist>();
 }
 
+boost::shared_ptr<Playlist>
+SessionPlaylists::by_id (const PBD::ID& id)
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+
+       for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+               if ((*i)->id() == id) {
+                       return* i;
+               }
+       }
+
+       for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+               if ((*i)->id() == id) {
+                       return* i;
+               }
+       }
+
+       return boost::shared_ptr<Playlist>();
+}
+
 void
 SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & list)
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
 
        for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
-               if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
+               if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
                        list.push_back (*i);
                }
        }
-       
+
        for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
-               if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
+               if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
                        list.push_back (*i);
                }
        }
 }
 
 void
-SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s)
+SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s) const
 {
-       Glib::Mutex::Lock lm (lock);
+       Glib::Threads::Mutex::Lock lm (lock);
 
-       for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+       for (List::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
                s.push_back (*i);
        }
-       
-       for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+
+       for (List::const_iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
                s.push_back (*i);
        }
 }
 
+void
+SessionPlaylists::destroy_region (boost::shared_ptr<Region> r)
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+
+       for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+                (*i)->destroy_region (r);
+       }
+
+       for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+                (*i)->destroy_region (r);
+       }
+}
+
 void
 SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
 {
@@ -184,23 +251,44 @@ SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> re
                (*i)->get_region_list_equivalent_regions (region, result);
 }
 
-/** Return the number of playlists (not regions) that contain @a src */
+/** Return the number of playlists (not regions) that contain @a src
+ *  Important: this counts usage in both used and not-used playlists.
+ */
 uint32_t
 SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
 {
        uint32_t count = 0;
+
+       /* XXXX this can go wildly wrong in the presence of circular references
+        * between compound regions.
+        */
+
        for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
-               for (Playlist::RegionList::const_iterator r = (*p)->region_list().begin();
-                               r != (*p)->region_list().end(); ++r) {
-                       if ((*r)->uses_source(src)) {
-                               ++count;
-                               break;
-                       }
-               }
+                if ((*p)->uses_source (src)) {
+                        ++count;
+                        break;
+                }
        }
+
+       for (List::const_iterator p = unused_playlists.begin(); p != unused_playlists.end(); ++p) {
+                if ((*p)->uses_source (src)) {
+                        ++count;
+                        break;
+                }
+       }
+
        return count;
 }
 
+void
+SessionPlaylists::sync_all_regions_with_regions ()
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+
+       for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
+                (*p)->sync_all_regions_with_regions ();
+        }
+}
 
 void
 SessionPlaylists::update_after_tempo_map_change ()
@@ -220,14 +308,12 @@ SessionPlaylists::add_state (XMLNode* node, bool full_state)
        XMLNode* child = node->add_child ("Playlists");
        for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
                if (!(*i)->hidden()) {
-                       if (!(*i)->empty()) {
-                               if (full_state) {
-                                       child->add_child_nocopy ((*i)->get_state());
-                               } else {
-                                       child->add_child_nocopy ((*i)->get_template());
-                               }
-                       }
-               }
+                        if (full_state) {
+                                child->add_child_nocopy ((*i)->get_state());
+                        } else {
+                                child->add_child_nocopy ((*i)->get_template());
+                        }
+                }
        }
 
        child = node->add_child ("UnusedPlaylists");
@@ -246,19 +332,43 @@ SessionPlaylists::add_state (XMLNode* node, bool full_state)
 
 /** @return true for `stop cleanup', otherwise false */
 bool
-SessionPlaylists::maybe_delete_unused (sigc::signal<int, boost::shared_ptr<Playlist> > ask)
+SessionPlaylists::maybe_delete_unused (boost::function<int(boost::shared_ptr<Playlist>)> ask)
 {
        vector<boost::shared_ptr<Playlist> > playlists_tbd;
 
+       bool delete_remaining = false;
+       bool keep_remaining = false;
+
        for (List::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
 
+               if (keep_remaining) {
+                       break;
+               }
+
+               if (delete_remaining) {
+                       playlists_tbd.push_back (*x);
+                       continue;
+               }
+
                int status = ask (*x);
 
                switch (status) {
                case -1:
+                       // abort
                        return true;
 
-               case 0:
+               case -2:
+                       // keep this and all later
+                       keep_remaining = true;
+                       break;
+
+               case 2:
+                       // delete this and all later
+                       delete_remaining = true;
+                       // no break;
+
+               case 1:
+                       // delete this
                        playlists_tbd.push_back (*x);
                        break;
 
@@ -271,6 +381,7 @@ SessionPlaylists::maybe_delete_unused (sigc::signal<int, boost::shared_ptr<Playl
        /* now delete any that were marked for deletion */
 
        for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
+               boost::shared_ptr<Playlist> keeper (*x);
                (*x)->drop_references ();
        }
 
@@ -334,3 +445,77 @@ SessionPlaylists::XMLPlaylistFactory (Session& session, const XMLNode& node)
        }
 }
 
+boost::shared_ptr<Crossfade>
+SessionPlaylists::find_crossfade (const PBD::ID& id)
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+
+       boost::shared_ptr<Crossfade> c;
+
+       for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+               c = (*i)->find_crossfade (id);
+               if (c) {
+                       return c;
+               }
+       }
+
+       for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+               c = (*i)->find_crossfade (id);
+               if (c) {
+                       return c;
+               }
+       }
+
+       return boost::shared_ptr<Crossfade> ();
+}
+
+uint32_t
+SessionPlaylists::region_use_count (boost::shared_ptr<Region> region) const
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+        uint32_t cnt = 0;
+
+       for (List::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
+                cnt += (*i)->region_use_count (region);
+       }
+
+       for (List::const_iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+                cnt += (*i)->region_use_count (region);
+       }
+
+       return cnt;
+}
+
+/** @return list of Playlists that are associated with a track */
+vector<boost::shared_ptr<Playlist> >
+SessionPlaylists::playlists_for_track (boost::shared_ptr<Track> tr) const
+{
+       vector<boost::shared_ptr<Playlist> > pl;
+       get (pl);
+
+       vector<boost::shared_ptr<Playlist> > pl_tr;
+
+       for (vector<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
+               if (((*i)->get_orig_track_id() == tr->id()) || (tr->playlist()->id() == (*i)->id())) {
+                       pl_tr.push_back (*i);
+               }
+       }
+
+       return pl_tr;
+}
+
+void
+SessionPlaylists::foreach (boost::function<void(boost::shared_ptr<const Playlist>)> functor)
+{
+       Glib::Threads::Mutex::Lock lm (lock);
+       for (List::iterator i = playlists.begin(); i != playlists.end(); i++) {
+               if (!(*i)->hidden()) {
+                       functor (*i);
+               }
+       }
+       for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); i++) {
+               if (!(*i)->hidden()) {
+                       functor (*i);
+               }
+       }
+}