Supporters update.
[dcpomatic.git] / src / tools / dcpomatic_playlist.cc
index dbc5e81ddf673b445876b42863260daddfdd936e..a373d81e69e518f329bbd69479f78c4b86534544 100644 (file)
 */
 
 
-#include "wx/wx_util.h"
-#include "wx/wx_signal_manager.h"
+#include "wx/about_dialog.h"
 #include "wx/content_view.h"
 #include "wx/dcpomatic_button.h"
-#include "wx/about_dialog.h"
 #include "wx/playlist_editor_config_dialog.h"
+#include "wx/wx_signal_manager.h"
+#include "wx/wx_util.h"
 #include "lib/config.h"
+#include "lib/constants.h"
 #include "lib/cross.h"
 #include "lib/dcp_content.h"
 #include "lib/film.h"
 #include "lib/spl.h"
 #include "lib/spl_entry.h"
-#include "lib/util.h"
-#include <wx/wx.h>
-#include <wx/listctrl.h>
+#include <dcp/filesystem.h>
+#include <dcp/warnings.h>
+LIBDCP_DISABLE_WARNINGS
 #include <wx/imaglist.h>
-#include <wx/spinctrl.h>
+#include <wx/listctrl.h>
 #include <wx/preferences.h>
+#include <wx/spinctrl.h>
+#include <wx/wx.h>
+LIBDCP_ENABLE_WARNINGS
 
 
 using std::cout;
@@ -56,6 +60,16 @@ using namespace boost::placeholders;
 #endif
 
 
+static
+void
+save_playlist(shared_ptr<const SPL> playlist)
+{
+       if (auto dir = Config::instance()->player_playlist_directory()) {
+               playlist->write(*dir / (playlist->id() + ".xml"));
+       }
+}
+
+
 class ContentDialog : public wxDialog, public ContentStore
 {
 public:
@@ -77,6 +91,7 @@ public:
 
                overall_sizer->Layout ();
 
+               _content_view->Bind(wxEVT_LIST_ITEM_ACTIVATED, boost::bind(&ContentDialog::EndModal, this, wxID_OK));
                _config_changed_connection = Config::instance()->Changed.connect(boost::bind(&ContentView::update, _content_view));
        }
 
@@ -103,6 +118,7 @@ public:
        PlaylistList (wxPanel* parent, ContentStore* content_store)
                : _sizer (new wxBoxSizer(wxVERTICAL))
                , _content_store (content_store)
+               , _parent(parent)
        {
                auto label = new wxStaticText (parent, wxID_ANY, wxEmptyString);
                label->SetLabelMarkup (_("<b>Playlists</b>"));
@@ -133,6 +149,8 @@ public:
                _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, bind(&PlaylistList::selection_changed, this));
                _new->Bind (wxEVT_BUTTON, bind(&PlaylistList::new_playlist, this));
                _delete->Bind (wxEVT_BUTTON, bind(&PlaylistList::delete_playlist, this));
+
+               setup_sensitivity();
        }
 
        wxSizer* sizer ()
@@ -152,6 +170,11 @@ public:
        boost::signals2::signal<void (shared_ptr<SignalSPL>)> Edit;
 
 private:
+       void setup_sensitivity()
+       {
+               _delete->Enable(static_cast<bool>(selected()));
+       }
+
        void add_playlist_to_view (shared_ptr<const SignalSPL> playlist)
        {
                wxListItem item;
@@ -163,22 +186,31 @@ private:
        void add_playlist_to_model (shared_ptr<SignalSPL> playlist)
        {
                _playlists.push_back (playlist);
-               playlist->NameChanged.connect (bind(&PlaylistList::name_changed, this, weak_ptr<SignalSPL>(playlist)));
+               playlist->Changed.connect(bind(&PlaylistList::changed, this, weak_ptr<SignalSPL>(playlist), _1));
        }
 
-       void name_changed (weak_ptr<SignalSPL> wp)
+       void changed(weak_ptr<SignalSPL> wp, SignalSPL::Change change)
        {
                auto playlist = wp.lock ();
                if (!playlist) {
                        return;
                }
 
-               int N = 0;
-               for (auto i: _playlists) {
-                       if (i == playlist) {
-                               _list->SetItem (N, 0, std_to_wx(i->name()));
+               switch (change) {
+               case SignalSPL::Change::NAME:
+               {
+                       int N = 0;
+                       for (auto i: _playlists) {
+                               if (i == playlist) {
+                                       _list->SetItem (N, 0, std_to_wx(i->name()));
+                               }
+                               ++N;
                        }
-                       ++N;
+                       break;
+               }
+               case SignalSPL::Change::CONTENT:
+                       save_playlist(playlist);
+                       break;
                }
        }
 
@@ -191,13 +223,15 @@ private:
 
                _list->DeleteAllItems ();
                _playlists.clear ();
-               for (auto i: boost::filesystem::directory_iterator(*path)) {
-                       auto spl = make_shared<SignalSPL>();
-                       try {
-                               spl->read (i, _content_store);
-                               add_playlist_to_model (spl);
-                       } catch (...) {}
-               }
+               try {
+                       for (auto i: dcp::filesystem::directory_iterator(*path)) {
+                               auto spl = make_shared<SignalSPL>();
+                               try {
+                                       spl->read (i, _content_store);
+                                       add_playlist_to_model (spl);
+                               } catch (...) {}
+                       }
+               } catch (...) {}
 
                for (auto i: _playlists) {
                        add_playlist_to_view (i);
@@ -206,16 +240,32 @@ private:
 
        void new_playlist ()
        {
+               auto dir = Config::instance()->player_playlist_directory();
+               if (!dir) {
+                       error_dialog(_parent, _("No playlist folder is specified in preferences.  Please set one and then try again."));
+                       return;
+               }
+
                shared_ptr<SignalSPL> spl (new SignalSPL(wx_to_std(_("New Playlist"))));
                add_playlist_to_model (spl);
                add_playlist_to_view (spl);
                _list->SetItemState (_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
        }
 
-       void delete_playlist ()
+       boost::optional<int> selected() const
        {
-               long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+               long int selected = _list->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
                if (selected < 0 || selected >= int(_playlists.size())) {
+                       return {};
+               }
+
+               return selected;
+       }
+
+       void delete_playlist ()
+       {
+               auto index = selected();
+               if (!index) {
                        return;
                }
 
@@ -224,11 +274,11 @@ private:
                        return;
                }
 
-               boost::filesystem::remove (*dir / (_playlists[selected]->id() + ".xml"));
-               _list->DeleteItem (selected);
-               _playlists.erase (_playlists.begin() + selected);
+               dcp::filesystem::remove(*dir / (_playlists[*index]->id() + ".xml"));
+               _list->DeleteItem(*index);
+               _playlists.erase(_playlists.begin() + *index);
 
-               Edit (shared_ptr<SignalSPL>());
+               Edit(shared_ptr<SignalSPL>());
        }
 
        void selection_changed ()
@@ -239,6 +289,8 @@ private:
                } else {
                        Edit (_playlists[selected]);
                }
+
+               setup_sensitivity();
        }
 
        wxBoxSizer* _sizer;
@@ -247,6 +299,7 @@ private:
        wxButton* _delete;
        vector<shared_ptr<SignalSPL>> _playlists;
        ContentStore* _content_store;
+       wxWindow* _parent;
 };
 
 
@@ -263,6 +316,8 @@ public:
                title->Add (label, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_GAP);
                _name = new wxTextCtrl (parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400, -1));
                title->Add (_name, 0, wxRIGHT, DCPOMATIC_SIZER_GAP);
+               _save_name = new Button(parent, _("Save"));
+               title->Add(_save_name);
                _sizer->Add (title, 0, wxTOP | wxLEFT, DCPOMATIC_SIZER_GAP * 2);
 
                auto list = new wxBoxSizer (wxHORIZONTAL);
@@ -276,16 +331,6 @@ public:
                _list->AppendColumn (_("Type"), wxLIST_FORMAT_LEFT, 100);
                _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90);
 
-               auto images = new wxImageList (16, 16);
-               wxIcon tick_icon;
-               wxIcon no_tick_icon;
-               tick_icon.LoadFile (bitmap_path("tick"), wxBITMAP_TYPE_PNG);
-               no_tick_icon.LoadFile (bitmap_path("no_tick"), wxBITMAP_TYPE_PNG);
-               images->Add (tick_icon);
-               images->Add (no_tick_icon);
-
-               _list->SetImageList (images, wxIMAGE_LIST_SMALL);
-
                list->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP);
 
                auto button_sizer = new wxBoxSizer (wxVERTICAL);
@@ -305,10 +350,13 @@ public:
                _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, bind(&PlaylistContent::setup_sensitivity, this));
                _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, bind(&PlaylistContent::setup_sensitivity, this));
                _name->Bind (wxEVT_TEXT, bind(&PlaylistContent::name_changed, this));
+               _save_name->bind(&PlaylistContent::save_name_clicked, this);
                _up->Bind (wxEVT_BUTTON, bind(&PlaylistContent::up_clicked, this));
                _down->Bind (wxEVT_BUTTON, bind(&PlaylistContent::down_clicked, this));
                _add->Bind (wxEVT_BUTTON, bind(&PlaylistContent::add_clicked, this));
                _remove->Bind (wxEVT_BUTTON, bind(&PlaylistContent::remove_clicked, this));
+
+               setup_sensitivity();
        }
 
        wxSizer* sizer ()
@@ -338,11 +386,18 @@ public:
 
 
 private:
-       void name_changed ()
+       void save_name_clicked()
        {
                if (_playlist) {
-                       _playlist->set_name (wx_to_std(_name->GetValue()));
+                       _playlist->set_name(wx_to_std(_name->GetValue()));
+                       save_playlist(_playlist);
                }
+               setup_sensitivity();
+       }
+
+       void name_changed ()
+       {
+               setup_sensitivity();
        }
 
        void add (SPLEntry e)
@@ -357,7 +412,7 @@ private:
        {
                _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, 2, std_to_wx(e.kind->name()));
                _list->SetItem (N, 3, e.encrypted ? S_("Question|Y") : S_("Question|N"));
        }
 
@@ -367,6 +422,7 @@ private:
                int const num_selected = _list->GetSelectedItemCount ();
                long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
                _name->Enable (have_list);
+               _save_name->Enable(_playlist && _playlist->name() != wx_to_std(_name->GetValue()));
                _list->Enable (have_list);
                _up->Enable (have_list && selected > 0);
                _down->Enable (have_list && selected != -1 && selected < (_list->GetItemCount() - 1));
@@ -397,9 +453,7 @@ private:
 
                DCPOMATIC_ASSERT (_playlist);
 
-               auto tmp = (*_playlist)[s];
-               (*_playlist)[s] = (*_playlist)[s-1];
-               (*_playlist)[s-1] = tmp;
+               _playlist->swap(s, s - 1);
 
                set_item (s - 1, (*_playlist)[s-1]);
                set_item (s, (*_playlist)[s]);
@@ -414,9 +468,7 @@ private:
 
                DCPOMATIC_ASSERT (_playlist);
 
-               auto tmp = (*_playlist)[s];
-               (*_playlist)[s] = (*_playlist)[s+1];
-               (*_playlist)[s+1] = tmp;
+               _playlist->swap(s, s + 1);
 
                set_item (s + 1, (*_playlist)[s+1]);
                set_item (s, (*_playlist)[s]);
@@ -437,6 +489,7 @@ private:
        ContentDialog* _content_dialog;
        wxBoxSizer* _sizer;
        wxTextCtrl* _name;
+       Button* _save_name;
        wxListCtrl* _list;
        wxButton* _up;
        wxButton* _down;
@@ -474,8 +527,6 @@ public:
 
                _playlist_list->Edit.connect (bind(&DOMFrame::change_playlist, this, _1));
 
-               _playlist_content->set (_playlist_list->first_playlist());
-
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::edit_preferences, this), wxID_PREFERENCES);
@@ -493,9 +544,8 @@ private:
 
        void help_about ()
        {
-               auto d = new AboutDialog (this);
+               auto d = make_wx<AboutDialog>(this);
                d->ShowModal ();
-               d->Destroy ();
        }
 
        void edit_preferences ()
@@ -515,20 +565,11 @@ private:
                _playlist_content->set (playlist);
        }
 
-       void save_playlist (shared_ptr<SignalSPL> playlist)
-       {
-               auto dir = Config::instance()->player_playlist_directory();
-               if (!dir) {
-                       error_dialog (this, _("No playlist folder is specified in preferences.  Please set one and then try again."));
-                       return;
-               }
-               playlist->write (*dir / (playlist->id() + ".xml"));
-       }
-
        void setup_menu (wxMenuBar* m)
        {
                auto file = new wxMenu;
 #ifdef __WXOSX__
+               file->Append(wxID_PREFERENCES, _("&Preferences...\tCtrl-,"));
                file->Append (wxID_EXIT, _("&Exit"));
 #else
                file->Append (wxID_EXIT, _("&Quit"));