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