X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Ftools%2Fdcpomatic_playlist.cc;h=0b8f935b3aaf0d8dc02ba1b3e7dcd0599b092e10;hb=a72e28935d88d569a64f3d08a8791b368dda11d7;hp=889ed6b1701826d082d67ecf44467beaf8ac4d83;hpb=592e22afa11706f61f39eeb038be56adea09cbc8;p=dcpomatic.git diff --git a/src/tools/dcpomatic_playlist.cc b/src/tools/dcpomatic_playlist.cc index 889ed6b17..0b8f935b3 100644 --- a/src/tools/dcpomatic_playlist.cc +++ b/src/tools/dcpomatic_playlist.cc @@ -20,19 +20,70 @@ #include "../wx/wx_util.h" #include "../wx/wx_signal_manager.h" +#include "../wx/content_view.h" +#include "../wx/dcpomatic_button.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 using std::exception; +using std::cout; +using std::string; +using boost::optional; +using boost::shared_ptr; +using boost::weak_ptr; +using boost::bind; +using boost::dynamic_pointer_cast; + +class ContentDialog : public wxDialog, public ContentStore +{ +public: + ContentDialog (wxWindow* parent) + : wxDialog (parent, wxID_ANY, _("Add content"), wxDefaultPosition, wxSize(800, 640)) + , _content_view (new ContentView(this)) + { + _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 (); + } + + shared_ptr get (string digest) const + { + return _content_view->get (digest); + } + +private: + ContentView* _content_view; +}; class DOMFrame : public wxFrame { public: explicit DOMFrame (wxString const & title) : wxFrame (0, -1, title) + , _content_dialog (new ContentDialog(this)) { /* Use a panel as the only child of the Frame so that we avoid the dark-grey background on Windows. @@ -45,46 +96,231 @@ public: ); _list->AppendColumn (_("Name"), wxLIST_FORMAT_LEFT, 400); - _list->AppendColumn (_("CPL"), wxLIST_FORMAT_LEFT, 400); - _list->AppendColumn (_("Type"), wxLIST_FORMAT_LEFT, 75); - _list->AppendColumn (_("Format"), wxLIST_FORMAT_LEFT, 75); - _list->AppendColumn (_("Encrypted"), wxLIST_FORMAT_LEFT, 90); - _list->AppendColumn (_("Skippable"), wxLIST_FORMAT_LEFT, 90); - _list->AppendColumn (_("Disable timeline"), wxLIST_FORMAT_LEFT, 125); - _list->AppendColumn (_("Stop after play"), wxLIST_FORMAT_LEFT, 125); - - /* + _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); + _list->AppendColumn (_("Disable timeline"), wxLIST_FORMAT_CENTRE, 125); + _list->AppendColumn (_("Stop after play"), wxLIST_FORMAT_CENTRE, 125); + wxImageList* images = new wxImageList (16, 16); - wxIcon icon; - icon.LoadFile ("test.png", wxBITMAP_TYPE_PNG); - images->Add (icon); - _list->SetImageList (images, wxIMAGE_LIST_SMALL); - */ + wxIcon tick_icon; + wxIcon no_tick_icon; +#ifdef DCPOMATIX_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())); + boost::filesystem::path no_tick_path = shared_path() / "no_tick.png"; + no_tick_icon.LoadFile (std_to_wx(no_tick_path.string())); +#endif + images->Add (tick_icon); + images->Add (no_tick_icon); - wxListItem item; - item.SetId (0); - item.SetImage (0); - _list->InsertItem (item); + _list->SetImageList (images, wxIMAGE_LIST_SMALL); main_sizer->Add (_list, 1, wxEXPAND | wxALL, DCPOMATIC_SIZER_GAP); wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL); - _up = new wxButton (overall_panel, wxID_ANY, _("Up")); - button_sizer->Add (_up, 0, wxALL, DCPOMATIC_SIZER_GAP); + _up = new Button (overall_panel, _("Up")); + _down = new Button (overall_panel, _("Down")); + _add = new Button (overall_panel, _("Add")); + _remove = new Button (overall_panel, _("Remove")); + _save = new Button (overall_panel, _("Save playlist")); + _load = new Button (overall_panel, _("Load playlist")); + button_sizer->Add (_up, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_down, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_add, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_remove, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_save, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); + button_sizer->Add (_load, 0, wxEXPAND | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP); main_sizer->Add (button_sizer, 0, wxALL, DCPOMATIC_SIZER_GAP); overall_panel->SetSizer (main_sizer); + _list->Bind (wxEVT_LEFT_DOWN, bind(&DOMFrame::list_left_click, this, _1)); + _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)); + _save->Bind (wxEVT_BUTTON, bind(&DOMFrame::save_clicked, this)); + _load->Bind (wxEVT_BUTTON, bind(&DOMFrame::load_clicked, this)); + + setup_sensitivity (); + } + +private: + + void add (SPLEntry e) + { + wxListItem item; + item.SetId (_list->GetItemCount()); + long const N = _list->InsertItem (item); + set_item (N, e); + } + + void selection_changed () + { setup_sensitivity (); } + 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 == 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); + _list->SetItem (N, COLUMN_STOP_AFTER_PLAY, wxEmptyString, e.stop_after_play ? 0 : 1); + } + void setup_sensitivity () { + 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 != -1 && selected < (_list->GetItemCount() - 1)); + _remove->Enable (num_selected > 0); + } + + void list_left_click (wxMouseEvent& ev) + { + int flags; + long item = _list->HitTest (ev.GetPosition(), flags, 0); + int x = ev.GetPosition().x; + optional column; + for (int i = 0; i < _list->GetColumnCount(); ++i) { + x -= _list->GetColumnWidth (i); + if (x < 0) { + column = i; + break; + } + } + + if (item != -1 && column) { + switch (*column) { + case COLUMN_SKIPPABLE: + _playlist[item].skippable = !_playlist[item].skippable; + break; + case COLUMN_DISABLE_TIMELINE: + _playlist[item].disable_timeline = !_playlist[item].disable_timeline; + break; + case COLUMN_STOP_AFTER_PLAY: + _playlist[item].stop_after_play = !_playlist[item].stop_after_play; + break; + default: + ev.Skip (); + } + set_item (item, _playlist[item]); + } else { + ev.Skip (); + } + } + + void add_clicked () + { + int const r = _content_dialog->ShowModal (); + if (r == wxID_OK) { + shared_ptr content = _content_dialog->selected (); + if (content) { + SPLEntry e (content); + add (e); + _playlist.add (e); + } + } + } + + void up_clicked () + { + long int s = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s < 1) { + return; + } + + SPLEntry 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; + } + + SPLEntry 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.remove (s); + _list->DeleteItem (s); + } + void save_clicked () + { + Config* c = Config::instance (); + wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); + wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml"), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + if (d->ShowModal() == wxID_OK) { + _playlist.write (wx_to_std(d->GetPath())); + } + } + + void load_clicked () + { + Config* c = Config::instance (); + wxString default_dir = c->player_playlist_directory() ? std_to_wx(c->player_playlist_directory()->string()) : wxString(wxEmptyString); + wxFileDialog* d = new wxFileDialog (this, _("Select playlist file"), default_dir, wxEmptyString, wxT("XML files (*.xml)|*.xml")); + if (d->ShowModal() == wxID_OK) { + _list->DeleteAllItems (); + _playlist.read (wx_to_std(d->GetPath()), _content_dialog); + if (!_playlist.missing()) { + _list->DeleteAllItems (); + BOOST_FOREACH (SPLEntry i, _playlist.get()) { + add (i); + } + } else { + error_dialog (this, _("Some content in this playlist was not found.")); + } + } } wxListCtrl* _list; wxButton* _up; + wxButton* _down; + wxButton* _add; + wxButton* _remove; + wxButton* _save; + wxButton* _load; + SPL _playlist; + ContentDialog* _content_dialog; + + enum { + COLUMN_SKIPPABLE = 5, + COLUMN_DISABLE_TIMELINE = 6, + COLUMN_STOP_AFTER_PLAY = 7 + }; }; /** @class App @@ -139,7 +375,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 ();