Remove reel type / length controls from DCP panel.
[dcpomatic.git] / src / wx / dcp_panel.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21
22 #include "audio_dialog.h"
23 #include "check_box.h"
24 #include "check_box.h"
25 #include "dcp_panel.h"
26 #include "dcpomatic_button.h"
27 #include "dcpomatic_choice.h"
28 #include "dcpomatic_spin_ctrl.h"
29 #include "focus_manager.h"
30 #include "interop_metadata_dialog.h"
31 #include "language_tag_dialog.h"
32 #include "markers_dialog.h"
33 #include "smpte_metadata_dialog.h"
34 #include "static_text.h"
35 #include "wx_util.h"
36 #include "lib/audio_content.h"
37 #include "lib/audio_processor.h"
38 #include "lib/config.h"
39 #include "lib/dcp_content.h"
40 #include "lib/dcp_content_type.h"
41 #include "lib/ffmpeg_content.h"
42 #include "lib/film.h"
43 #include "lib/ratio.h"
44 #include "lib/text_content.h"
45 #include "lib/util.h"
46 #include "lib/video_content.h"
47 #include <dcp/locale_convert.h>
48 #include <dcp/warnings.h>
49 LIBDCP_DISABLE_WARNINGS
50 #include <wx/gbsizer.h>
51 #include <wx/notebook.h>
52 #include <wx/spinctrl.h>
53 #include <wx/wx.h>
54 LIBDCP_ENABLE_WARNINGS
55 #include <boost/lexical_cast.hpp>
56
57
58 using std::list;
59 using std::make_pair;
60 using std::max;
61 using std::pair;
62 using std::shared_ptr;
63 using std::string;
64 using std::vector;
65 using std::weak_ptr;
66 using boost::lexical_cast;
67 #if BOOST_VERSION >= 106100
68 using namespace boost::placeholders;
69 #endif
70 using dcp::locale_convert;
71
72
73 DCPPanel::DCPPanel(wxNotebook* n, shared_ptr<Film> film, FilmViewer& viewer)
74         : _film (film)
75         , _viewer (viewer)
76         , _generally_sensitive (true)
77 {
78         _panel = new wxPanel (n);
79         _sizer = new wxBoxSizer (wxVERTICAL);
80         _panel->SetSizer (_sizer);
81
82         _grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
83         _sizer->Add (_grid, 0, wxEXPAND | wxALL, 8);
84
85         _name_label = create_label (_panel, _("Name"), true);
86         _name = new wxTextCtrl (_panel, wxID_ANY);
87         FocusManager::instance()->add(_name);
88
89         _use_isdcf_name = new CheckBox (_panel, _("Use ISDCF name"));
90         _copy_isdcf_name_button = new Button (_panel, _("Copy as name"));
91
92         /* wxST_ELLIPSIZE_MIDDLE works around a bug in GTK2 and/or wxWidgets, see
93            http://trac.wxwidgets.org/ticket/12539
94         */
95         _dcp_name = new StaticText (
96                 _panel, wxT (""), wxDefaultPosition, wxDefaultSize,
97                 wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE | wxST_ELLIPSIZE_MIDDLE
98                 );
99
100         _dcp_content_type_label = create_label (_panel, _("Content Type"), true);
101         _dcp_content_type = new Choice(_panel);
102
103         _encrypted = new CheckBox (_panel, _("Encrypted"));
104
105         wxClientDC dc (_panel);
106         auto size = dc.GetTextExtent (wxT ("GGGGGGGG..."));
107         size.SetHeight (-1);
108
109         _standard_label = create_label (_panel, _("Standard"), true);
110         _standard = new Choice(_panel);
111
112         _markers = new Button (_panel, _("Markers..."));
113         _metadata = new Button (_panel, _("Metadata..."));
114
115         _notebook = new wxNotebook (_panel, wxID_ANY);
116         _sizer->Add (_notebook, 1, wxEXPAND | wxTOP, 6);
117
118         _notebook->AddPage (make_video_panel (), _("Video"), false);
119         _notebook->AddPage (make_audio_panel (), _("Audio"), false);
120
121         _name->Bind                  (wxEVT_TEXT,     boost::bind(&DCPPanel::name_changed, this));
122         _use_isdcf_name->bind(&DCPPanel::use_isdcf_name_toggled, this);
123         _copy_isdcf_name_button->Bind(wxEVT_BUTTON,   boost::bind(&DCPPanel::copy_isdcf_name_button_clicked, this));
124         _dcp_content_type->Bind      (wxEVT_CHOICE,   boost::bind(&DCPPanel::dcp_content_type_changed, this));
125         _encrypted->bind(&DCPPanel::encrypted_toggled, this);
126         _standard->Bind              (wxEVT_CHOICE,   boost::bind(&DCPPanel::standard_changed, this));
127         _markers->Bind               (wxEVT_BUTTON,   boost::bind(&DCPPanel::markers_clicked, this));
128         _metadata->Bind              (wxEVT_BUTTON,   boost::bind(&DCPPanel::metadata_clicked, this));
129         for (auto i: DCPContentType::all()) {
130                 _dcp_content_type->add(i->pretty_name());
131         }
132
133         add_standards();
134         _standard->SetToolTip(_("The standard that the DCP should use.  Interop is older, and SMPTE is the newer (current) standard.  If in doubt, choose 'SMPTE'"));
135
136         Config::instance()->Changed.connect (boost::bind(&DCPPanel::config_changed, this, _1));
137
138         add_to_grid ();
139 }
140
141
142 void
143 DCPPanel::add_standards()
144 {
145         _standard->add(_("SMPTE"), N_("smpte"));
146         if (Config::instance()->allow_smpte_bv20() || (_film && _film->limit_to_smpte_bv20())) {
147                 _standard->add(_("SMPTE (Bv2.0 only)"), N_("smpte-bv20"));
148         }
149         _standard->add(_("Interop"), N_("interop"));
150         _sizer->Layout();
151 }
152
153
154 void
155 DCPPanel::set_standard()
156 {
157         DCPOMATIC_ASSERT(_film);
158         DCPOMATIC_ASSERT(!_film->limit_to_smpte_bv20() || _standard->GetCount() == 3);
159
160         if (_film->interop()) {
161                 checked_set(_standard, "interop");
162         } else {
163                 checked_set(_standard, _film->limit_to_smpte_bv20() ? "smpte-bv20" : "smpte");
164         }
165 }
166
167
168 void
169 DCPPanel::standard_changed ()
170 {
171         if (!_film || !_standard->get()) {
172                 return;
173         }
174
175         auto const data = _standard->get_data();
176         if (!data) {
177                 return;
178         }
179
180         if (*data == N_("interop")) {
181                 _film->set_interop(true);
182                 _film->set_limit_to_smpte_bv20(false);
183         } else if (*data == N_("smpte")) {
184                 _film->set_interop(false);
185                 _film->set_limit_to_smpte_bv20(false);
186         } else if (*data == N_("smpte-bv20")) {
187                 _film->set_interop(false);
188                 _film->set_limit_to_smpte_bv20(true);
189         }
190 }
191
192
193 void
194 DCPPanel::add_to_grid ()
195 {
196         int r = 0;
197
198         auto name_sizer = new wxBoxSizer (wxHORIZONTAL);
199         name_sizer->Add (_name_label, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
200         name_sizer->Add (_name, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
201         _grid->Add (name_sizer, wxGBPosition(r, 0), wxGBSpan(1, 2), wxRIGHT | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
202         ++r;
203
204         int flags = wxALIGN_CENTER_VERTICAL;
205 #ifdef __WXOSX__
206         flags |= wxALIGN_RIGHT;
207 #endif
208
209         _grid->Add (_use_isdcf_name, wxGBPosition(r, 0), wxDefaultSpan, flags);
210         {
211                 auto s = new wxBoxSizer (wxHORIZONTAL);
212                 s->Add (_copy_isdcf_name_button, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
213                 _grid->Add (s, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxBOTTOM, DCPOMATIC_CHECKBOX_BOTTOM_PAD);
214         }
215         ++r;
216
217         _grid->Add (_dcp_name, wxGBPosition(r, 0), wxGBSpan(1, 2), wxALIGN_CENTER_VERTICAL | wxEXPAND);
218         ++r;
219
220         add_label_to_sizer (_grid, _dcp_content_type_label, true, wxGBPosition(r, 0));
221         _grid->Add (_dcp_content_type, wxGBPosition(r, 1));
222         ++r;
223
224         _grid->Add (_encrypted, wxGBPosition(r, 0), wxGBSpan(1, 2));
225         ++r;
226
227         add_label_to_sizer (_grid, _standard_label, true, wxGBPosition(r, 0));
228         _grid->Add (_standard, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
229         ++r;
230
231         auto extra = new wxBoxSizer (wxHORIZONTAL);
232         extra->Add (_markers, 1, wxRIGHT, DCPOMATIC_SIZER_X_GAP);
233         extra->Add (_metadata, 1, wxRIGHT, DCPOMATIC_SIZER_X_GAP);
234         _grid->Add (extra, wxGBPosition(r, 0), wxGBSpan(1, 2));
235         ++r;
236 }
237
238
239 void
240 DCPPanel::name_changed ()
241 {
242         if (!_film) {
243                 return;
244         }
245
246         _film->set_name (string(_name->GetValue().mb_str()));
247 }
248
249
250 void
251 DCPPanel::j2k_bandwidth_changed ()
252 {
253         if (!_film) {
254                 return;
255         }
256
257         _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
258 }
259
260
261 void
262 DCPPanel::encrypted_toggled ()
263 {
264         if (!_film) {
265                 return;
266         }
267
268         _film->set_encrypted (_encrypted->GetValue());
269 }
270
271
272 /** Called when the frame rate choice widget has been changed */
273 void
274 DCPPanel::frame_rate_choice_changed ()
275 {
276         if (!_film || !_frame_rate_choice->get()) {
277                 return;
278         }
279
280         _film->set_video_frame_rate (
281                 boost::lexical_cast<int>(
282                         wx_to_std(_frame_rate_choice->GetString(*_frame_rate_choice->get()))
283                         ),
284                 true
285                 );
286 }
287
288
289 /** Called when the frame rate spin widget has been changed */
290 void
291 DCPPanel::frame_rate_spin_changed ()
292 {
293         if (!_film) {
294                 return;
295         }
296
297         _film->set_video_frame_rate (_frame_rate_spin->GetValue());
298 }
299
300
301 void
302 DCPPanel::audio_channels_changed ()
303 {
304         if (!_film) {
305                 return;
306         }
307
308         _film->set_audio_channels(locale_convert<int>(string_client_data(_audio_channels->GetClientObject(*_audio_channels->get()))));
309 }
310
311
312 void
313 DCPPanel::resolution_changed ()
314 {
315         if (!_film || !_resolution->get()) {
316                 return;
317         }
318
319         _film->set_resolution(*_resolution->get() == 0 ? Resolution::TWO_K : Resolution::FOUR_K);
320 }
321
322
323 void
324 DCPPanel::markers_clicked ()
325 {
326         _markers_dialog.reset(_panel, _film, _viewer);
327         _markers_dialog->Show();
328 }
329
330
331 void
332 DCPPanel::metadata_clicked ()
333 {
334         if (_film->interop()) {
335                 _interop_metadata_dialog.reset(_panel, _film);
336                 _interop_metadata_dialog->setup ();
337                 _interop_metadata_dialog->Show ();
338         } else {
339                 _smpte_metadata_dialog.reset(_panel, _film);
340                 _smpte_metadata_dialog->setup ();
341                 _smpte_metadata_dialog->Show ();
342         }
343 }
344
345
346 void
347 DCPPanel::film_changed(FilmProperty p)
348 {
349         switch (p) {
350         case FilmProperty::NONE:
351                 break;
352         case FilmProperty::CONTAINER:
353                 setup_container ();
354                 break;
355         case FilmProperty::NAME:
356                 checked_set (_name, _film->name());
357                 setup_dcp_name ();
358                 break;
359         case FilmProperty::DCP_CONTENT_TYPE:
360         {
361                 auto index = DCPContentType::as_index(_film->dcp_content_type());
362                 DCPOMATIC_ASSERT (index);
363                 checked_set (_dcp_content_type, *index);
364                 setup_dcp_name ();
365                 break;
366         }
367         case FilmProperty::ENCRYPTED:
368                 checked_set (_encrypted, _film->encrypted ());
369                 break;
370         case FilmProperty::RESOLUTION:
371                 checked_set (_resolution, _film->resolution() == Resolution::TWO_K ? 0 : 1);
372                 setup_container ();
373                 setup_dcp_name ();
374                 break;
375         case FilmProperty::J2K_BANDWIDTH:
376                 checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
377                 break;
378         case FilmProperty::USE_ISDCF_NAME:
379         {
380                 checked_set (_use_isdcf_name, _film->use_isdcf_name());
381                 if (_film->use_isdcf_name()) {
382                         /* We are going back to using an ISDCF name.  Remove anything after a _ in the current name,
383                            in case the user has clicked 'Copy as name' then re-ticked 'Use ISDCF name' (#1513).
384                         */
385                         string const name = _film->name ();
386                         string::size_type const u = name.find("_");
387                         if (u != string::npos) {
388                                 _film->set_name (name.substr(0, u));
389                         }
390                 }
391                 setup_dcp_name ();
392                 break;
393         }
394         case FilmProperty::VIDEO_FRAME_RATE:
395         {
396                 bool done = false;
397                 for (unsigned int i = 0; i < _frame_rate_choice->GetCount(); ++i) {
398                         if (wx_to_std(_frame_rate_choice->GetString(i)) == boost::lexical_cast<string>(_film->video_frame_rate())) {
399                                 checked_set (_frame_rate_choice, i);
400                                 done = true;
401                                 break;
402                         }
403                 }
404
405                 if (!done) {
406                         checked_set (_frame_rate_choice, -1);
407                 }
408
409                 checked_set (_frame_rate_spin, _film->video_frame_rate ());
410
411                 _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
412                 setup_dcp_name ();
413                 break;
414         }
415         case FilmProperty::AUDIO_CHANNELS:
416                 if (_film->audio_channels() < minimum_allowed_audio_channels()) {
417                         _film->set_audio_channels (minimum_allowed_audio_channels());
418                 } else {
419                         checked_set (_audio_channels, locale_convert<string>(max(minimum_allowed_audio_channels(), _film->audio_channels())));
420                         setup_dcp_name ();
421                 }
422                 break;
423         case FilmProperty::THREE_D:
424                 checked_set (_three_d, _film->three_d());
425                 setup_dcp_name ();
426                 break;
427         case FilmProperty::REENCODE_J2K:
428                 checked_set (_reencode_j2k, _film->reencode_j2k());
429                 break;
430         case FilmProperty::INTEROP:
431                 set_standard();
432                 setup_dcp_name ();
433                 _markers->Enable (!_film->interop());
434                 break;
435         case FilmProperty::LIMIT_TO_SMPTE_BV20:
436                 set_standard();
437                 break;
438         case FilmProperty::AUDIO_PROCESSOR:
439                 if (_film->audio_processor()) {
440                         checked_set (_audio_processor, _film->audio_processor()->id());
441                 } else {
442                         checked_set (_audio_processor, 0);
443                 }
444                 setup_audio_channels_choice (_audio_channels, minimum_allowed_audio_channels());
445                 film_changed (FilmProperty::AUDIO_CHANNELS);
446                 break;
447         case FilmProperty::CONTENT:
448                 setup_dcp_name ();
449                 setup_sensitivity ();
450                 /* Maybe we now have ATMOS content which changes our minimum_allowed_audio_channels */
451                 setup_audio_channels_choice(_audio_channels, minimum_allowed_audio_channels());
452                 film_changed(FilmProperty::AUDIO_CHANNELS);
453                 break;
454         case FilmProperty::AUDIO_LANGUAGE:
455         {
456                 auto al = _film->audio_language();
457                 checked_set (_enable_audio_language, static_cast<bool>(al));
458                 checked_set (_audio_language, al ? std_to_wx(al->to_string()) : wxT(""));
459                 setup_dcp_name ();
460                 setup_sensitivity ();
461                 _audio_panel_sizer->Layout();
462                 break;
463         }
464         case FilmProperty::AUDIO_FRAME_RATE:
465                 if (_audio_sample_rate) {
466                         checked_set (_audio_sample_rate, _film->audio_frame_rate() == 48000 ? 0 : 1);
467                 }
468                 break;
469         case FilmProperty::CONTENT_VERSIONS:
470         case FilmProperty::VERSION_NUMBER:
471         case FilmProperty::RELEASE_TERRITORY:
472         case FilmProperty::RATINGS:
473         case FilmProperty::FACILITY:
474         case FilmProperty::STUDIO:
475         case FilmProperty::TEMP_VERSION:
476         case FilmProperty::PRE_RELEASE:
477         case FilmProperty::RED_BAND:
478         case FilmProperty::TWO_D_VERSION_OF_THREE_D:
479         case FilmProperty::CHAIN:
480         case FilmProperty::LUMINANCE:
481         case FilmProperty::TERRITORY_TYPE:
482                 setup_dcp_name ();
483                 break;
484         default:
485                 break;
486         }
487 }
488
489
490 void
491 DCPPanel::film_content_changed (int property)
492 {
493         if (property == AudioContentProperty::STREAMS ||
494             property == TextContentProperty::USE ||
495             property == TextContentProperty::BURN ||
496             property == TextContentProperty::LANGUAGE ||
497             property == TextContentProperty::LANGUAGE_IS_ADDITIONAL ||
498             property == TextContentProperty::TYPE ||
499             property == TextContentProperty::DCP_TRACK ||
500             property == VideoContentProperty::CUSTOM_RATIO ||
501             property == VideoContentProperty::CUSTOM_SIZE ||
502             property == VideoContentProperty::BURNT_SUBTITLE_LANGUAGE ||
503             property == VideoContentProperty::CROP ||
504             property == DCPContentProperty::REFERENCE_VIDEO ||
505             property == DCPContentProperty::REFERENCE_AUDIO ||
506             property == DCPContentProperty::REFERENCE_TEXT) {
507                 setup_dcp_name ();
508                 setup_sensitivity ();
509         }
510 }
511
512
513 void
514 DCPPanel::setup_container ()
515 {
516         int n = 0;
517         auto ratios = Ratio::containers ();
518         auto i = ratios.begin ();
519         while (i != ratios.end() && *i != _film->container()) {
520                 ++i;
521                 ++n;
522         }
523
524         if (i == ratios.end()) {
525                 checked_set (_container, -1);
526                 checked_set (_container_size, wxT(""));
527         } else {
528                 checked_set (_container, n);
529                 auto const size = fit_ratio_within (_film->container()->ratio(), _film->full_frame ());
530                 checked_set (_container_size, wxString::Format("%dx%d", size.width, size.height));
531         }
532
533         setup_dcp_name ();
534 }
535
536
537 /** Called when the container widget has been changed */
538 void
539 DCPPanel::container_changed ()
540 {
541         if (!_film) {
542                 return;
543         }
544
545         if (auto const container = _container->get()) {
546                 auto ratios = Ratio::containers ();
547                 DCPOMATIC_ASSERT(*container < int(ratios.size()));
548                 _film->set_container(ratios[*container]);
549         }
550 }
551
552
553 /** Called when the DCP content type widget has been changed */
554 void
555 DCPPanel::dcp_content_type_changed ()
556 {
557         if (!_film) {
558                 return;
559         }
560
561         if (auto const type = _dcp_content_type->get()) {
562                 _film->set_dcp_content_type(DCPContentType::from_index(*type));
563         }
564 }
565
566
567 void
568 DCPPanel::set_film (shared_ptr<Film> film)
569 {
570         /* We are changing film, so destroy any dialogs for the old one */
571         _audio_dialog.reset();
572         _markers_dialog.reset();
573         _interop_metadata_dialog.reset();
574         _smpte_metadata_dialog.reset();
575
576         _film = film;
577
578         if (!_film) {
579                 /* Really should all the film_changed below but this might be enough */
580                 checked_set (_dcp_name, wxT(""));
581                 set_general_sensitivity (false);
582                 return;
583         }
584
585         _standard->Clear();
586         add_standards();
587
588         film_changed(FilmProperty::NAME);
589         film_changed(FilmProperty::USE_ISDCF_NAME);
590         film_changed(FilmProperty::CONTENT);
591         film_changed(FilmProperty::DCP_CONTENT_TYPE);
592         film_changed(FilmProperty::CONTAINER);
593         film_changed(FilmProperty::RESOLUTION);
594         film_changed(FilmProperty::ENCRYPTED);
595         film_changed(FilmProperty::J2K_BANDWIDTH);
596         film_changed(FilmProperty::VIDEO_FRAME_RATE);
597         film_changed(FilmProperty::AUDIO_CHANNELS);
598         film_changed(FilmProperty::SEQUENCE);
599         film_changed(FilmProperty::THREE_D);
600         film_changed(FilmProperty::INTEROP);
601         film_changed(FilmProperty::AUDIO_PROCESSOR);
602         film_changed(FilmProperty::REEL_TYPE);
603         film_changed(FilmProperty::REEL_LENGTH);
604         film_changed(FilmProperty::REENCODE_J2K);
605         film_changed(FilmProperty::AUDIO_LANGUAGE);
606         film_changed(FilmProperty::AUDIO_FRAME_RATE);
607         film_changed(FilmProperty::LIMIT_TO_SMPTE_BV20);
608
609         set_general_sensitivity(static_cast<bool>(_film));
610 }
611
612
613 void
614 DCPPanel::set_general_sensitivity (bool s)
615 {
616         _generally_sensitive = s;
617         setup_sensitivity ();
618 }
619
620
621 void
622 DCPPanel::setup_sensitivity ()
623 {
624         _name->Enable                   (_generally_sensitive);
625         _use_isdcf_name->Enable         (_generally_sensitive);
626         _dcp_content_type->Enable       (_generally_sensitive);
627         _copy_isdcf_name_button->Enable (_generally_sensitive);
628         _enable_audio_language->Enable  (_generally_sensitive);
629         _audio_language->Enable         (_enable_audio_language->GetValue());
630         _edit_audio_language->Enable    (_enable_audio_language->GetValue());
631         _encrypted->Enable              (_generally_sensitive);
632         _markers->Enable                (_generally_sensitive && _film && !_film->interop());
633         _metadata->Enable               (_generally_sensitive);
634         _frame_rate_choice->Enable      (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->contains_atmos_content());
635         _frame_rate_spin->Enable        (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->contains_atmos_content());
636         _audio_channels->Enable         (_generally_sensitive && _film && !_film->references_dcp_audio());
637         _audio_processor->Enable        (_generally_sensitive && _film && !_film->references_dcp_audio());
638         _j2k_bandwidth->Enable          (_generally_sensitive && _film && !_film->references_dcp_video());
639         _container->Enable              (_generally_sensitive && _film && !_film->references_dcp_video());
640         _best_frame_rate->Enable (
641                 _generally_sensitive &&
642                 _film &&
643                 _film->best_video_frame_rate () != _film->video_frame_rate() &&
644                 !_film->references_dcp_video() &&
645                 !_film->contains_atmos_content()
646                 );
647         _resolution->Enable             (_generally_sensitive && _film && !_film->references_dcp_video());
648         _three_d->Enable                (_generally_sensitive && _film && !_film->references_dcp_video());
649
650         _standard->Enable (
651                 _generally_sensitive &&
652                 _film &&
653                 !_film->references_dcp_video() &&
654                 !_film->references_dcp_audio() &&
655                 !_film->contains_atmos_content()
656                 );
657
658         _reencode_j2k->Enable           (_generally_sensitive && _film);
659         _show_audio->Enable             (_generally_sensitive && _film);
660 }
661
662
663 void
664 DCPPanel::use_isdcf_name_toggled ()
665 {
666         if (!_film) {
667                 return;
668         }
669
670         _film->set_use_isdcf_name (_use_isdcf_name->GetValue());
671 }
672
673 void
674 DCPPanel::setup_dcp_name ()
675 {
676         if (!_film) {
677                 return;
678         }
679
680         _dcp_name->SetLabel (std_to_wx(_film->dcp_name(true)));
681         _dcp_name->SetToolTip (std_to_wx(_film->dcp_name(true)));
682 }
683
684
685 void
686 DCPPanel::best_frame_rate_clicked ()
687 {
688         if (!_film) {
689                 return;
690         }
691
692         _film->set_video_frame_rate (_film->best_video_frame_rate());
693 }
694
695
696 void
697 DCPPanel::three_d_changed ()
698 {
699         if (!_film) {
700                 return;
701         }
702
703         _film->set_three_d (_three_d->GetValue());
704 }
705
706
707 void
708 DCPPanel::reencode_j2k_changed ()
709 {
710         if (!_film) {
711                 return;
712         }
713
714         _film->set_reencode_j2k (_reencode_j2k->GetValue());
715 }
716
717
718 void
719 DCPPanel::config_changed (Config::Property p)
720 {
721         _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
722         setup_frame_rate_widget ();
723
724         if (p == Config::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS) {
725                 _audio_processor->Clear ();
726                 add_audio_processors ();
727                 if (_film) {
728                         film_changed(FilmProperty::AUDIO_PROCESSOR);
729                 }
730         } else if (p == Config::ALLOW_SMPTE_BV20) {
731                 _standard->Clear();
732                 add_standards();
733                 if (_film) {
734                         film_changed(FilmProperty::INTEROP);
735                         film_changed(FilmProperty::LIMIT_TO_SMPTE_BV20);
736                 }
737         } else if (p == Config::ISDCF_NAME_PART_LENGTH) {
738                 setup_dcp_name();
739         }
740 }
741
742
743 void
744 DCPPanel::setup_frame_rate_widget ()
745 {
746         if (Config::instance()->allow_any_dcp_frame_rate()) {
747                 _frame_rate_choice->Hide ();
748                 _frame_rate_spin->Show ();
749         } else {
750                 _frame_rate_choice->Show ();
751                 _frame_rate_spin->Hide ();
752         }
753         _frame_rate_sizer->Layout();
754 }
755
756
757 wxPanel *
758 DCPPanel::make_video_panel ()
759 {
760         auto panel = new wxPanel (_notebook);
761         auto sizer = new wxBoxSizer (wxVERTICAL);
762         _video_grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
763         sizer->Add (_video_grid, 0, wxALL, 8);
764         panel->SetSizer (sizer);
765
766         _container_label = create_label (panel, _("Container"), true);
767         _container = new Choice(panel);
768         _container_size = new StaticText (panel, wxT (""));
769
770         _resolution_label = create_label (panel, _("Resolution"), true);
771         _resolution = new Choice(panel);
772
773         _frame_rate_label = create_label (panel, _("Frame Rate"), true);
774         _frame_rate_choice = new Choice(panel);
775         _frame_rate_spin = new SpinCtrl (panel, DCPOMATIC_SPIN_CTRL_WIDTH);
776         _best_frame_rate = new Button (panel, _("Use best"));
777
778         _three_d = new CheckBox (panel, _("3D"));
779
780         _j2k_bandwidth_label = create_label (panel, _("JPEG2000 bandwidth\nfor newly-encoded data"), true);
781         _j2k_bandwidth = new SpinCtrl (panel, DCPOMATIC_SPIN_CTRL_WIDTH);
782         _mbits_label = create_label (panel, _("Mbit/s"), false);
783
784         _reencode_j2k = new CheckBox (panel, _("Re-encode JPEG2000 data from input"));
785
786         _container->Bind         (wxEVT_CHOICE,   boost::bind(&DCPPanel::container_changed, this));
787         _frame_rate_choice->Bind (wxEVT_CHOICE,   boost::bind(&DCPPanel::frame_rate_choice_changed, this));
788         _frame_rate_spin->Bind   (wxEVT_SPINCTRL, boost::bind(&DCPPanel::frame_rate_spin_changed, this));
789         _best_frame_rate->Bind   (wxEVT_BUTTON,   boost::bind(&DCPPanel::best_frame_rate_clicked, this));
790         _j2k_bandwidth->Bind     (wxEVT_SPINCTRL, boost::bind(&DCPPanel::j2k_bandwidth_changed, this));
791         /* Also listen to wxEVT_TEXT so that typing numbers directly in is always noticed */
792         _j2k_bandwidth->Bind     (wxEVT_TEXT,     boost::bind(&DCPPanel::j2k_bandwidth_changed, this));
793         _resolution->Bind        (wxEVT_CHOICE,   boost::bind(&DCPPanel::resolution_changed, this));
794         _three_d->bind(&DCPPanel::three_d_changed, this);
795         _reencode_j2k->bind(&DCPPanel::reencode_j2k_changed, this);
796
797         for (auto i: Ratio::containers()) {
798                 _container->add(i->container_nickname());
799         }
800
801         for (auto i: Config::instance()->allowed_dcp_frame_rates()) {
802                 _frame_rate_choice->add(boost::lexical_cast<string>(i));
803         }
804
805         _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
806         _frame_rate_spin->SetRange (1, 480);
807
808         _resolution->add(_("2K"));
809         _resolution->add(_("4K"));
810
811         add_video_panel_to_grid ();
812         setup_frame_rate_widget();
813
814         return panel;
815 }
816
817
818 void
819 DCPPanel::add_video_panel_to_grid ()
820 {
821         int r = 0;
822
823         add_label_to_sizer (_video_grid, _container_label, true, wxGBPosition (r, 0));
824         {
825                 auto s = new wxBoxSizer (wxHORIZONTAL);
826                 s->Add (_container, 1, wxEXPAND | wxRIGHT, DCPOMATIC_SIZER_X_GAP);
827                 s->Add (_container_size, 1, wxLEFT | wxALIGN_CENTER_VERTICAL);
828                 _video_grid->Add (s, wxGBPosition(r, 1));
829                 ++r;
830         }
831
832         add_label_to_sizer (_video_grid, _resolution_label, true, wxGBPosition (r, 0));
833         _video_grid->Add (_resolution, wxGBPosition (r, 1));
834         ++r;
835
836         add_label_to_sizer (_video_grid, _frame_rate_label, true, wxGBPosition (r, 0));
837         {
838                 _frame_rate_sizer = new wxBoxSizer (wxHORIZONTAL);
839                 _frame_rate_sizer->Add (_frame_rate_choice, 1, wxALIGN_CENTER_VERTICAL);
840                 _frame_rate_sizer->Add (_frame_rate_spin, 1, wxALIGN_CENTER_VERTICAL);
841                 _frame_rate_sizer->Add (_best_frame_rate, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
842                 _video_grid->Add (_frame_rate_sizer, wxGBPosition (r, 1));
843                 ++r;
844         }
845
846         _video_grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
847         ++r;
848
849         add_label_to_sizer (_video_grid, _j2k_bandwidth_label, true, wxGBPosition (r, 0));
850         auto s = new wxBoxSizer (wxHORIZONTAL);
851         s->Add (_j2k_bandwidth, 0, wxALIGN_CENTER_VERTICAL);
852         add_label_to_sizer (s, _mbits_label, false, 0, wxLEFT | wxALIGN_CENTER_VERTICAL);
853         _video_grid->Add (s, wxGBPosition(r, 1), wxDefaultSpan);
854         ++r;
855         _video_grid->Add (_reencode_j2k, wxGBPosition(r, 0), wxGBSpan(1, 2));
856 }
857
858
859 int
860 DCPPanel::minimum_allowed_audio_channels () const
861 {
862         int min = 2;
863         if (_film) {
864                 if (_film->audio_processor()) {
865                         min = _film->audio_processor()->out_channels();
866                 }
867                 if (_film->contains_atmos_content()) {
868                         min = std::max(min, 14);
869                 }
870         }
871
872         if (min % 2 == 1) {
873                 ++min;
874         }
875
876         return min;
877 }
878
879
880 wxPanel *
881 DCPPanel::make_audio_panel ()
882 {
883         auto panel = new wxPanel (_notebook);
884         _audio_panel_sizer = new wxBoxSizer (wxVERTICAL);
885         _audio_grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
886         _audio_panel_sizer->Add (_audio_grid, 0, wxALL, 8);
887         panel->SetSizer (_audio_panel_sizer);
888
889         _channels_label = create_label (panel, _("Channels"), true);
890         _audio_channels = new Choice(panel);
891         setup_audio_channels_choice (_audio_channels, minimum_allowed_audio_channels ());
892
893         if (Config::instance()->allow_96khz_audio()) {
894                 _audio_sample_rate_label = create_label (panel, _("Sample rate"), true);
895                 _audio_sample_rate = new wxChoice (panel, wxID_ANY);
896         }
897
898         _processor_label = create_label (panel, _("Processor"), true);
899         _audio_processor = new Choice(panel);
900         add_audio_processors ();
901
902         _enable_audio_language = new CheckBox(panel, _("Language"));
903         _audio_language = new wxStaticText(panel, wxID_ANY, wxT(""));
904         _edit_audio_language = new Button(panel, _("Edit..."));
905
906         _show_audio = new Button (panel, _("Show graph of audio levels..."));
907
908         _audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DCPPanel::audio_channels_changed, this));
909         if (_audio_sample_rate) {
910                 _audio_sample_rate->Bind (wxEVT_CHOICE, boost::bind(&DCPPanel::audio_sample_rate_changed, this));
911         }
912         _audio_processor->Bind (wxEVT_CHOICE, boost::bind (&DCPPanel::audio_processor_changed, this));
913
914         _enable_audio_language->bind(&DCPPanel::enable_audio_language_toggled, this);
915         _edit_audio_language->Bind(wxEVT_BUTTON, boost::bind(&DCPPanel::edit_audio_language_clicked, this));
916
917         _show_audio->Bind (wxEVT_BUTTON, boost::bind (&DCPPanel::show_audio_clicked, this));
918
919         if (_audio_sample_rate) {
920                 _audio_sample_rate->Append (_("48kHz"));
921                 _audio_sample_rate->Append (_("96kHz"));
922         }
923
924         add_audio_panel_to_grid ();
925
926         return panel;
927 }
928
929
930 void
931 DCPPanel::add_audio_panel_to_grid ()
932 {
933         int r = 0;
934
935         add_label_to_sizer (_audio_grid, _channels_label, true, wxGBPosition (r, 0));
936         _audio_grid->Add (_audio_channels, wxGBPosition (r, 1));
937         ++r;
938
939         if (_audio_sample_rate_label && _audio_sample_rate) {
940                 add_label_to_sizer (_audio_grid, _audio_sample_rate_label, true, wxGBPosition(r, 0));
941                 _audio_grid->Add (_audio_sample_rate, wxGBPosition(r, 1));
942                 ++r;
943         }
944
945         add_label_to_sizer (_audio_grid, _processor_label, true, wxGBPosition (r, 0));
946         _audio_grid->Add (_audio_processor, wxGBPosition (r, 1));
947         ++r;
948
949         {
950                 auto s = new wxBoxSizer (wxHORIZONTAL);
951                 s->Add(_enable_audio_language, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, DCPOMATIC_SIZER_GAP);
952                 s->Add(_audio_language, 1, wxALIGN_CENTER_VERTICAL | wxBOTTOM, DCPOMATIC_CHECKBOX_BOTTOM_PAD);
953                 s->Add(DCPOMATIC_SIZER_X_GAP, 0);
954                 s->Add(_edit_audio_language, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, DCPOMATIC_CHECKBOX_BOTTOM_PAD);
955                 _audio_grid->Add(s, wxGBPosition(r, 0), wxGBSpan(1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL);
956         }
957         ++r;
958
959         _audio_grid->Add (_show_audio, wxGBPosition (r, 0), wxGBSpan (1, 2));
960         ++r;
961 }
962
963
964 void
965 DCPPanel::copy_isdcf_name_button_clicked ()
966 {
967         _film->set_name (_film->isdcf_name (true));
968         _film->set_use_isdcf_name (false);
969 }
970
971
972 void
973 DCPPanel::audio_processor_changed ()
974 {
975         if (!_film || !_audio_processor->get()) {
976                 return;
977         }
978
979         auto const s = string_client_data(_audio_processor->GetClientObject(*_audio_processor->get()));
980         _film->set_audio_processor (AudioProcessor::from_id (s));
981 }
982
983
984 void
985 DCPPanel::show_audio_clicked ()
986 {
987         if (!_film) {
988                 return;
989         }
990
991         _audio_dialog.reset(_panel, _film, _viewer);
992         _audio_dialog->Show();
993 }
994
995
996 void
997 DCPPanel::add_audio_processors ()
998 {
999         _audio_processor->add(_("None"), new wxStringClientData(N_("none")));
1000         for (auto ap: AudioProcessor::visible()) {
1001                 _audio_processor->add(std_to_wx(ap->name()), new wxStringClientData(std_to_wx(ap->id())));
1002         }
1003         _audio_panel_sizer->Layout();
1004 }
1005
1006
1007 void
1008 DCPPanel::enable_audio_language_toggled ()
1009 {
1010         setup_sensitivity ();
1011         if (_enable_audio_language->GetValue()) {
1012                 auto al = wx_to_std (_audio_language->GetLabel());
1013                 _film->set_audio_language (al.empty() ? dcp::LanguageTag("en-US") : dcp::LanguageTag(al));
1014         } else {
1015                 _film->set_audio_language (boost::none);
1016         }
1017 }
1018
1019
1020 void
1021 DCPPanel::edit_audio_language_clicked ()
1022 {
1023        DCPOMATIC_ASSERT (_film->audio_language());
1024        auto d = make_wx<LanguageTagDialog>(_panel, *_film->audio_language());
1025        if (d->ShowModal() == wxID_OK) {
1026                _film->set_audio_language(d->get());
1027        }
1028 }
1029
1030
1031 void
1032 DCPPanel::audio_sample_rate_changed ()
1033 {
1034         if (_audio_sample_rate) {
1035                 _film->set_audio_frame_rate (_audio_sample_rate->GetSelection() == 0 ? 48000 : 96000);
1036         }
1037 }
1038