Merge remote-tracking branch 'origin/master' into 2.0
[dcpomatic.git] / src / wx / content_menu.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <wx/wx.h>
21 #include <wx/dirdlg.h>
22 #include "lib/playlist.h"
23 #include "lib/film.h"
24 #include "lib/image_content.h"
25 #include "lib/content_factory.h"
26 #include "lib/examine_content_job.h"
27 #include "lib/job_manager.h"
28 #include "lib/exceptions.h"
29 #include "lib/dcp_content.h"
30 #include "content_menu.h"
31 #include "repeat_dialog.h"
32 #include "wx_util.h"
33
34 using std::cout;
35 using std::vector;
36 using boost::shared_ptr;
37 using boost::weak_ptr;
38 using boost::dynamic_pointer_cast;
39
40 enum {
41         ID_repeat = 1,
42         ID_join,
43         ID_find_missing,
44         ID_re_examine,
45         ID_kdm,
46         ID_remove
47 };
48
49 ContentMenu::ContentMenu (wxWindow* p)
50         : _menu (new wxMenu)
51         , _parent (p)
52 {
53         _repeat = _menu->Append (ID_repeat, _("Repeat..."));
54         _join = _menu->Append (ID_join, _("Join"));
55         _find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
56         _re_examine = _menu->Append (ID_re_examine, _("Re-examine..."));
57         _kdm = _menu->Append (ID_kdm, _("Add KDM..."));
58         _menu->AppendSeparator ();
59         _remove = _menu->Append (ID_remove, _("Remove"));
60
61         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat);
62         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::join, this), ID_join);
63         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing);
64         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::re_examine, this), ID_re_examine);
65         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::kdm, this), ID_kdm);
66         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove);
67 }
68
69 ContentMenu::~ContentMenu ()
70 {
71         delete _menu;
72 }
73
74 void
75 ContentMenu::popup (weak_ptr<Film> f, ContentList c, wxPoint p)
76 {
77         _film = f;
78         _content = c;
79         _repeat->Enable (!_content.empty ());
80
81         int n = 0;
82         for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
83                 if (dynamic_pointer_cast<FFmpegContent> (*i)) {
84                         ++n;
85                 }
86         }
87         
88         _join->Enable (n > 1);
89         
90         _find_missing->Enable (_content.size() == 1 && !_content.front()->paths_valid ());
91         _re_examine->Enable (!_content.empty ());
92
93         if (_content.size() == 1) {
94                 shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
95                 _kdm->Enable (dcp && dcp->encrypted ());
96         } else {
97                 _kdm->Enable (false);
98         }
99         
100         _remove->Enable (!_content.empty ());
101         _parent->PopupMenu (_menu, p);
102 }
103
104 void
105 ContentMenu::repeat ()
106 {
107         if (_content.empty ()) {
108                 return;
109         }
110                 
111         RepeatDialog* d = new RepeatDialog (_parent);
112         if (d->ShowModal() != wxID_OK) {
113                 d->Destroy ();
114                 return;
115         }
116
117         shared_ptr<const Film> film = _film.lock ();
118         if (!film) {
119                 return;
120         }
121
122         film->playlist()->repeat (_content, d->number ());
123         d->Destroy ();
124
125         _content.clear ();
126 }
127
128 void
129 ContentMenu::join ()
130 {
131         vector<shared_ptr<Content> > fc;
132         for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
133                 shared_ptr<FFmpegContent> f = dynamic_pointer_cast<FFmpegContent> (*i);
134                 if (f) {
135                         fc.push_back (f);
136                 }
137         }
138
139         assert (fc.size() > 1);
140
141         shared_ptr<Film> film = _film.lock ();
142         if (!film) {
143                 return;
144         }
145
146         try {
147                 shared_ptr<FFmpegContent> joined (new FFmpegContent (film, fc));
148                 for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
149                         film->remove_content (*i);
150                 }
151                 film->add_content (joined);
152         } catch (JoinError& e) {
153                 error_dialog (_parent, std_to_wx (e.what ()));
154         }
155 }
156
157 void
158 ContentMenu::remove ()
159 {
160         if (_content.empty ()) {
161                 return;
162         }
163
164         shared_ptr<const Film> film = _film.lock ();
165         if (!film) {
166                 return;
167         }
168
169         film->playlist()->remove (_content);
170
171         _content.clear ();
172 }
173
174 void
175 ContentMenu::find_missing ()
176 {
177         if (_content.size() != 1) {
178                 return;
179         }
180
181         shared_ptr<const Film> film = _film.lock ();
182         if (!film) {
183                 return;
184         }
185         
186         shared_ptr<Content> content;
187
188         /* XXX: a bit nasty */
189         shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (_content.front ());
190         if (ic && !ic->still ()) {
191                 wxDirDialog* d = new wxDirDialog (0, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
192                 int const r = d->ShowModal ();
193                 if (r == wxID_OK) {
194                         content.reset (new ImageContent (film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
195                 }
196                 d->Destroy ();
197         } else {
198                 wxFileDialog* d = new wxFileDialog (0, _("Choose a file"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
199                 int const r = d->ShowModal ();
200                 if (r == wxID_OK) {
201                         content = content_factory (film, wx_to_std (d->GetPath ()));
202                 }
203                 d->Destroy ();
204         }
205
206         if (!content) {
207                 return;
208         }
209
210         shared_ptr<Job> j (new ExamineContentJob (film, content));
211         
212         j->Finished.connect (
213                 bind (
214                         &ContentMenu::maybe_found_missing,
215                         this,
216                         boost::weak_ptr<Job> (j),
217                         boost::weak_ptr<Content> (_content.front ()),
218                         boost::weak_ptr<Content> (content)
219                         )
220                 );
221         
222         JobManager::instance()->add (j);
223 }
224
225 void
226 ContentMenu::re_examine ()
227 {
228         shared_ptr<Film> film = _film.lock ();
229         if (!film) {
230                 return;
231         }
232
233         for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
234                 film->examine_content (*i);
235         }
236 }
237
238 void
239 ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_ptr<Content> nc)
240 {
241         shared_ptr<Job> job = j.lock ();
242         if (!job || !job->finished_ok ()) {
243                 return;
244         }
245
246         shared_ptr<Content> old_content = oc.lock ();
247         shared_ptr<Content> new_content = nc.lock ();
248         assert (old_content);
249         assert (new_content);
250
251         if (new_content->digest() != old_content->digest()) {
252                 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."));
253                 return;
254         }
255
256         old_content->set_path (new_content->path (0));
257 }
258
259 void
260 ContentMenu::kdm ()
261 {
262         assert (!_content.empty ());
263         shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
264         assert (dcp);
265         
266         wxFileDialog* d = new wxFileDialog (_parent, _("Select KDM"));
267                 
268         if (d->ShowModal() == wxID_OK) {
269                 dcp->add_kdm (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()))));
270                 shared_ptr<Film> film = _film.lock ();
271                 assert (film);
272                 film->examine_content (dcp);
273         }
274         
275         d->Destroy ();
276 }