Separate out SPL/SPLEntry; start trying to make player read SPLs sensibly.
authorCarl Hetherington <cth@carlh.net>
Tue, 20 Nov 2018 00:01:03 +0000 (00:01 +0000)
committerCarl Hetherington <cth@carlh.net>
Thu, 22 Nov 2018 23:26:27 +0000 (23:26 +0000)
src/lib/content_store.h [new file with mode: 0644]
src/lib/spl.cc [new file with mode: 0644]
src/lib/spl.h [new file with mode: 0644]
src/lib/spl_entry.cc [new file with mode: 0644]
src/lib/spl_entry.h [new file with mode: 0644]
src/lib/wscript
src/tools/dcpomatic_playlist.cc
src/wx/content_view.h
src/wx/controls.cc
src/wx/controls.h

diff --git a/src/lib/content_store.h b/src/lib/content_store.h
new file mode 100644 (file)
index 0000000..028fa27
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <boost/shared_ptr.hpp>
+
+class Content;
+
+/** @class ContentStore
+ *  @brief Parent for classes which store content and can return content with a given digest.
+ */
+class ContentStore
+{
+public:
+       virtual boost::shared_ptr<Content> get (std::string digest) const = 0;
+};
diff --git a/src/lib/spl.cc b/src/lib/spl.cc
new file mode 100644 (file)
index 0000000..5447066
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "spl.h"
+#include "content_store.h"
+#include <libcxml/cxml.h>
+#include <libxml++/libxml++.h>
+#include <boost/foreach.hpp>
+
+using boost::shared_ptr;
+
+bool
+SPL::read (boost::filesystem::path path, ContentStore* store)
+{
+       _spl.clear ();
+       cxml::Document doc ("SPL");
+       doc.read_file (path);
+       bool missing = false;
+       BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) {
+               shared_ptr<Content> c = store->get(i->string_child("Digest"));
+               if (c) {
+                       add (SPLEntry(c, i));
+               } else {
+                       missing = true;
+               }
+       }
+
+       _name = path.filename().string();
+       return missing;
+}
+
+void
+SPL::write (boost::filesystem::path path) const
+{
+       xmlpp::Document doc;
+       xmlpp::Element* root = doc.create_root_node ("SPL");
+       BOOST_FOREACH (SPLEntry i, _spl) {
+               i.as_xml (root->add_child("Entry"));
+       }
+       doc.write_to_file_formatted (path.string());
+}
diff --git a/src/lib/spl.h b/src/lib/spl.h
new file mode 100644 (file)
index 0000000..b93a930
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "spl_entry.h"
+
+class ContentStore;
+
+class SPL
+{
+public:
+       void add (SPLEntry e) {
+               _spl.push_back (e);
+       }
+
+       void remove (std::size_t index) {
+               _spl.erase (_spl.begin() + index);
+       }
+
+       std::vector<SPLEntry> const & get () const {
+               return _spl;
+       }
+
+       SPLEntry & operator[] (std::size_t index) {
+               return _spl[index];
+       }
+
+       SPLEntry const & operator[] (std::size_t index) const {
+               return _spl[index];
+       }
+
+       bool read (boost::filesystem::path path, ContentStore* store);
+       void write (boost::filesystem::path path) const;
+
+       std::string name () const {
+               return _name;
+       }
+
+private:
+       std::string _name;
+       std::vector<SPLEntry> _spl;
+};
diff --git a/src/lib/spl_entry.cc b/src/lib/spl_entry.cc
new file mode 100644 (file)
index 0000000..a2f3631
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "spl_entry.h"
+#include "dcp_content.h"
+#include "dcpomatic_assert.h"
+#include <libxml++/libxml++.h>
+
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+SPLEntry::SPLEntry (shared_ptr<Content> content)
+       : skippable (false)
+       , disable_timeline (false)
+       , stop_after_play (false)
+{
+       construct (content);
+}
+
+SPLEntry::SPLEntry (shared_ptr<Content> content, cxml::ConstNodePtr node)
+       : skippable (node->bool_child("Skippable"))
+       , disable_timeline (node->bool_child("DisableTimeline"))
+       , stop_after_play (node->bool_child("StopAfterPlay"))
+{
+       construct (content);
+}
+
+void
+SPLEntry::construct (shared_ptr<Content> c)
+{
+       content = c;
+       shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content);
+       digest = content->digest ();
+       if (dcp) {
+               name = dcp->name ();
+               DCPOMATIC_ASSERT (dcp->cpl());
+               id = *dcp->cpl();
+               kind = dcp->content_kind().get_value_or(dcp::FEATURE);
+               type = DCP;
+               encrypted = dcp->encrypted ();
+       } else {
+               name = content->path(0).filename().string();
+               type = ECINEMA;
+               kind = dcp::FEATURE;
+       }
+}
+
+void
+SPLEntry::as_xml (xmlpp::Element* e)
+{
+       e->add_child("Digest")->add_child_text(digest);
+       e->add_child("Skippable")->add_child_text(skippable ? "1" : "0");
+       e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0");
+       e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0");
+}
diff --git a/src/lib/spl_entry.h b/src/lib/spl_entry.h
new file mode 100644 (file)
index 0000000..d939ec6
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic 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.
+
+    DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef DCPOMATIC_SPL_ENTRY_H
+#define DCPOMATIC_SPL_ENTRY_H
+
+#include <libcxml/cxml.h>
+#include <dcp/types.h>
+#include <boost/shared_ptr.hpp>
+
+namespace xmlpp {
+       class Element;
+}
+
+class Content;
+
+class SPLEntry
+{
+public:
+       SPLEntry (boost::shared_ptr<Content> content);
+       SPLEntry (boost::shared_ptr<Content> content, cxml::ConstNodePtr node);
+
+       void as_xml (xmlpp::Element* e);
+
+       boost::shared_ptr<Content> content;
+       std::string name;
+       /** Digest of this content */
+       std::string digest;
+       /** CPL ID or something else for MP4 (?) */
+       std::string id;
+       dcp::ContentKind kind;
+       enum Type {
+               DCP,
+               ECINEMA
+       };
+       Type type;
+       bool encrypted;
+       bool skippable;
+       bool disable_timeline;
+       bool stop_after_play;
+
+private:
+       void construct (boost::shared_ptr<Content> content);
+};
+
+#endif
index 7c332e88df1d4843a74bf1598fa4786e3bf12518..e2aeb7107ec8db83bc4410758d4fd89be51cb635 100644 (file)
@@ -141,6 +141,8 @@ sources = """
           send_problem_report_job.cc
           server.cc
           shuffler.cc
+          spl.cc
+          spl_entry.cc
           string_log_entry.cc
           string_text_file.cc
           string_text_file_content.cc
index 43cb70550c73a0ec4861aa0ce606dc5b06728639..2c575af366f3fe922eedb5b108f3f461c389f712 100644 (file)
@@ -26,6 +26,8 @@
 #include "../lib/cross.h"
 #include "../lib/film.h"
 #include "../lib/dcp_content.h"
+#include "../lib/spl_entry.h"
+#include "../lib/spl.h"
 #include <wx/wx.h>
 #include <wx/listctrl.h>
 #include <wx/imaglist.h>
@@ -39,69 +41,7 @@ using boost::weak_ptr;
 using boost::bind;
 using boost::dynamic_pointer_cast;
 
-class PlaylistEntry
-{
-public:
-       PlaylistEntry (boost::shared_ptr<Content> content)
-               : skippable (false)
-               , disable_timeline (false)
-               , stop_after_play (false)
-       {
-               construct (content);
-       }
-
-       PlaylistEntry (boost::shared_ptr<Content> content, cxml::ConstNodePtr node)
-               : skippable (node->bool_child("Skippable"))
-               , disable_timeline (node->bool_child("DisableTimeline"))
-               , stop_after_play (node->bool_child("StopAfterPlay"))
-       {
-               construct (content);
-       }
-
-       void construct (shared_ptr<Content> content)
-       {
-               shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content);
-               digest = content->digest ();
-               if (dcp) {
-                       name = dcp->name ();
-                       DCPOMATIC_ASSERT (dcp->cpl());
-                       id = *dcp->cpl();
-                       kind = dcp->content_kind().get_value_or(dcp::FEATURE);
-                       type = DCP;
-                       encrypted = dcp->encrypted ();
-               } else {
-                       name = content->path(0).filename().string();
-                       type = ECINEMA;
-                       kind = dcp::FEATURE;
-               }
-       }
-
-       void as_xml (xmlpp::Element* e)
-       {
-               e->add_child("Digest")->add_child_text(digest);
-               e->add_child("Skippable")->add_child_text(skippable ? "1" : "0");
-               e->add_child("DisableTimeline")->add_child_text(disable_timeline ? "1" : "0");
-               e->add_child("StopAfterPlay")->add_child_text(stop_after_play ? "1" : "0");
-       }
-
-       std::string name;
-       /** Digest of this content */
-       std::string digest;
-       /** CPL ID or something else for MP4 (?) */
-       std::string id;
-       dcp::ContentKind kind;
-       enum Type {
-               DCP,
-               ECINEMA
-       };
-       Type type;
-       bool encrypted;
-       bool skippable;
-       bool disable_timeline;
-       bool stop_after_play;
-};
-
-class ContentDialog : public wxDialog
+class ContentDialog : public wxDialog, public ContentStore
 {
 public:
        ContentDialog (wxWindow* parent, weak_ptr<Film> film)
@@ -216,13 +156,13 @@ public:
 
 private:
 
-       void add (PlaylistEntry e)
+       void add (SPLEntry e)
        {
                wxListItem item;
                item.SetId (_list->GetItemCount());
                long const N = _list->InsertItem (item);
                set_item (N, e);
-               _playlist.push_back (e);
+               _playlist.add (e);
        }
 
        void selection_changed ()
@@ -230,12 +170,12 @@ private:
                setup_sensitivity ();
        }
 
-       void set_item (long N, PlaylistEntry e)
+       void set_item (long N, SPLEntry e)
        {
                _list->SetItem (N, 0, std_to_wx(e.name));
                _list->SetItem (N, 1, std_to_wx(e.id));
                _list->SetItem (N, 2, std_to_wx(dcp::content_kind_to_string(e.kind)));
-               _list->SetItem (N, 3, e.type == PlaylistEntry::DCP ? _("DCP") : _("E-cinema"));
+               _list->SetItem (N, 3, e.type == SPLEntry::DCP ? _("DCP") : _("E-cinema"));
                _list->SetItem (N, 4, e.encrypted ? _("Y") : _("N"));
                _list->SetItem (N, COLUMN_SKIPPABLE, wxEmptyString, e.skippable ? 0 : 1);
                _list->SetItem (N, COLUMN_DISABLE_TIMELINE, wxEmptyString, e.disable_timeline ? 0 : 1);
@@ -291,7 +231,7 @@ private:
                if (r == wxID_OK) {
                        shared_ptr<Content> content = _content_dialog->selected ();
                        if (content) {
-                               add (PlaylistEntry(content));
+                               add (SPLEntry(content));
                        }
                }
        }
@@ -303,7 +243,7 @@ private:
                        return;
                }
 
-               PlaylistEntry tmp = _playlist[s];
+               SPLEntry tmp = _playlist[s];
                _playlist[s] = _playlist[s-1];
                _playlist[s-1] = tmp;
 
@@ -318,7 +258,7 @@ private:
                        return;
                }
 
-               PlaylistEntry tmp = _playlist[s];
+               SPLEntry tmp = _playlist[s];
                _playlist[s] = _playlist[s+1];
                _playlist[s+1] = tmp;
 
@@ -333,7 +273,7 @@ private:
                        return;
                }
 
-               _playlist.erase (_playlist.begin() + s);
+               _playlist.remove (s);
                _list->DeleteItem (s);
        }
 
@@ -341,12 +281,7 @@ private:
        {
                wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
                if (d->ShowModal() == wxID_OK) {
-                       xmlpp::Document doc;
-                       xmlpp::Element* root = doc.create_root_node ("SPL");
-                       BOOST_FOREACH (PlaylistEntry i, _playlist) {
-                               i.as_xml (root->add_child("Entry"));
-                       }
-                       doc.write_to_file_formatted (wx_to_std(d->GetPath()));
+                       _playlist.write (wx_to_std(d->GetPath()));
                }
        }
 
@@ -355,19 +290,7 @@ private:
                wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), wxEmptyString, wxEmptyString, wxT("XML files (*.xml)|*.xml"));
                if (d->ShowModal() == wxID_OK) {
                        _list->DeleteAllItems ();
-                       _playlist.clear ();
-                       cxml::Document doc ("SPL");
-                       doc.read_file (wx_to_std(d->GetPath()));
-                       bool missing = false;
-                       BOOST_FOREACH (cxml::ConstNodePtr i, doc.node_children("Entry")) {
-                               shared_ptr<Content> c = _content_dialog->get(i->string_child("Digest"));
-                               if (c) {
-                                       add (PlaylistEntry(c, i));
-                               } else {
-                                       missing = true;
-                               }
-                       }
-                       if (missing) {
+                       if (_playlist.read (wx_to_std(d->GetPath()), _content_dialog)) {
                                error_dialog (this, _("Some content in this playlist was not found."));
                        }
                }
@@ -381,7 +304,7 @@ private:
        wxButton* _save;
        wxButton* _load;
        boost::shared_ptr<Film> _film;
-       std::vector<PlaylistEntry> _playlist;
+       SPL _playlist;
        ContentDialog* _content_dialog;
 
        enum {
index 471dd054bd95f81ab3ec29644a64c6a53ce465d4..d27638c05099f89bc3ebe0555e46177676796663 100644 (file)
@@ -18,6 +18,7 @@
 
 */
 
+#include "lib/content_store.h"
 #include <wx/listctrl.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
@@ -26,7 +27,7 @@
 class Content;
 class Film;
 
-class ContentView : public wxListCtrl
+class ContentView : public wxListCtrl, public ContentStore
 {
 public:
        ContentView (wxWindow* parent, boost::weak_ptr<Film> film);
index 5fa8d7176761e0e3090bc43971d0923ecde5fcce..cc9817b15f2dbef097ca963589be97b6dab3b59e 100644 (file)
@@ -103,9 +103,8 @@ Controls::Controls (wxWindow* parent, shared_ptr<FilmViewer> viewer, bool editor
        e_sizer->Add (left_sizer, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP);
 
        _current_spl_view = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_NO_HEADER);
+       _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 500);
        _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80);
-       _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80);
-       _current_spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 580);
        e_sizer->Add (_current_spl_view, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP);
 
        _v_sizer->Add (e_sizer, 1, wxEXPAND);
@@ -165,12 +164,12 @@ Controls::Controls (wxWindow* parent, shared_ptr<FilmViewer> viewer, bool editor
        _forward_button->Bind   (wxEVT_LEFT_DOWN,            boost::bind(&Controls::forward_clicked, this, _1));
        _frame_number->Bind     (wxEVT_LEFT_DOWN,            boost::bind(&Controls::frame_number_clicked, this));
        _timecode->Bind         (wxEVT_LEFT_DOWN,            boost::bind(&Controls::timecode_clicked, this));
-       _content_view->Bind     (wxEVT_LIST_ITEM_SELECTED,   boost::bind(&Controls::setup_sensitivity, this));
-       _content_view->Bind     (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&Controls::setup_sensitivity, this));
        if (_jump_to_selected) {
                _jump_to_selected->Bind (wxEVT_CHECKBOX, boost::bind (&Controls::jump_to_selected_clicked, this));
                _jump_to_selected->SetValue (Config::instance()->jump_to_selected ());
        }
+       _spl_view->Bind         (wxEVT_LIST_ITEM_SELECTED,   boost::bind(&Controls::spl_selection_changed, this));
+       _spl_view->Bind         (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&Controls::spl_selection_changed, this));
 
        _viewer->PositionChanged.connect (boost::bind(&Controls::position_changed, this));
        _viewer->Started.connect (boost::bind(&Controls::started, this));
@@ -191,6 +190,31 @@ Controls::Controls (wxWindow* parent, shared_ptr<FilmViewer> viewer, bool editor
        config_changed (Config::OTHER);
 }
 
+void
+Controls::spl_selection_changed ()
+{
+       long int selected = _spl_view->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       if (selected == -1) {
+               _current_spl_view->DeleteAllItems ();
+               return;
+       }
+
+       shared_ptr<Film> film (new Film(optional<boost::filesystem::path>()));
+
+       int N = 0;
+       BOOST_FOREACH (SPLEntry i, _playlists[selected].get()) {
+               wxListItem it;
+               it.SetId (N);
+               it.SetColumn (0);
+               it.SetText (std_to_wx(i.name));
+               _current_spl_view->InsertItem (it);
+               film->add_content (i.content);
+               ++N;
+       }
+
+       _viewer->set_film (film);
+}
+
 void
 Controls::config_changed (int property)
 {
@@ -248,18 +272,6 @@ Controls::outline_content_changed ()
        _viewer->set_outline_content (_outline_content->GetValue());
 }
 
-void
-Controls::film_change (ChangeType type, Film::Property p)
-{
-       if (type != CHANGE_TYPE_DONE) {
-               return;
-       }
-
-       if (p == Film::CONTENT || p == Film::THREE_D) {
-               setup_sensitivity ();
-       }
-}
-
 /** @param page true if this was a PAGEUP/PAGEDOWN event for which we won't receive a THUMBRELEASE */
 void
 Controls::slider_moved (bool page)
@@ -483,10 +495,6 @@ Controls::film_changed ()
        update_position_slider ();
        update_position_label ();
 
-       if (_film) {
-               _film->Change.connect (boost::bind (&Controls::film_change, this, _1, _2));
-       }
-
        _content_view->set_film (film);
        _content_view->update ();
 }
@@ -512,14 +520,14 @@ Controls::show_extended_player_controls (bool s)
 }
 
 void
-Controls::add_playlist_to_list (shared_ptr<Film> film)
+Controls::add_playlist_to_list (SPL spl)
 {
        int const N = _spl_view->GetItemCount();
 
        wxListItem it;
        it.SetId(N);
        it.SetColumn(0);
-       it.SetText (std_to_wx(film->name()));
+       it.SetText (std_to_wx(spl.name()));
        _spl_view->InsertItem (it);
 }
 
@@ -540,10 +548,12 @@ Controls::update_playlist_directory ()
 
        for (directory_iterator i = directory_iterator(*dir); i != directory_iterator(); ++i) {
                try {
-                       shared_ptr<Film> film (new Film(optional<path>()));
-                       film->read_metadata (i->path());
-                       _playlists.push_back (film);
-                       add_playlist_to_list (film);
+                       if (is_regular_file(i->path()) && i->path().extension() == ".xml") {
+                               SPL spl;
+                               spl.read (i->path(), _content_view);
+                               _playlists.push_back (spl);
+                               add_playlist_to_list (spl);
+                       }
                } catch (exception& e) {
                        /* Never mind */
                }
index 3bb963660b4c0a47222e9f4a5fcc0f11dc2b61a6..7cdd4a0d2f0a7984b11f06b7c8898d3a74065bea 100644 (file)
@@ -21,6 +21,7 @@
 #include "lib/dcpomatic_time.h"
 #include "lib/types.h"
 #include "lib/film.h"
+#include "lib/spl.h"
 #include <wx/wx.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/signals2.hpp>
@@ -33,6 +34,7 @@ class PlayerVideo;
 class wxToggleButton;
 class wxListCtrl;
 class ContentView;
+class SPL;
 
 namespace dcp {
        class CPL;
@@ -73,7 +75,6 @@ private:
        void active_jobs_changed (boost::optional<std::string>);
        DCPTime nudge_amount (wxKeyboardState& ev);
        void image_changed (boost::weak_ptr<PlayerVideo>);
-       void film_change (ChangeType type, Film::Property p);
        void outline_content_changed ();
        void eye_changed ();
        void position_changed ();
@@ -83,6 +84,7 @@ private:
        void update_content_directory ();
        void update_playlist_directory ();
        void config_changed (int property);
+       void spl_selection_changed ();
 
        typedef std::pair<boost::shared_ptr<dcp::CPL>, boost::filesystem::path> CPL;
 
@@ -90,7 +92,7 @@ private:
        void pause_clicked ();
        void stop_clicked ();
 #endif
-       void add_playlist_to_list (boost::shared_ptr<Film> film);
+       void add_playlist_to_list (SPL spl);
 
        boost::shared_ptr<Film> _film;
        boost::shared_ptr<FilmViewer> _viewer;
@@ -106,7 +108,7 @@ private:
        wxListCtrl* _spl_view;
        wxListCtrl* _current_spl_view;
        wxTextCtrl* _log;
-       std::vector<boost::shared_ptr<Film> > _playlists;
+       std::vector<SPL> _playlists;
        wxSlider* _slider;
        wxButton* _rewind_button;
        wxButton* _back_button;