Fix sensitivity of 'refer' button in the subtitle panel.
[dcpomatic.git] / src / wx / subtitle_panel.cc
1 /*
2     Copyright (C) 2012-2015 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 "subtitle_panel.h"
21 #include "film_editor.h"
22 #include "wx_util.h"
23 #include "subtitle_view.h"
24 #include "content_panel.h"
25 #include "fonts_dialog.h"
26 #include "lib/ffmpeg_content.h"
27 #include "lib/subrip_content.h"
28 #include "lib/ffmpeg_subtitle_stream.h"
29 #include "lib/dcp_subtitle_content.h"
30 #include "lib/subrip_decoder.h"
31 #include "lib/dcp_subtitle_decoder.h"
32 #include "lib/dcp_content.h"
33 #include <wx/spinctrl.h>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/foreach.hpp>
36
37 using std::vector;
38 using std::string;
39 using boost::shared_ptr;
40 using boost::lexical_cast;
41 using boost::dynamic_pointer_cast;
42
43 SubtitlePanel::SubtitlePanel (ContentPanel* p)
44         : ContentSubPanel (p, _("Subtitles"))
45         , _subtitle_view (0)
46         , _fonts_dialog (0)
47 {
48         wxFlexGridSizer* grid = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
49         _sizer->Add (grid, 0, wxALL, 8);
50
51         _reference = new wxCheckBox (this, wxID_ANY, _("Refer to existing DCP"));
52         grid->Add (_reference);
53         grid->AddSpacer (0);
54
55         _use = new wxCheckBox (this, wxID_ANY, _("Use subtitles"));
56         grid->Add (_use);
57         grid->AddSpacer (0);
58
59         _burn = new wxCheckBox (this, wxID_ANY, _("Burn subtitles into image"));
60         grid->Add (_burn);
61         grid->AddSpacer (0);
62
63         {
64                 add_label_to_sizer (grid, this, _("X Offset"), true);
65                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
66                 _x_offset = new wxSpinCtrl (this);
67                 s->Add (_x_offset);
68                 add_label_to_sizer (s, this, _("%"), false);
69                 grid->Add (s);
70         }
71
72         {
73                 add_label_to_sizer (grid, this, _("Y Offset"), true);
74                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
75                 _y_offset = new wxSpinCtrl (this);
76                 s->Add (_y_offset);
77                 add_label_to_sizer (s, this, _("%"), false);
78                 grid->Add (s);
79         }
80
81         {
82                 add_label_to_sizer (grid, this, _("X Scale"), true);
83                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
84                 _x_scale = new wxSpinCtrl (this);
85                 s->Add (_x_scale);
86                 add_label_to_sizer (s, this, _("%"), false);
87                 grid->Add (s);
88         }
89
90         {
91                 add_label_to_sizer (grid, this, _("Y Scale"), true);
92                 wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
93                 _y_scale = new wxSpinCtrl (this);
94                 s->Add (_y_scale);
95                 add_label_to_sizer (s, this, _("%"), false);
96                 grid->Add (s);
97         }
98
99         add_label_to_sizer (grid, this, _("Language"), true);
100         _language = new wxTextCtrl (this, wxID_ANY);
101         grid->Add (_language, 1, wxEXPAND);
102
103         add_label_to_sizer (grid, this, _("Stream"), true);
104         _stream = new wxChoice (this, wxID_ANY);
105         grid->Add (_stream, 1, wxEXPAND);
106
107         _subtitle_view_button = new wxButton (this, wxID_ANY, _("View..."));
108         grid->Add (_subtitle_view_button);
109         grid->AddSpacer (0);
110
111         _fonts_dialog_button = new wxButton (this, wxID_ANY, _("Fonts..."));
112         grid->Add (_fonts_dialog_button);
113         grid->AddSpacer (0);
114
115         _x_offset->SetRange (-100, 100);
116         _y_offset->SetRange (-100, 100);
117         _x_scale->SetRange (10, 1000);
118         _y_scale->SetRange (10, 1000);
119
120         _reference->Bind            (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::reference_clicked, this));
121         _use->Bind                  (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::use_toggled, this));
122         _burn->Bind                 (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::burn_toggled, this));
123         _x_offset->Bind             (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_offset_changed, this));
124         _y_offset->Bind             (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this));
125         _x_scale->Bind              (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_scale_changed, this));
126         _y_scale->Bind              (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_scale_changed, this));
127         _language->Bind             (wxEVT_COMMAND_TEXT_UPDATED,     boost::bind (&SubtitlePanel::language_changed, this));
128         _stream->Bind               (wxEVT_COMMAND_CHOICE_SELECTED,  boost::bind (&SubtitlePanel::stream_changed, this));
129         _subtitle_view_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED,   boost::bind (&SubtitlePanel::subtitle_view_clicked, this));
130         _fonts_dialog_button->Bind  (wxEVT_COMMAND_BUTTON_CLICKED,   boost::bind (&SubtitlePanel::fonts_dialog_clicked, this));
131 }
132
133 void
134 SubtitlePanel::film_changed (Film::Property property)
135 {
136         if (property == Film::CONTENT) {
137                 setup_sensitivity ();
138         }
139 }
140
141 void
142 SubtitlePanel::film_content_changed (int property)
143 {
144         FFmpegContentList fc = _parent->selected_ffmpeg ();
145         SubtitleContentList sc = _parent->selected_subtitle ();
146
147         shared_ptr<FFmpegContent> fcs;
148         if (fc.size() == 1) {
149                 fcs = fc.front ();
150         }
151
152         shared_ptr<SubtitleContent> scs;
153         if (sc.size() == 1) {
154                 scs = sc.front ();
155         }
156
157         if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
158                 _stream->Clear ();
159                 if (fcs) {
160                         vector<shared_ptr<FFmpegSubtitleStream> > s = fcs->subtitle_streams ();
161                         for (vector<shared_ptr<FFmpegSubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
162                                 _stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx ((*i)->identifier ())));
163                         }
164
165                         if (fcs->subtitle_stream()) {
166                                 checked_set (_stream, fcs->subtitle_stream()->identifier ());
167                         } else {
168                                 _stream->SetSelection (wxNOT_FOUND);
169                         }
170                 }
171                 setup_sensitivity ();
172         } else if (property == SubtitleContentProperty::USE_SUBTITLES) {
173                 checked_set (_use, scs ? scs->use_subtitles() : false);
174                 setup_sensitivity ();
175         } else if (property == SubtitleContentProperty::BURN_SUBTITLES) {
176                 checked_set (_burn, scs ? scs->burn_subtitles() : false);
177         } else if (property == SubtitleContentProperty::SUBTITLE_X_OFFSET) {
178                 checked_set (_x_offset, scs ? (scs->subtitle_x_offset() * 100) : 0);
179         } else if (property == SubtitleContentProperty::SUBTITLE_Y_OFFSET) {
180                 checked_set (_y_offset, scs ? (scs->subtitle_y_offset() * 100) : 0);
181         } else if (property == SubtitleContentProperty::SUBTITLE_X_SCALE) {
182                 checked_set (_x_scale, scs ? lrint (scs->subtitle_x_scale() * 100) : 100);
183         } else if (property == SubtitleContentProperty::SUBTITLE_Y_SCALE) {
184                 checked_set (_y_scale, scs ? lrint (scs->subtitle_y_scale() * 100) : 100);
185         } else if (property == SubtitleContentProperty::SUBTITLE_LANGUAGE) {
186                 checked_set (_language, scs ? scs->subtitle_language() : "");
187         } else if (property == DCPContentProperty::REFERENCE_SUBTITLE) {
188                 if (scs) {
189                         shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (scs);
190                         checked_set (_reference, dcp ? dcp->reference_subtitle () : false);
191                 } else {
192                         checked_set (_reference, false);
193                 }
194
195                 setup_sensitivity ();
196         }
197 }
198
199 void
200 SubtitlePanel::use_toggled ()
201 {
202         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
203                 i->set_use_subtitles (_use->GetValue());
204         }
205 }
206
207 void
208 SubtitlePanel::burn_toggled ()
209 {
210         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
211                 i->set_burn_subtitles (_burn->GetValue());
212         }
213 }
214
215 void
216 SubtitlePanel::setup_sensitivity ()
217 {
218         int any_subs = 0;
219         int ffmpeg_subs = 0;
220         int subrip_or_dcp_subs = 0;
221         int image_subs = 0;
222         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
223                 shared_ptr<const FFmpegContent> fc = boost::dynamic_pointer_cast<const FFmpegContent> (i);
224                 shared_ptr<const SubRipContent> sc = boost::dynamic_pointer_cast<const SubRipContent> (i);
225                 shared_ptr<const DCPSubtitleContent> dsc = boost::dynamic_pointer_cast<const DCPSubtitleContent> (i);
226                 if (fc) {
227                         if (fc->has_subtitles ()) {
228                                 ++ffmpeg_subs;
229                                 ++any_subs;
230                         }
231                 } else if (sc || dsc) {
232                         ++subrip_or_dcp_subs;
233                         ++any_subs;
234                 } else {
235                         ++any_subs;
236                 }
237
238                 if (i->has_image_subtitles ()) {
239                         ++image_subs;
240                         /* We must burn image subtitles at the moment */
241                         i->set_burn_subtitles (true);
242                 }
243         }
244
245         bool const reference = _reference->GetValue ();
246
247         _use->Enable (!reference && any_subs > 0);
248         bool const use = _use->GetValue ();
249         _burn->Enable (!reference && any_subs > 0 && use && image_subs == 0);
250         _x_offset->Enable (!reference && any_subs > 0 && use);
251         _y_offset->Enable (!reference && any_subs > 0 && use);
252         _x_scale->Enable (!reference && any_subs > 0 && use);
253         _y_scale->Enable (!reference && any_subs > 0 && use);
254         _language->Enable (!reference && any_subs > 0 && use);
255         _stream->Enable (!reference && ffmpeg_subs == 1);
256         _subtitle_view_button->Enable (!reference && subrip_or_dcp_subs == 1);
257         _fonts_dialog_button->Enable (!reference && subrip_or_dcp_subs == 1);
258
259         ContentList sel = _parent->selected ();
260         _reference->Enable (sel.size() == 1 && dynamic_pointer_cast<DCPContent> (sel.front ()));
261 }
262
263 void
264 SubtitlePanel::stream_changed ()
265 {
266         FFmpegContentList fc = _parent->selected_ffmpeg ();
267         if (fc.size() != 1) {
268                 return;
269         }
270
271         shared_ptr<FFmpegContent> fcs = fc.front ();
272
273         vector<shared_ptr<FFmpegSubtitleStream> > a = fcs->subtitle_streams ();
274         vector<shared_ptr<FFmpegSubtitleStream> >::iterator i = a.begin ();
275         string const s = string_client_data (_stream->GetClientObject (_stream->GetSelection ()));
276         while (i != a.end() && (*i)->identifier () != s) {
277                 ++i;
278         }
279
280         if (i != a.end ()) {
281                 fcs->set_subtitle_stream (*i);
282         }
283 }
284
285 void
286 SubtitlePanel::x_offset_changed ()
287 {
288         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
289                 i->set_subtitle_x_offset (_x_offset->GetValue() / 100.0);
290         }
291 }
292
293 void
294 SubtitlePanel::y_offset_changed ()
295 {
296         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
297                 i->set_subtitle_y_offset (_y_offset->GetValue() / 100.0);
298         }
299 }
300
301 void
302 SubtitlePanel::x_scale_changed ()
303 {
304         SubtitleContentList c = _parent->selected_subtitle ();
305         if (c.size() == 1) {
306                 c.front()->set_subtitle_x_scale (_x_scale->GetValue() / 100.0);
307         }
308 }
309
310 void
311 SubtitlePanel::y_scale_changed ()
312 {
313         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
314                 i->set_subtitle_y_scale (_y_scale->GetValue() / 100.0);
315         }
316 }
317
318 void
319 SubtitlePanel::language_changed ()
320 {
321         BOOST_FOREACH (shared_ptr<SubtitleContent> i, _parent->selected_subtitle ()) {
322                 i->set_subtitle_language (wx_to_std (_language->GetValue()));
323         }
324 }
325
326 void
327 SubtitlePanel::content_selection_changed ()
328 {
329         film_content_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
330         film_content_changed (SubtitleContentProperty::USE_SUBTITLES);
331         film_content_changed (SubtitleContentProperty::BURN_SUBTITLES);
332         film_content_changed (SubtitleContentProperty::SUBTITLE_X_OFFSET);
333         film_content_changed (SubtitleContentProperty::SUBTITLE_Y_OFFSET);
334         film_content_changed (SubtitleContentProperty::SUBTITLE_X_SCALE);
335         film_content_changed (SubtitleContentProperty::SUBTITLE_Y_SCALE);
336         film_content_changed (SubtitleContentProperty::SUBTITLE_LANGUAGE);
337         film_content_changed (SubtitleContentProperty::FONTS);
338 }
339
340 void
341 SubtitlePanel::subtitle_view_clicked ()
342 {
343         if (_subtitle_view) {
344                 _subtitle_view->Destroy ();
345                 _subtitle_view = 0;
346         }
347
348         SubtitleContentList c = _parent->selected_subtitle ();
349         DCPOMATIC_ASSERT (c.size() == 1);
350
351         shared_ptr<SubtitleDecoder> decoder;
352
353         shared_ptr<SubRipContent> sr = dynamic_pointer_cast<SubRipContent> (c.front ());
354         if (sr) {
355                 decoder.reset (new SubRipDecoder (sr));
356         }
357
358         shared_ptr<DCPSubtitleContent> dc = dynamic_pointer_cast<DCPSubtitleContent> (c.front ());
359         if (dc) {
360                 decoder.reset (new DCPSubtitleDecoder (dc));
361         }
362
363         if (decoder) {
364                 _subtitle_view = new SubtitleView (this, _parent->film(), decoder, c.front()->position ());
365                 _subtitle_view->Show ();
366         }
367 }
368
369 void
370 SubtitlePanel::fonts_dialog_clicked ()
371 {
372         if (_fonts_dialog) {
373                 _fonts_dialog->Destroy ();
374                 _fonts_dialog = 0;
375         }
376
377         SubtitleContentList c = _parent->selected_subtitle ();
378         DCPOMATIC_ASSERT (c.size() == 1);
379
380         _fonts_dialog = new FontsDialog (this, c.front ());
381         _fonts_dialog->Show ();
382 }
383
384 void
385 SubtitlePanel::reference_clicked ()
386 {
387         ContentList c = _parent->selected ();
388         if (c.size() != 1) {
389                 return;
390         }
391
392         shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (c.front ());
393         if (!d) {
394                 return;
395         }
396
397         d->set_reference_subtitle (_reference->GetValue ());
398 }