2 Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
21 #include "content_menu.h"
22 #include "repeat_dialog.h"
24 #include "timeline_video_content_view.h"
25 #include "timeline_audio_content_view.h"
26 #include "content_properties_dialog.h"
27 #include "lib/playlist.h"
29 #include "lib/image_content.h"
30 #include "lib/content_factory.h"
31 #include "lib/examine_content_job.h"
32 #include "lib/job_manager.h"
33 #include "lib/exceptions.h"
34 #include "lib/dcp_content.h"
35 #include "lib/ffmpeg_content.h"
36 #include "lib/audio_content.h"
38 #include <wx/dirdlg.h>
39 #include <boost/foreach.hpp>
40 #include <boost/make_shared.hpp>
46 using boost::shared_ptr;
47 using boost::make_shared;
48 using boost::weak_ptr;
49 using boost::dynamic_pointer_cast;
61 ContentMenu::ContentMenu (wxWindow* p)
65 _repeat = _menu->Append (ID_repeat, _("Repeat..."));
66 _join = _menu->Append (ID_join, _("Join"));
67 _find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
68 _properties = _menu->Append (ID_properties, _("Properties..."));
69 _re_examine = _menu->Append (ID_re_examine, _("Re-examine..."));
70 _kdm = _menu->Append (ID_kdm, _("Add KDM..."));
71 _menu->AppendSeparator ();
72 _remove = _menu->Append (ID_remove, _("Remove"));
74 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat);
75 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::join, this), ID_join);
76 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing);
77 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::properties, this), ID_properties);
78 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::re_examine, this), ID_re_examine);
79 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::kdm, this), ID_kdm);
80 _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove);
83 ContentMenu::~ContentMenu ()
89 ContentMenu::popup (weak_ptr<Film> film, ContentList c, TimelineContentViewList v, wxPoint p)
94 _repeat->Enable (!_content.empty ());
97 BOOST_FOREACH (shared_ptr<Content> i, _content) {
98 if (dynamic_pointer_cast<FFmpegContent> (i)) {
103 _join->Enable (n > 1);
105 _find_missing->Enable (_content.size() == 1 && !_content.front()->paths_valid ());
106 _properties->Enable (_content.size() == 1);
107 _re_examine->Enable (!_content.empty ());
109 if (_content.size() == 1) {
110 shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
111 _kdm->Enable (dcp && dcp->encrypted ());
113 _kdm->Enable (false);
116 _remove->Enable (!_content.empty ());
117 _parent->PopupMenu (_menu, p);
121 ContentMenu::repeat ()
123 if (_content.empty ()) {
127 RepeatDialog* d = new RepeatDialog (_parent);
128 if (d->ShowModal() != wxID_OK) {
133 shared_ptr<Film> film = _film.lock ();
138 film->repeat_content (_content, d->number ());
148 vector<shared_ptr<Content> > fc;
149 BOOST_FOREACH (shared_ptr<Content> i, _content) {
150 shared_ptr<FFmpegContent> f = dynamic_pointer_cast<FFmpegContent> (i);
156 DCPOMATIC_ASSERT (fc.size() > 1);
158 shared_ptr<Film> film = _film.lock ();
164 shared_ptr<FFmpegContent> joined = boost::make_shared<FFmpegContent> (film, fc);
165 BOOST_FOREACH (shared_ptr<Content> i, _content) {
166 film->remove_content (i);
168 film->add_content (joined);
169 } catch (JoinError& e) {
170 error_dialog (_parent, std_to_wx (e.what ()));
175 ContentMenu::remove ()
177 if (_content.empty ()) {
181 shared_ptr<Film> film = _film.lock ();
186 /* We are removing from the timeline if _views is not empty */
187 bool handled = false;
188 if (!_views.empty ()) {
189 /* Special case: we only remove FFmpegContent if its video view is selected;
190 if not, and its audio view is selected, we unmap the audio.
192 BOOST_FOREACH (shared_ptr<Content> i, _content) {
193 shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (i);
198 shared_ptr<TimelineVideoContentView> video;
199 shared_ptr<TimelineAudioContentView> audio;
201 BOOST_FOREACH (shared_ptr<TimelineContentView> j, _views) {
202 shared_ptr<TimelineVideoContentView> v = dynamic_pointer_cast<TimelineVideoContentView> (j);
203 shared_ptr<TimelineAudioContentView> a = dynamic_pointer_cast<TimelineAudioContentView> (j);
204 if (v && v->content() == fc) {
206 } else if (a && a->content() == fc) {
211 if (!video && audio) {
212 AudioMapping m = fc->audio->mapping ();
214 fc->audio->set_mapping (m);
221 film->remove_content (_content);
229 ContentMenu::find_missing ()
231 if (_content.size() != 1) {
235 shared_ptr<const Film> film = _film.lock ();
240 shared_ptr<Content> content;
242 /* XXX: a bit nasty */
243 shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (_content.front ());
244 shared_ptr<DCPContent> dc = dynamic_pointer_cast<DCPContent> (_content.front ());
247 boost::filesystem::path path;
249 if ((ic && !ic->still ()) || dc) {
250 wxDirDialog* d = new wxDirDialog (0, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
252 path = wx_to_std (d->GetPath ());
255 wxFileDialog* d = new wxFileDialog (0, _("Choose a file"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
257 path = wx_to_std (d->GetPath ());
262 content = content_factory (film, path);
269 shared_ptr<Job> j = make_shared<ExamineContentJob> (film, content);
271 _job_connection = j->Finished.connect (
273 &ContentMenu::maybe_found_missing,
275 boost::weak_ptr<Job> (j),
276 boost::weak_ptr<Content> (_content.front ()),
277 boost::weak_ptr<Content> (content)
281 JobManager::instance()->add (j);
285 ContentMenu::re_examine ()
287 shared_ptr<Film> film = _film.lock ();
292 BOOST_FOREACH (shared_ptr<Content> i, _content) {
293 film->examine_content (i);
298 ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_ptr<Content> nc)
300 shared_ptr<Job> job = j.lock ();
301 if (!job || !job->finished_ok ()) {
305 shared_ptr<Content> old_content = oc.lock ();
306 shared_ptr<Content> new_content = nc.lock ();
307 DCPOMATIC_ASSERT (old_content);
308 DCPOMATIC_ASSERT (new_content);
310 if (new_content->digest() != old_content->digest()) {
311 error_dialog (0, _("The content file(s) you specified are not the same as those that are missing. Either try again with the correct content file or remove the missing content."));
315 old_content->set_path (new_content->path (0));
321 DCPOMATIC_ASSERT (!_content.empty ());
322 shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
323 DCPOMATIC_ASSERT (dcp);
325 wxFileDialog* d = new wxFileDialog (_parent, _("Select KDM"));
327 if (d->ShowModal() == wxID_OK) {
329 dcp->add_kdm (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()))));
330 } catch (exception& e) {
331 error_dialog (_parent, wxString::Format (_("Could not load KDM (%s)"), e.what ()));
336 shared_ptr<Film> film = _film.lock ();
337 DCPOMATIC_ASSERT (film);
338 film->examine_content (dcp);
345 ContentMenu::properties ()
347 ContentPropertiesDialog* d = new ContentPropertiesDialog (_parent, _content.front ());