GPL boilerplate.
[ardour.git] / libs / ardour / session_playlists.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include "pbd/xml++.h"
21 #include "pbd/compose.h"
22 #include "ardour/debug.h"
23 #include "ardour/session_playlists.h"
24 #include "ardour/playlist.h"
25 #include "ardour/region.h"
26 #include "ardour/playlist_factory.h"
27 #include "ardour/session.h"
28 #include "i18n.h"
29
30 using namespace std;
31 using namespace PBD;
32 using namespace ARDOUR;
33
34 SessionPlaylists::~SessionPlaylists ()
35 {
36         DEBUG_TRACE (DEBUG::Destruction, "delete playlists\n");
37         
38         for (List::iterator i = playlists.begin(); i != playlists.end(); ) {
39                 SessionPlaylists::List::iterator tmp;
40
41                 tmp = i;
42                 ++tmp;
43
44                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
45                 (*i)->drop_references ();
46
47                 i = tmp;
48         }
49
50         DEBUG_TRACE (DEBUG::Destruction, "delete unused playlists\n");
51         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) {
52                 List::iterator tmp;
53
54                 tmp = i;
55                 ++tmp;
56
57                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
58                 (*i)->drop_references ();
59
60                 i = tmp;
61         }
62
63         playlists.clear ();
64         unused_playlists.clear ();
65 }
66
67 bool
68 SessionPlaylists::add (boost::shared_ptr<Playlist> playlist)
69 {
70         Glib::Mutex::Lock lm (lock);
71
72         bool const existing = find (playlists.begin(), playlists.end(), playlist) != playlists.end();
73
74         if (!existing) {
75                 playlists.insert (playlists.begin(), playlist);
76                 playlist->InUse.connect (sigc::bind (mem_fun (*this, &SessionPlaylists::track), boost::weak_ptr<Playlist>(playlist)));
77         }
78
79         return existing;
80 }
81
82 void
83 SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
84 {
85         Glib::Mutex::Lock lm (lock);
86
87         List::iterator i;
88
89         i = find (playlists.begin(), playlists.end(), playlist);
90         if (i != playlists.end()) {
91                 playlists.erase (i);
92         }
93
94         i = find (unused_playlists.begin(), unused_playlists.end(), playlist);
95         if (i != unused_playlists.end()) {
96                 unused_playlists.erase (i);
97         }
98 }
99         
100
101 void
102 SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
103 {
104         boost::shared_ptr<Playlist> pl(wpl.lock());
105
106         if (!pl) {
107                 return;
108         }
109
110         List::iterator x;
111
112         if (pl->hidden()) {
113                 /* its not supposed to be visible */
114                 return;
115         }
116
117         {
118                 Glib::Mutex::Lock lm (lock);
119
120                 if (!inuse) {
121
122                         unused_playlists.insert (pl);
123
124                         if ((x = playlists.find (pl)) != playlists.end()) {
125                                 playlists.erase (x);
126                         }
127
128
129                 } else {
130
131                         playlists.insert (pl);
132
133                         if ((x = unused_playlists.find (pl)) != unused_playlists.end()) {
134                                 unused_playlists.erase (x);
135                         }
136                 }
137         }
138 }
139
140 uint32_t
141 SessionPlaylists::n_playlists () const
142 {
143         Glib::Mutex::Lock lm (lock);
144         return playlists.size();
145 }
146
147 boost::shared_ptr<Playlist>
148 SessionPlaylists::by_name (string name)
149 {
150         Glib::Mutex::Lock lm (lock);
151
152         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
153                 if ((*i)->name() == name) {
154                         return* i;
155                 }
156         }
157         
158         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
159                 if ((*i)->name() == name) {
160                         return* i;
161                 }
162         }
163
164         return boost::shared_ptr<Playlist>();
165 }
166
167 void
168 SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & list)
169 {
170         Glib::Mutex::Lock lm (lock);
171
172         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
173                 if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
174                         list.push_back (*i);
175                 }
176         }
177         
178         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
179                 if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
180                         list.push_back (*i);
181                 }
182         }
183 }
184
185 void
186 SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s)
187 {
188         Glib::Mutex::Lock lm (lock);
189
190         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
191                 s.push_back (*i);
192         }
193         
194         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
195                 s.push_back (*i);
196         }
197 }
198
199 void
200 SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
201 {
202         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i)
203                 (*i)->get_region_list_equivalent_regions (region, result);
204 }
205
206 /** Return the number of playlists (not regions) that contain @a src */
207 uint32_t
208 SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
209 {
210         uint32_t count = 0;
211         for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
212                 for (Playlist::RegionList::const_iterator r = (*p)->region_list().begin();
213                                 r != (*p)->region_list().end(); ++r) {
214                         if ((*r)->uses_source(src)) {
215                                 ++count;
216                                 break;
217                         }
218                 }
219         }
220         return count;
221 }
222
223
224 void
225 SessionPlaylists::update_after_tempo_map_change ()
226 {
227         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
228                 (*i)->update_after_tempo_map_change ();
229         }
230
231         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
232                 (*i)->update_after_tempo_map_change ();
233         }
234 }
235
236 void
237 SessionPlaylists::add_state (XMLNode* node, bool full_state)
238 {
239         XMLNode* child = node->add_child ("Playlists");
240         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
241                 if (!(*i)->hidden()) {
242                         if (!(*i)->empty()) {
243                                 if (full_state) {
244                                         child->add_child_nocopy ((*i)->get_state());
245                                 } else {
246                                         child->add_child_nocopy ((*i)->get_template());
247                                 }
248                         }
249                 }
250         }
251
252         child = node->add_child ("UnusedPlaylists");
253         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
254                 if (!(*i)->hidden()) {
255                         if (!(*i)->empty()) {
256                                 if (full_state) {
257                                         child->add_child_nocopy ((*i)->get_state());
258                                 } else {
259                                         child->add_child_nocopy ((*i)->get_template());
260                                 }
261                         }
262                 }
263         }
264 }
265
266 /** @return true for `stop cleanup', otherwise false */
267 bool
268 SessionPlaylists::maybe_delete_unused (sigc::signal<int, boost::shared_ptr<Playlist> > ask)
269 {
270         vector<boost::shared_ptr<Playlist> > playlists_tbd;
271
272         for (List::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
273
274                 int status = ask (*x);
275
276                 switch (status) {
277                 case -1:
278                         return true;
279
280                 case 0:
281                         playlists_tbd.push_back (*x);
282                         break;
283
284                 default:
285                         /* leave it alone */
286                         break;
287                 }
288         }
289
290         /* now delete any that were marked for deletion */
291
292         for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
293                 (*x)->drop_references ();
294         }
295
296         playlists_tbd.clear ();
297
298         return false;
299 }
300
301 int
302 SessionPlaylists::load (Session& session, const XMLNode& node)
303 {
304         XMLNodeList nlist;
305         XMLNodeConstIterator niter;
306         boost::shared_ptr<Playlist> playlist;
307
308         nlist = node.children();
309
310         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
311
312                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
313                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
314                 }
315         }
316
317         return 0;
318 }
319
320 int
321 SessionPlaylists::load_unused (Session& session, const XMLNode& node)
322 {
323         XMLNodeList nlist;
324         XMLNodeConstIterator niter;
325         boost::shared_ptr<Playlist> playlist;
326
327         nlist = node.children();
328
329         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
330
331                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
332                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
333                         continue;
334                 }
335
336                 // now manually untrack it
337
338                 track (false, boost::weak_ptr<Playlist> (playlist));
339         }
340
341         return 0;
342 }
343
344 boost::shared_ptr<Playlist>
345 SessionPlaylists::XMLPlaylistFactory (Session& session, const XMLNode& node)
346 {
347         try {
348                 return PlaylistFactory::create (session, node);
349         }
350
351         catch (failed_constructor& err) {
352                 return boost::shared_ptr<Playlist>();
353         }
354 }
355