X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic_playlist.cc;h=96dff56ed21ec169264eb6d822ae7f3a8ac5366e;hp=a39aed63b8d8dfc4860f08aafb1b95d7df52b437;hb=ff3e116f48298bbee68a3f5679bffd509930c19b;hpb=7016527f7fcddd545986453cf05163edc217f732 diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index a39aed63b..96dff56ed 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2018-2020 Carl Hetherington + Copyright (C) 2018-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,40 +18,56 @@ */ -#include "../wx/wx_util.h" -#include "../wx/wx_signal_manager.h" -#include "../wx/content_view.h" -#include "../wx/dcpomatic_button.h" -#include "../wx/about_dialog.h" -#include "../wx/playlist_editor_config_dialog.h" -#include "../lib/util.h" -#include "../lib/config.h" -#include "../lib/cross.h" -#include "../lib/film.h" -#include "../lib/dcp_content.h" -#include "../lib/spl_entry.h" -#include "../lib/spl.h" -#include -#include + +#include "wx/about_dialog.h" +#include "wx/content_view.h" +#include "wx/dcpomatic_button.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/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 +LIBDCP_DISABLE_WARNINGS #include -#include +#include #include -#ifdef __WXOSX__ -#include -#endif -#include +#include +#include +LIBDCP_ENABLE_WARNINGS + -using std::exception; using std::cout; -using std::string; -using std::map; +using std::exception; using std::make_pair; +using std::make_shared; +using std::map; +using std::shared_ptr; +using std::string; using std::vector; -using boost::optional; -using boost::shared_ptr; -using boost::weak_ptr; +using std::weak_ptr; using boost::bind; -using boost::dynamic_pointer_cast; +using boost::optional; +using std::dynamic_pointer_cast; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif + + +static +void +save_playlist(shared_ptr playlist) +{ + if (auto dir = Config::instance()->player_playlist_directory()) { + playlist->write(*dir / (playlist->id() + ".xml")); + } +} + class ContentDialog : public wxDialog, public ContentStore { @@ -62,17 +78,20 @@ public: { _content_view->update (); - wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + auto overall_sizer = new wxBoxSizer (wxVERTICAL); SetSizer (overall_sizer); overall_sizer->Add (_content_view, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); - wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); + auto buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); if (buttons) { overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); } 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)); } shared_ptr selected () const @@ -80,13 +99,14 @@ public: return _content_view->selected (); } - shared_ptr get (string digest) const + shared_ptr get (string digest) const override { return _content_view->get (digest); } private: ContentView* _content_view; + boost::signals2::scoped_connection _config_changed_connection; }; @@ -97,8 +117,9 @@ public: PlaylistList (wxPanel* parent, ContentStore* content_store) : _sizer (new wxBoxSizer(wxVERTICAL)) , _content_store (content_store) + , _parent(parent) { - wxStaticText* label = new wxStaticText (parent, wxID_ANY, wxEmptyString); + auto label = new wxStaticText (parent, wxID_ANY, wxEmptyString); label->SetLabelMarkup (_("Playlists")); _sizer->Add (label, 0, wxTOP | wxLEFT, DCPOMATIC_SIZER_GAP * 2); @@ -109,13 +130,13 @@ public: _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 840); _list->AppendColumn (_("Length"), wxLIST_FORMAT_LEFT, 100); - wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL); + auto button_sizer = new wxBoxSizer (wxVERTICAL); _new = new Button (parent, _("New")); button_sizer->Add (_new, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); _delete = new Button (parent, _("Delete")); button_sizer->Add (_delete, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); - wxSizer* list = new wxBoxSizer (wxHORIZONTAL); + auto list = new wxBoxSizer (wxHORIZONTAL); list->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP); list->Add (button_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); @@ -127,6 +148,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 () @@ -137,7 +160,7 @@ public: shared_ptr first_playlist () const { if (_playlists.empty()) { - return shared_ptr(); + return {}; } return _playlists.front (); @@ -146,6 +169,11 @@ public: boost::signals2::signal)> Edit; private: + void setup_sensitivity() + { + _delete->Enable(static_cast(selected())); + } + void add_playlist_to_view (shared_ptr playlist) { wxListItem item; @@ -157,72 +185,97 @@ private: void add_playlist_to_model (shared_ptr playlist) { _playlists.push_back (playlist); - playlist->NameChanged.connect (bind(&PlaylistList::name_changed, this, weak_ptr(playlist))); + playlist->Changed.connect(bind(&PlaylistList::changed, this, weak_ptr(playlist), _1)); } - void name_changed (weak_ptr wp) + void changed(weak_ptr wp, SignalSPL::Change change) { - shared_ptr playlist = wp.lock (); + auto playlist = wp.lock (); if (!playlist) { return; } - int N = 0; - BOOST_FOREACH (shared_ptr 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; } } void load_playlists () { - optional path = Config::instance()->player_playlist_directory(); + auto path = Config::instance()->player_playlist_directory(); if (!path) { return; } _list->DeleteAllItems (); _playlists.clear (); - for (boost::filesystem::directory_iterator i(*path); i != boost::filesystem::directory_iterator(); ++i) { - shared_ptr spl(new SignalSPL); + for (auto i: boost::filesystem::directory_iterator(*path)) { + auto spl = make_shared(); try { - spl->read (*i, _content_store); + spl->read (i, _content_store); add_playlist_to_model (spl); } catch (...) {} } - BOOST_FOREACH (shared_ptr i, _playlists) { + for (auto i: _playlists) { add_playlist_to_view (i); } } 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 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 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; } - optional dir = Config::instance()->player_playlist_directory(); + auto dir = Config::instance()->player_playlist_directory(); if (!dir) { return; } - boost::filesystem::remove (*dir / (_playlists[selected]->id() + ".xml")); - _list->DeleteItem (selected); - _playlists.erase (_playlists.begin() + selected); + boost::filesystem::remove(*dir / (_playlists[*index]->id() + ".xml")); + _list->DeleteItem(*index); + _playlists.erase(_playlists.begin() + *index); - Edit (shared_ptr()); + Edit(shared_ptr()); } void selection_changed () @@ -233,14 +286,17 @@ private: } else { Edit (_playlists[selected]); } + + setup_sensitivity(); } wxBoxSizer* _sizer; wxListCtrl* _list; wxButton* _new; wxButton* _delete; - vector > _playlists; + vector> _playlists; ContentStore* _content_store; + wxWindow* _parent; }; @@ -251,15 +307,17 @@ public: : _content_dialog (content_dialog) , _sizer (new wxBoxSizer(wxVERTICAL)) { - wxBoxSizer* title = new wxBoxSizer (wxHORIZONTAL); - wxStaticText* label = new wxStaticText (parent, wxID_ANY, wxEmptyString); + auto title = new wxBoxSizer (wxHORIZONTAL); + auto label = new wxStaticText (parent, wxID_ANY, wxEmptyString); label->SetLabelMarkup (_("Playlist:")); 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); - wxBoxSizer* list = new wxBoxSizer (wxHORIZONTAL); + auto list = new wxBoxSizer (wxHORIZONTAL); _list = new wxListCtrl ( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL @@ -270,18 +328,11 @@ public: _list->AppendColumn (_("Type"), wxLIST_FORMAT_LEFT, 100); _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90); - wxImageList* images = new wxImageList (16, 16); + auto images = new wxImageList (16, 16); wxIcon tick_icon; wxIcon no_tick_icon; -#ifdef DCPOMATIC_OSX - tick_icon.LoadFile ("tick.png", wxBITMAP_TYPE_PNG_RESOURCE); - no_tick_icon.LoadFile ("no_tick.png", wxBITMAP_TYPE_PNG_RESOURCE); -#else - boost::filesystem::path tick_path = shared_path() / "tick.png"; - tick_icon.LoadFile (std_to_wx(tick_path.string()), wxBITMAP_TYPE_PNG); - boost::filesystem::path no_tick_path = shared_path() / "no_tick.png"; - no_tick_icon.LoadFile (std_to_wx(no_tick_path.string()), wxBITMAP_TYPE_PNG); -#endif + tick_icon.LoadFile (bitmap_path("tick.png"), wxBITMAP_TYPE_PNG); + no_tick_icon.LoadFile (bitmap_path("no_tick.png"), wxBITMAP_TYPE_PNG); images->Add (tick_icon); images->Add (no_tick_icon); @@ -289,7 +340,7 @@ public: list->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP); - wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL); + auto button_sizer = new wxBoxSizer (wxVERTICAL); _up = new Button (parent, _("Up")); _down = new Button (parent, _("Down")); _add = new Button (parent, _("Add")); @@ -306,10 +357,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 () @@ -322,7 +376,7 @@ public: _playlist = playlist; _list->DeleteAllItems (); if (_playlist) { - BOOST_FOREACH (SPLEntry i, _playlist->get()) { + for (auto i: _playlist->get()) { add (i); } _name->SetValue (std_to_wx(_playlist->name())); @@ -339,11 +393,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) @@ -358,7 +419,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")); } @@ -368,6 +429,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)); @@ -379,7 +441,7 @@ private: { int const r = _content_dialog->ShowModal (); if (r == wxID_OK) { - shared_ptr content = _content_dialog->selected (); + auto content = _content_dialog->selected (); if (content) { SPLEntry e (content); add (e); @@ -398,9 +460,7 @@ private: DCPOMATIC_ASSERT (_playlist); - SPLEntry 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]); @@ -415,9 +475,7 @@ private: DCPOMATIC_ASSERT (_playlist); - SPLEntry 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]); @@ -438,6 +496,7 @@ private: ContentDialog* _content_dialog; wxBoxSizer* _sizer; wxTextCtrl* _name; + Button* _save_name; wxListCtrl* _list; wxButton* _up; wxButton* _down; @@ -451,18 +510,19 @@ class DOMFrame : public wxFrame { public: explicit DOMFrame (wxString const & title) - : wxFrame (0, -1, title) + : wxFrame (nullptr, wxID_ANY, title) , _content_dialog (new ContentDialog(this)) + , _config_dialog (nullptr) { - wxMenuBar* bar = new wxMenuBar; + auto bar = new wxMenuBar; setup_menu (bar); SetMenuBar (bar); /* Use a panel as the only child of the Frame so that we avoid the dark-grey background on Windows. */ - wxPanel* overall_panel = new wxPanel (this, wxID_ANY); - wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); + auto overall_panel = new wxPanel (this, wxID_ANY); + auto sizer = new wxBoxSizer (wxVERTICAL); _playlist_list = new PlaylistList (overall_panel, _content_dialog); _playlist_content = new PlaylistContent (overall_panel, _content_dialog); @@ -474,11 +534,11 @@ 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); + + _config_changed_connection = Config::instance()->Changed.connect(boost::bind(&DOMFrame::config_changed, this)); } private: @@ -491,7 +551,7 @@ private: void help_about () { - AboutDialog* d = new AboutDialog (this); + auto d = new AboutDialog (this); d->ShowModal (); d->Destroy (); } @@ -506,26 +566,16 @@ private: void change_playlist (shared_ptr playlist) { - shared_ptr old = _playlist_content->playlist (); + auto old = _playlist_content->playlist (); if (old) { save_playlist (old); } _playlist_content->set (playlist); } - void save_playlist (shared_ptr playlist) - { - optional dir = Config::instance()->player_playlist_directory(); - if (!dir) { - error_dialog (this, _("No playlist folder is specified in preferences. Please set on and then try again.")); - return; - } - playlist->write (*dir / (playlist->id() + ".xml")); - } - void setup_menu (wxMenuBar* m) { - wxMenu* file = new wxMenu; + auto file = new wxMenu; #ifdef __WXOSX__ file->Append (wxID_EXIT, _("&Exit")); #else @@ -533,11 +583,11 @@ private: #endif #ifndef __WXOSX__ - wxMenu* edit = new wxMenu; + auto edit = new wxMenu; edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P")); #endif - wxMenu* help = new wxMenu; + auto help = new wxMenu; #ifdef __WXOSX__ help->Append (wxID_ABOUT, _("About DCP-o-matic")); #else @@ -551,12 +601,30 @@ private: m->Append (help, _("&Help")); } + + void config_changed () + { + try { + Config::instance()->write_config(); + } catch (exception& e) { + error_dialog ( + this, + wxString::Format ( + _("Could not write to config file at %s. Your changes have not been saved."), + std_to_wx (Config::instance()->cinemas_file().string()).data() + ) + ); + } + } + ContentDialog* _content_dialog; PlaylistList* _playlist_list; PlaylistContent* _playlist_content; wxPreferencesEditor* _config_dialog; + boost::signals2::scoped_connection _config_changed_connection; }; + /** @class App * @brief The magic App class for wxWidgets. */ @@ -565,15 +633,16 @@ class App : public wxApp public: App () : wxApp () - , _frame (0) + , _frame (nullptr) {} private: - bool OnInit () + bool OnInit () override try { - SetAppName (_("DCP-o-matic KDM Creator")); + wxInitAllImageHandlers (); + SetAppName (_("DCP-o-matic Playlist Editor")); if (!wxApp::OnInit()) { return false; @@ -583,10 +652,8 @@ private: unsetenv ("UBUNTU_MENUPROXY"); #endif -#ifdef __WXOSX__ - ProcessSerialNumber serial; - GetCurrentProcess (&serial); - TransformProcessType (&serial, kProcessTransformToForegroundApplication); +#ifdef DCPOMATIC_OSX + make_foreground_application (); #endif dcpomatic_setup_path_encoding (); @@ -626,7 +693,7 @@ private: } /* An unhandled exception has occurred inside the main event loop */ - bool OnExceptionInMainLoop () + bool OnExceptionInMainLoop () override { try { throw; @@ -655,7 +722,7 @@ private: return false; } - void OnUnhandledException () + void OnUnhandledException () override { error_dialog (0, _("An unknown exception occurred.") + " " + REPORT_PROBLEM); }