From a3f6e20d055cdf1697eab011622dc569010ad617 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 14 Nov 2018 23:58:40 +0000 Subject: [PATCH 1/1] swaroop: basic manipulation of content in playlist creator. --- src/tools/dcpomatic_playlist.cc | 148 ++++++++++++++++++++++++++++---- src/wx/content_view.cc | 147 +++++++++++++++++++++++++++++++ src/wx/content_view.h | 42 +++++++++ src/wx/controls.cc | 84 ++---------------- src/wx/controls.h | 5 +- src/wx/wscript | 1 + 6 files changed, 330 insertions(+), 97 deletions(-) create mode 100644 src/wx/content_view.cc create mode 100644 src/wx/content_view.h diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index ec8c1efd8..04beb0250 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -20,9 +20,12 @@ #include "../wx/wx_util.h" #include "../wx/wx_signal_manager.h" +#include "../wx/content_view.h" #include "../lib/util.h" #include "../lib/config.h" #include "../lib/cross.h" +#include "../lib/film.h" +#include "../lib/dcp_content.h" #include #include #include @@ -30,10 +33,32 @@ using std::exception; using std::cout; using boost::optional; +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::dynamic_pointer_cast; class PlaylistEntry { public: + PlaylistEntry (boost::shared_ptr content) + : skippable (false) + , disable_timeline (false) + , stop_after_play (false) + { + shared_ptr dcp = dynamic_pointer_cast (content); + if (dcp) { + name = dcp->name (); + cpl_id = dcp->cpl().get_value_or(""); + kind = dcp->content_kind().get_value_or(dcp::FEATURE); + type = DCP; + encrypted = dcp->encrypted (); + } else { + name = content->path(0).filename().string(); + type = ECINEMA; + } + } + std::string name; std::string cpl_id; dcp::ContentKind kind; @@ -48,12 +73,45 @@ public: bool stop_after_play; }; +class ContentDialog : public wxDialog +{ +public: + ContentDialog (wxWindow* parent, weak_ptr film) + : wxDialog (parent, wxID_ANY, _("Add content"), wxDefaultPosition, wxSize(800, 640)) + , _content_view (new ContentView(this, film)) + { + _content_view->update (); + + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + SetSizer (overall_sizer); + + overall_sizer->Add (_content_view, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); + + wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + overall_sizer->Layout (); + } + + shared_ptr selected () const + { + return _content_view->selected (); + } + +private: + ContentView* _content_view; +}; class DOMFrame : public wxFrame { public: explicit DOMFrame (wxString const & title) : wxFrame (0, -1, title) + /* XXX: this is a bit of a hack, but we need it to be able to use the Content class hierarchy */ + , _film (new Film(optional())) + , _content_dialog (new ContentDialog(this, _film)) { /* Use a panel as the only child of the Frame so that we avoid the dark-grey background on Windows. @@ -66,8 +124,8 @@ public: ); _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 400); - _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 400); - _list->AppendColumn (_("Type"), wxLIST_FORMAT_CENTRE, 75); + _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 350); + _list->AppendColumn (_("Type"), wxLIST_FORMAT_CENTRE, 100); _list->AppendColumn (_("Format"), wxLIST_FORMAT_CENTRE, 75); _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_CENTRE, 90); _list->AppendColumn (_("Skippable"), wxLIST_FORMAT_CENTRE, 90); @@ -111,16 +169,12 @@ public: overall_panel->SetSizer (main_sizer); _list->Bind (wxEVT_LEFT_DOWN, bind(&DOMFrame::list_left_click, this, _1)); - - PlaylistEntry pe; - pe.name = "Shit"; - pe.cpl_id = "sh-1t"; - pe.kind = dcp::FEATURE; - pe.type = PlaylistEntry::ECINEMA; - pe.encrypted = true; - pe.disable_timeline = false; - pe.stop_after_play = true; - add (pe); + _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&DOMFrame::selection_changed, this)); + _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&DOMFrame::selection_changed, this)); + _up->Bind (wxEVT_BUTTON, bind(&DOMFrame::up_clicked, this)); + _down->Bind (wxEVT_BUTTON, bind(&DOMFrame::down_clicked, this)); + _add->Bind (wxEVT_BUTTON, bind(&DOMFrame::add_clicked, this)); + _remove->Bind (wxEVT_BUTTON, bind(&DOMFrame::remove_clicked, this)); setup_sensitivity (); } @@ -130,12 +184,17 @@ private: void add (PlaylistEntry e) { wxListItem item; - item.SetId (0); + item.SetId (_list->GetItemCount()); long const N = _list->InsertItem (item); set_item (N, e); _playlist.push_back (e); } + void selection_changed () + { + setup_sensitivity (); + } + void set_item (long N, PlaylistEntry e) { _list->SetItem (N, 0, std_to_wx(e.name)); @@ -150,10 +209,11 @@ private: void setup_sensitivity () { - int const selected = _list->GetSelectedItemCount (); + int const num_selected = _list->GetSelectedItemCount (); + long int selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); _up->Enable (selected > 0); - _down->Enable (selected > 0); - _remove->Enable (selected > 0); + _down->Enable (selected != -1 && selected < (_list->GetItemCount() - 1)); + _remove->Enable (num_selected > 0); } void list_left_click (wxMouseEvent& ev) @@ -190,6 +250,58 @@ private: } } + void add_clicked () + { + int const r = _content_dialog->ShowModal (); + if (r == wxID_OK) { + shared_ptr content = _content_dialog->selected (); + if (content) { + add (PlaylistEntry(content)); + } + } + } + + void up_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s < 1) { + return; + } + + PlaylistEntry tmp = _playlist[s]; + _playlist[s] = _playlist[s-1]; + _playlist[s-1] = tmp; + + set_item (s - 1, _playlist[s-1]); + set_item (s, _playlist[s]); + } + + void down_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s > (_list->GetItemCount() - 1)) { + return; + } + + PlaylistEntry tmp = _playlist[s]; + _playlist[s] = _playlist[s+1]; + _playlist[s+1] = tmp; + + set_item (s + 1, _playlist[s+1]); + set_item (s, _playlist[s]); + } + + void remove_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s == -1) { + return; + } + + _playlist.erase (_playlist.begin() + s); + _list->DeleteItem (s); + } + wxListCtrl* _list; wxButton* _up; wxButton* _down; @@ -197,7 +309,9 @@ private: wxButton* _remove; wxButton* _save; wxButton* _load; + boost::shared_ptr _film; std::vector _playlist; + ContentDialog* _content_dialog; enum { COLUMN_SKIPPABLE = 5, @@ -258,7 +372,7 @@ private: */ Config::drop (); - _frame = new DOMFrame (_("DCP-o-matic KDM Creator")); + _frame = new DOMFrame (_("DCP-o-matic Playlist Editor")); SetTopWindow (_frame); _frame->Maximize (); _frame->Show (); diff --git a/src/wx/content_view.cc b/src/wx/content_view.cc new file mode 100644 index 000000000..4eab80339 --- /dev/null +++ b/src/wx/content_view.cc @@ -0,0 +1,147 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#include "content_view.h" +#include "wx_util.h" +#include "lib/dcpomatic_assert.h" +#include "lib/config.h" +#include "lib/dcp_content.h" +#include "lib/content_factory.h" +#include "lib/examine_content_job.h" +#include "lib/job_manager.h" +#include "lib/cross.h" +#include +#include +#include +#include + +using boost::shared_ptr; +using boost::weak_ptr; +using boost::optional; +using boost::dynamic_pointer_cast; + +ContentView::ContentView (wxWindow* parent, weak_ptr film) + : wxListCtrl (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_NO_HEADER) + , _film (film) +{ + AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80); + /* type */ + AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80); + /* annotation text */ + AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 580); +} + +shared_ptr +ContentView::selected () const +{ + long int s = GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s == -1) { + return shared_ptr(); + } + + DCPOMATIC_ASSERT (s < int(_content.size())); + return _content[s]; +} + +void +ContentView::update () +{ + if (!IsShown()) { + return; + } + + shared_ptr film = _film.lock (); + if (!film) { + return; + } + + using namespace boost::filesystem; + + DeleteAllItems (); + _content.clear (); + optional dir = Config::instance()->player_content_directory(); + if (!dir) { + return; + } + + wxProgressDialog progress (_("DCP-o-matic"), _("Reading content directory")); + JobManager* jm = JobManager::instance (); + + for (directory_iterator i = directory_iterator(*dir); i != directory_iterator(); ++i) { + try { + shared_ptr content; + if (is_directory(*i) && (is_regular_file(*i / "ASSETMAP") || is_regular_file(*i / "ASSETMAP.xml"))) { + content.reset (new DCPContent(film, *i)); + } else if (i->path().extension() == ".mp4" || i->path().extension() == ".ecinema") { + content = content_factory(film, *i).front(); + } + + if (content) { + jm->add (shared_ptr(new ExamineContentJob(film, content))); + while (jm->work_to_do()) { + if (!progress.Pulse()) { + /* user pressed cancel */ + BOOST_FOREACH (shared_ptr i, jm->get()) { + i->cancel(); + } + return; + } + dcpomatic_sleep (1); + } + if (report_errors_from_last_job (this)) { + add (content); + _content.push_back (content); + } + } + } catch (boost::filesystem::filesystem_error& e) { + /* Never mind */ + } catch (dcp::DCPReadError& e) { + /* Never mind */ + } + } +} + +void +ContentView::add (shared_ptr content) +{ + int const N = GetItemCount(); + + wxListItem it; + it.SetId(N); + it.SetColumn(0); + DCPTime length = content->length_after_trim (); + int h, m, s, f; + length.split (24, h, m, s, f); + it.SetText(wxString::Format("%02d:%02d:%02d", h, m, s)); + InsertItem(it); + + shared_ptr dcp = dynamic_pointer_cast(content); + if (dcp && dcp->content_kind()) { + it.SetId(N); + it.SetColumn(1); + it.SetText(std_to_wx(dcp::content_kind_to_string(*dcp->content_kind()))); + SetItem(it); + } + + it.SetId(N); + it.SetColumn(2); + it.SetText(std_to_wx(content->summary())); + SetItem(it); +} diff --git a/src/wx/content_view.h b/src/wx/content_view.h new file mode 100644 index 000000000..0da53f636 --- /dev/null +++ b/src/wx/content_view.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2018 Carl Hetherington + + 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 . + +*/ + +#include +#include +#include +#include + +class Content; +class Film; + +class ContentView : public wxListCtrl +{ +public: + ContentView (wxWindow* parent, boost::weak_ptr film); + + boost::shared_ptr selected () const; + void update (); + +private: + void add (boost::shared_ptr content); + + boost::weak_ptr _film; + std::vector > _content; +}; diff --git a/src/wx/controls.cc b/src/wx/controls.cc index cc0ed25bb..8f1d5abda 100644 --- a/src/wx/controls.cc +++ b/src/wx/controls.cc @@ -23,6 +23,7 @@ #include "wx_util.h" #include "playhead_to_timecode_dialog.h" #include "playhead_to_frame_dialog.h" +#include "content_view.h" #include "lib/job_manager.h" #include "lib/player_video.h" #include "lib/dcp_content.h" @@ -94,13 +95,7 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor _spl_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 740); left_sizer->Add (_spl_view, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP); - _content_view = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_NO_HEADER); - /* time */ - _content_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80); - /* type */ - _content_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 80); - /* annotation text */ - _content_view->AppendColumn (wxT(""), wxLIST_FORMAT_LEFT, 580); + _content_view = new ContentView (this, _film); left_sizer->Add (_content_view, 1, wxALL | wxEXPAND, DCPOMATIC_SIZER_GAP); wxBoxSizer* e_sizer = new wxBoxSizer (wxHORIZONTAL); @@ -200,7 +195,7 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor film_changed (); setup_sensitivity (); - update_content_directory (); + _content_view->update (); update_playlist_directory (); JobManager::instance()->ActiveJobsChanged.connect ( @@ -214,7 +209,7 @@ Controls::Controls (wxWindow* parent, shared_ptr viewer, bool editor void Controls::add_clicked () { - shared_ptr sel = selected_content()->clone(); + shared_ptr sel = _content_view->selected()->clone(); DCPOMATIC_ASSERT (sel); _film->examine_and_add_content (sel); bool const ok = display_progress (_("DCP-o-matic"), _("Loading DCP")); @@ -265,7 +260,7 @@ void Controls::config_changed (int property) { if (property == Config::PLAYER_CONTENT_DIRECTORY) { - update_content_directory (); + _content_view->update (); } else if (property == Config::PLAYER_PLAYLIST_DIRECTORY) { update_playlist_directory (); } else { @@ -510,22 +505,10 @@ Controls::setup_sensitivity () _eye->Enable (c && _film->three_d ()); } - _add_button->Enable (Config::instance()->allow_spl_editing() && static_cast(selected_content())); + _add_button->Enable (Config::instance()->allow_spl_editing() && static_cast(_content_view->selected())); _save_button->Enable (Config::instance()->allow_spl_editing()); } -shared_ptr -Controls::selected_content () const -{ - long int s = _content_view->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (s == -1) { - return shared_ptr(); - } - - DCPOMATIC_ASSERT (s < int(_content.size())); - return _content[s]; -} - void Controls::timecode_clicked () { @@ -585,7 +568,7 @@ Controls::show_extended_player_controls (bool s) _content_view->Show (s); _spl_view->Show (s); if (s) { - update_content_directory (); + _content_view->update (); update_playlist_directory (); } _current_spl_view->Show (s); @@ -636,59 +619,6 @@ Controls::add_playlist_to_list (shared_ptr film) _spl_view->InsertItem (it); } -void -Controls::update_content_directory () -{ - if (!_content_view->IsShown()) { - return; - } - - using namespace boost::filesystem; - - _content_view->DeleteAllItems (); - _content.clear (); - optional dir = Config::instance()->player_content_directory(); - if (!dir) { - return; - } - - wxProgressDialog progress (_("DCP-o-matic"), _("Reading content directory")); - JobManager* jm = JobManager::instance (); - - for (directory_iterator i = directory_iterator(*dir); i != directory_iterator(); ++i) { - try { - shared_ptr content; - if (is_directory(*i) && (is_regular_file(*i / "ASSETMAP") || is_regular_file(*i / "ASSETMAP.xml"))) { - content.reset (new DCPContent(*i)); - } else if (i->path().extension() == ".mp4" || i->path().extension() == ".ecinema") { - content = content_factory(*i).front(); - } - - if (content) { - jm->add (shared_ptr(new ExamineContentJob(_film, content))); - while (jm->work_to_do()) { - if (!progress.Pulse()) { - /* user pressed cancel */ - BOOST_FOREACH (shared_ptr i, jm->get()) { - i->cancel(); - } - return; - } - dcpomatic_sleep (1); - } - if (report_errors_from_last_job (this)) { - add_content_to_list (content, _content_view); - _content.push_back (content); - } - } - } catch (boost::filesystem::filesystem_error& e) { - /* Never mind */ - } catch (dcp::DCPReadError& e) { - /* Never mind */ - } - } -} - void Controls::update_playlist_directory () { diff --git a/src/wx/controls.h b/src/wx/controls.h index d916beaf8..59fe4e5d2 100644 --- a/src/wx/controls.h +++ b/src/wx/controls.h @@ -32,6 +32,7 @@ class Content; class PlayerVideo; class wxToggleButton; class wxListCtrl; +class ContentView; namespace dcp { class CPL; @@ -85,7 +86,6 @@ private: typedef std::pair, boost::filesystem::path> CPL; - boost::shared_ptr selected_content () const; #ifdef DCPOMATIC_VARIANT_SWAROOP void pause_clicked (); void stop_clicked (); @@ -106,14 +106,13 @@ private: wxCheckBox* _outline_content; wxChoice* _eye; wxCheckBox* _jump_to_selected; - wxListCtrl* _content_view; + ContentView* _content_view; wxListCtrl* _spl_view; wxListCtrl* _current_spl_view; wxTextCtrl* _log; wxButton* _add_button; wxButton* _save_button; wxButton* _load_button; - std::vector > _content; std::vector > _playlists; wxSlider* _slider; wxButton* _rewind_button; diff --git a/src/wx/wscript b/src/wx/wscript index 5c026284e..865122a69 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -47,6 +47,7 @@ sources = """ content_panel.cc content_properties_dialog.cc content_sub_panel.cc + content_view.cc controls.cc closed_captions_dialog.cc dcp_panel.cc -- 2.30.2