Merge master.
[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_kdm,
45         ID_remove
46 };
47
48 ContentMenu::ContentMenu (wxWindow* p)
49         : _menu (new wxMenu)
50         , _parent (p)
51 {
52         _repeat = _menu->Append (ID_repeat, _("Repeat..."));
53         _join = _menu->Append (ID_join, _("Join"));
54         _find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
55         _kdm = _menu->Append (ID_kdm, _("Add KDM..."));
56         _menu->AppendSeparator ();
57         _remove = _menu->Append (ID_remove, _("Remove"));
58
59         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat);
60         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::join, this), ID_join);
61         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing);
62         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::kdm, this), ID_kdm);
63         _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove);
64 }
65
66 ContentMenu::~ContentMenu ()
67 {
68         delete _menu;
69 }
70
71 void
72 ContentMenu::popup (weak_ptr<Film> f, ContentList c, wxPoint p)
73 {
74         _film = f;
75         _content = c;
76         _repeat->Enable (!_content.empty ());
77
78         int n = 0;
79         for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
80                 if (dynamic_pointer_cast<FFmpegContent> (*i)) {
81                         ++n;
82                 }
83         }
84         
85         _join->Enable (n > 1);
86         
87         _find_missing->Enable (_content.size() == 1 && !_content.front()->paths_valid ());
88
89         if (_content.size() == 1) {
90                 shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
91                 _kdm->Enable (dcp && dcp->encrypted ());
92         } else {
93                 _kdm->Enable (false);
94         }
95         
96         _remove->Enable (!_content.empty ());
97         _parent->PopupMenu (_menu, p);
98 }
99
100 void
101 ContentMenu::repeat ()
102 {
103         if (_content.empty ()) {
104                 return;
105         }
106                 
107         RepeatDialog* d = new RepeatDialog (_parent);
108         if (d->ShowModal() != wxID_OK) {
109                 d->Destroy ();
110                 return;
111         }
112
113         shared_ptr<const Film> film = _film.lock ();
114         if (!film) {
115                 return;
116         }
117
118         film->playlist()->repeat (_content, d->number ());
119         d->Destroy ();
120
121         _content.clear ();
122 }
123
124 void
125 ContentMenu::join ()
126 {
127         vector<shared_ptr<Content> > fc;
128         for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
129                 shared_ptr<FFmpegContent> f = dynamic_pointer_cast<FFmpegContent> (*i);
130                 if (f) {
131                         fc.push_back (f);
132                 }
133         }
134
135         assert (fc.size() > 1);
136
137         shared_ptr<Film> film = _film.lock ();
138         if (!film) {
139                 return;
140         }
141
142         try {
143                 shared_ptr<FFmpegContent> joined (new FFmpegContent (film, fc));
144                 for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
145                         film->remove_content (*i);
146                 }
147                 film->add_content (joined);
148         } catch (JoinError& e) {
149                 error_dialog (_parent, std_to_wx (e.what ()));
150         }
151 }
152
153 void
154 ContentMenu::remove ()
155 {
156         if (_content.empty ()) {
157                 return;
158         }
159
160         shared_ptr<const Film> film = _film.lock ();
161         if (!film) {
162                 return;
163         }
164
165         film->playlist()->remove (_content);
166
167         _content.clear ();
168 }
169
170 void
171 ContentMenu::find_missing ()
172 {
173         if (_content.size() != 1) {
174                 return;
175         }
176
177         shared_ptr<const Film> film = _film.lock ();
178         if (!film) {
179                 return;
180         }
181         
182         shared_ptr<Content> content;
183
184         /* XXX: a bit nasty */
185         shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (_content.front ());
186         if (ic && !ic->still ()) {
187                 wxDirDialog* d = new wxDirDialog (0, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
188                 int const r = d->ShowModal ();
189                 if (r == wxID_OK) {
190                         content.reset (new ImageContent (film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
191                 }
192                 d->Destroy ();
193         } else {
194                 wxFileDialog* d = new wxFileDialog (0, _("Choose a file"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE);
195                 int const r = d->ShowModal ();
196                 if (r == wxID_OK) {
197                         content = content_factory (film, wx_to_std (d->GetPath ()));
198                 }
199                 d->Destroy ();
200         }
201
202         if (!content) {
203                 return;
204         }
205
206         shared_ptr<Job> j (new ExamineContentJob (film, content));
207         
208         j->Finished.connect (
209                 bind (
210                         &ContentMenu::maybe_found_missing,
211                         this,
212                         boost::weak_ptr<Job> (j),
213                         boost::weak_ptr<Content> (_content.front ()),
214                         boost::weak_ptr<Content> (content)
215                         )
216                 );
217         
218         JobManager::instance()->add (j);
219 }
220
221 void
222 ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_ptr<Content> nc)
223 {
224         shared_ptr<Job> job = j.lock ();
225         if (!job || !job->finished_ok ()) {
226                 return;
227         }
228
229         shared_ptr<Content> old_content = oc.lock ();
230         shared_ptr<Content> new_content = nc.lock ();
231         assert (old_content);
232         assert (new_content);
233
234         if (new_content->digest() != old_content->digest()) {
235                 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."));
236                 return;
237         }
238
239         old_content->set_path (new_content->path (0));
240 }
241
242 void
243 ContentMenu::kdm ()
244 {
245         assert (!_content.empty ());
246         shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
247         assert (dcp);
248         
249         wxFileDialog* d = new wxFileDialog (_parent, _("Select KDM"));
250                 
251         if (d->ShowModal() == wxID_OK) {
252                 dcp->add_kdm (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()))));
253                 shared_ptr<Film> film = _film.lock ();
254                 assert (film);
255                 film->examine_content (dcp);
256         }
257         
258         d->Destroy ();
259 }