Split SMPTE metadata dialogue into two tabs (#1933).
[dcpomatic.git] / src / wx / smpte_metadata_dialog.cc
1 /*
2     Copyright (C) 2019-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 "content_version_dialog.h"
23 #include "editable_list.h"
24 #include "language_tag_dialog.h"
25 #include "language_tag_widget.h"
26 #include "smpte_metadata_dialog.h"
27 #include "rating_dialog.h"
28 #include "lib/film.h"
29 #include <dcp/types.h>
30 #include <wx/gbsizer.h>
31 #include <wx/notebook.h>
32 #include <wx/spinctrl.h>
33
34
35 using std::string;
36 using std::vector;
37 using boost::optional;
38 using std::shared_ptr;
39 using std::weak_ptr;
40 #if BOOST_VERSION >= 106100
41 using namespace boost::placeholders;
42 #endif
43
44
45 static string
46 additional_subtitle_language_column (dcp::LanguageTag r, int)
47 {
48         return r.to_string();
49 }
50
51
52 static string
53 ratings_column (dcp::Rating r, int c)
54 {
55         if (c == 0) {
56                 return r.agency;
57         }
58
59         return r.label;
60 }
61
62
63 static string
64 content_versions_column (string v, int)
65 {
66         return v;
67 }
68
69
70 wxPanel *
71 SMPTEMetadataDialog::main_panel (wxWindow* parent)
72 {
73         auto panel = new wxPanel (parent, wxID_ANY);
74
75         auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
76         sizer->AddGrowableCol (1, 1);
77
78         add_label_to_sizer (sizer, panel, _("Title language"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
79         _name_language = new LanguageTagWidget(
80                 panel,
81                 wxString::Format(_("The language that the film's title (\"%s\") is in"), std_to_wx(film()->name())),
82                 film()->name_language()
83                 );
84         sizer->Add (_name_language->sizer(), 0, wxEXPAND);
85
86         add_label_to_sizer (sizer, panel, _("Audio language"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
87         _audio_language = new LanguageTagWidget(
88                 panel,
89                 _("The main language that is spoken in the film's soundtrack"),
90                 film()->audio_language()
91                 );
92         sizer->Add (_audio_language->sizer(), 0, wxEXPAND);
93
94         _enable_main_subtitle_language = new wxCheckBox (panel, wxID_ANY, _("Main subtitle language"));
95         sizer->Add (_enable_main_subtitle_language, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
96         vector<dcp::LanguageTag> subtitle_languages = film()->subtitle_languages();
97         _main_subtitle_language = new LanguageTagWidget(
98                 panel,
99                 _("The main language that is displayed in the film's subtitles"),
100                 subtitle_languages.empty() ? dcp::LanguageTag("en-US") : subtitle_languages.front()
101                 );
102         sizer->Add (_main_subtitle_language->sizer(), 0, wxEXPAND);
103
104         {
105                 int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
106 #ifdef __WXOSX__
107                 flags |= wxALIGN_RIGHT;
108 #endif
109                 auto m = create_label (panel, _("Additional subtitle languages"), true);
110                 sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
111         }
112
113         vector<EditableListColumn> columns;
114         columns.push_back (EditableListColumn("Language", 250, true));
115         _additional_subtitle_languages = new EditableList<dcp::LanguageTag, LanguageTagDialog> (
116                 panel,
117                 columns,
118                 boost::bind(&SMPTEMetadataDialog::additional_subtitle_languages, this),
119                 boost::bind(&SMPTEMetadataDialog::set_additional_subtitle_languages, this, _1),
120                 boost::bind(&additional_subtitle_language_column, _1, _2),
121                 true,
122                 false
123                 );
124         sizer->Add (_additional_subtitle_languages, 1, wxEXPAND);
125
126         {
127                 int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
128 #ifdef __WXOSX__
129                 flags |= wxALIGN_RIGHT;
130 #endif
131                 auto m = create_label (panel, _("Ratings"), true);
132                 sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
133         }
134
135         columns.clear ();
136         columns.push_back (EditableListColumn("Agency", 200, true));
137         columns.push_back (EditableListColumn("Label", 50, true));
138         _ratings = new EditableList<dcp::Rating, RatingDialog> (
139                 panel,
140                 columns,
141                 boost::bind(&SMPTEMetadataDialog::ratings, this),
142                 boost::bind(&SMPTEMetadataDialog::set_ratings, this, _1),
143                 boost::bind(&ratings_column, _1, _2),
144                 true,
145                 false
146                 );
147         sizer->Add (_ratings, 1, wxEXPAND);
148
149         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
150         overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
151         panel->SetSizer (overall_sizer);
152
153         return panel;
154 }
155
156
157 wxPanel *
158 SMPTEMetadataDialog::advanced_panel (wxWindow* parent)
159 {
160         auto panel = new wxPanel (parent, wxID_ANY);
161
162         auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
163         sizer->AddGrowableCol (1, 1);
164
165         Button* edit_release_territory = nullptr;
166         add_label_to_sizer (sizer, panel, _("Release territory"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
167         {
168                 wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
169                 _release_territory = new wxStaticText (panel, wxID_ANY, wxT(""));
170                 s->Add (_release_territory, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
171                 edit_release_territory = new Button (panel, _("Edit..."));
172                 s->Add (edit_release_territory, 0, wxLEFT, DCPOMATIC_SIZER_GAP);
173                 sizer->Add (s, 0, wxEXPAND);
174         }
175
176         add_label_to_sizer (sizer, panel, _("Version number"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
177         _version_number = new wxSpinCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 1000);
178         sizer->Add (_version_number, 0);
179
180         add_label_to_sizer (sizer, panel, _("Status"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
181         _status = new wxChoice (panel, wxID_ANY);
182         sizer->Add (_status, 0);
183
184         add_label_to_sizer (sizer, panel, _("Chain"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
185         _chain = new wxTextCtrl (panel, wxID_ANY);
186         sizer->Add (_chain, 1, wxEXPAND);
187
188         add_label_to_sizer (sizer, panel, _("Distributor"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
189         _distributor = new wxTextCtrl (panel, wxID_ANY);
190         sizer->Add (_distributor, 1, wxEXPAND);
191
192         add_label_to_sizer (sizer, panel, _("Facility"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
193         _facility = new wxTextCtrl (panel, wxID_ANY);
194         sizer->Add (_facility, 1, wxEXPAND);
195
196         add_label_to_sizer (sizer, panel, _("Luminance"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
197         {
198                 auto s = new wxBoxSizer (wxHORIZONTAL);
199                 _luminance_value = new wxSpinCtrlDouble (panel, wxID_ANY);
200                 _luminance_value->SetDigits (1);
201                 _luminance_value->SetIncrement (0.1);
202                 s->Add (_luminance_value, 0);
203                 _luminance_unit = new wxChoice (panel, wxID_ANY);
204                 s->Add (_luminance_unit, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
205                 sizer->Add (s, 1, wxEXPAND);
206         }
207
208         {
209                 int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
210 #ifdef __WXOSX__
211                 flags |= wxALIGN_RIGHT;
212 #endif
213                 auto m = create_label (panel, _("Content versions"), true);
214                 sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
215         }
216
217         vector<EditableListColumn> columns;
218         columns.push_back (EditableListColumn("Version", 350, true));
219         _content_versions = new EditableList<string, ContentVersionDialog> (
220                 panel,
221                 columns,
222                 boost::bind(&SMPTEMetadataDialog::content_versions, this),
223                 boost::bind(&SMPTEMetadataDialog::set_content_versions, this, _1),
224                 boost::bind(&content_versions_column, _1, _2),
225                 true,
226                 false
227                 );
228         sizer->Add (_content_versions, 1, wxEXPAND);
229
230         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
231         overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
232         panel->SetSizer (overall_sizer);
233
234         edit_release_territory->Bind (wxEVT_BUTTON, boost::bind(&SMPTEMetadataDialog::edit_release_territory, this));
235
236         return panel;
237 }
238
239
240 SMPTEMetadataDialog::SMPTEMetadataDialog (wxWindow* parent, weak_ptr<Film> weak_film)
241         : wxDialog (parent, wxID_ANY, _("Metadata"))
242         , WeakFilm (weak_film)
243 {
244         auto notebook = new wxNotebook (this, wxID_ANY);
245         notebook->AddPage (main_panel(notebook), _("Standard"));
246         notebook->AddPage (advanced_panel(notebook), _("Advanced"));
247
248         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
249         overall_sizer->Add (notebook, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
250
251         auto buttons = CreateSeparatedButtonSizer (wxCLOSE);
252         if (buttons) {
253                 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
254         }
255
256         SetSizer (overall_sizer);
257         overall_sizer->Layout ();
258         overall_sizer->SetSizeHints (this);
259
260         _status->Append (_("Temporary"));
261         _status->Append (_("Pre-release"));
262         _status->Append (_("Final"));
263
264         _luminance_unit->Append (_("candela per m²"));
265         _luminance_unit->Append (_("foot lambert"));
266
267         _name_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::name_language_changed, this, _1));
268         _audio_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::audio_language_changed, this, _1));
269         _enable_main_subtitle_language->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_main_subtitle_changed, this));
270         _main_subtitle_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::main_subtitle_language_changed, this, _1));
271         _version_number->Bind (wxEVT_SPINCTRL, boost::bind(&SMPTEMetadataDialog::version_number_changed, this));
272         _status->Bind (wxEVT_CHOICE, boost::bind(&SMPTEMetadataDialog::status_changed, this));
273         _chain->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::chain_changed, this));
274         _distributor->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::distributor_changed, this));
275         _facility->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::facility_changed, this));
276         _luminance_value->Bind (wxEVT_SPINCTRLDOUBLE, boost::bind(&SMPTEMetadataDialog::luminance_changed, this));
277         _luminance_unit->Bind (wxEVT_CHOICE, boost::bind(&SMPTEMetadataDialog::luminance_changed, this));
278
279         _version_number->SetFocus ();
280
281         _film_changed_connection = film()->Change.connect(boost::bind(&SMPTEMetadataDialog::film_changed, this, _1, _2));
282
283         film_changed (ChangeType::DONE, Film::Property::NAME_LANGUAGE);
284         film_changed (ChangeType::DONE, Film::Property::RELEASE_TERRITORY);
285         film_changed (ChangeType::DONE, Film::Property::VERSION_NUMBER);
286         film_changed (ChangeType::DONE, Film::Property::STATUS);
287         film_changed (ChangeType::DONE, Film::Property::CHAIN);
288         film_changed (ChangeType::DONE, Film::Property::DISTRIBUTOR);
289         film_changed (ChangeType::DONE, Film::Property::FACILITY);
290         film_changed (ChangeType::DONE, Film::Property::CONTENT_VERSIONS);
291         film_changed (ChangeType::DONE, Film::Property::LUMINANCE);
292         film_changed (ChangeType::DONE, Film::Property::SUBTITLE_LANGUAGES);
293
294         setup_sensitivity ();
295 }
296
297
298 void
299 SMPTEMetadataDialog::film_changed (ChangeType type, Film::Property property)
300 {
301         if (type != ChangeType::DONE || film()->interop()) {
302                 return;
303         }
304
305         if (property == Film::Property::NAME_LANGUAGE) {
306                 _name_language->set (film()->name_language());
307         } else if (property == Film::Property::RELEASE_TERRITORY) {
308                 checked_set (_release_territory, std_to_wx(*dcp::LanguageTag::get_subtag_description(dcp::LanguageTag::SubtagType::REGION, film()->release_territory().subtag())));
309         } else if (property == Film::Property::VERSION_NUMBER) {
310                 checked_set (_version_number, film()->version_number());
311         } else if (property == Film::Property::STATUS) {
312                 switch (film()->status()) {
313                 case dcp::Status::TEMP:
314                         checked_set (_status, 0);
315                         break;
316                 case dcp::Status::PRE:
317                         checked_set (_status, 1);
318                         break;
319                 case dcp::Status::FINAL:
320                         checked_set (_status, 2);
321                         break;
322                 }
323         } else if (property == Film::Property::CHAIN) {
324                 checked_set (_chain, film()->chain());
325         } else if (property == Film::Property::DISTRIBUTOR) {
326                 checked_set (_distributor, film()->distributor());
327         } else if (property == Film::Property::FACILITY) {
328                 checked_set (_facility, film()->facility());
329         } else if (property == Film::Property::LUMINANCE) {
330                 checked_set (_luminance_value, film()->luminance().value());
331                 switch (film()->luminance().unit()) {
332                 case dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE:
333                         checked_set (_luminance_unit, 0);
334                         break;
335                 case dcp::Luminance::Unit::FOOT_LAMBERT:
336                         checked_set (_luminance_unit, 1);
337                         break;
338                 }
339         } else if (property == Film::Property::SUBTITLE_LANGUAGES) {
340                 vector<dcp::LanguageTag> languages = film()->subtitle_languages();
341                 checked_set (_enable_main_subtitle_language, !languages.empty());
342                 if (!languages.empty()) {
343                         _main_subtitle_language->set (languages.front());
344                 } else {
345                         _main_subtitle_language->set (dcp::LanguageTag("en-US"));
346                 }
347         }
348 }
349
350
351 vector<dcp::Rating>
352 SMPTEMetadataDialog::ratings () const
353 {
354         return film()->ratings ();
355 }
356
357
358 void
359 SMPTEMetadataDialog::set_ratings (vector<dcp::Rating> r)
360 {
361         film()->set_ratings (r);
362 }
363
364
365 vector<string>
366 SMPTEMetadataDialog::content_versions () const
367 {
368         return film()->content_versions ();
369 }
370
371
372 void
373 SMPTEMetadataDialog::set_content_versions (vector<string> cv)
374 {
375         film()->set_content_versions (cv);
376 }
377
378
379 void
380 SMPTEMetadataDialog::name_language_changed (dcp::LanguageTag tag)
381 {
382         film()->set_name_language (tag);
383 }
384
385
386 void
387 SMPTEMetadataDialog::audio_language_changed (dcp::LanguageTag tag)
388 {
389         film()->set_audio_language (tag);
390 }
391
392
393 void
394 SMPTEMetadataDialog::edit_release_territory ()
395 {
396         auto d = new RegionSubtagDialog(this, film()->release_territory());
397         d->ShowModal ();
398         auto tag = d->get();
399         if (tag) {
400                 film()->set_release_territory(*tag);
401         }
402         d->Destroy ();
403 }
404
405
406 void
407 SMPTEMetadataDialog::version_number_changed ()
408 {
409         film()->set_version_number (_version_number->GetValue());
410 }
411
412
413 void
414 SMPTEMetadataDialog::status_changed ()
415 {
416         switch (_status->GetSelection()) {
417         case 0:
418                 film()->set_status(dcp::Status::TEMP);
419                 break;
420         case 1:
421                 film()->set_status(dcp::Status::PRE);
422                 break;
423         case 2:
424                 film()->set_status(dcp::Status::FINAL);
425                 break;
426         }
427 }
428
429
430 void
431 SMPTEMetadataDialog::chain_changed ()
432 {
433         film()->set_chain (wx_to_std(_chain->GetValue()));
434 }
435
436
437 void
438 SMPTEMetadataDialog::distributor_changed ()
439 {
440         film()->set_distributor (wx_to_std(_distributor->GetValue()));
441 }
442
443
444 void
445 SMPTEMetadataDialog::facility_changed ()
446 {
447         film()->set_facility (wx_to_std(_facility->GetValue()));
448 }
449
450
451 void
452 SMPTEMetadataDialog::luminance_changed ()
453 {
454         dcp::Luminance::Unit unit;
455         switch (_luminance_unit->GetSelection()) {
456         case 0:
457                 unit = dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE;
458                 break;
459         case 1:
460                 unit = dcp::Luminance::Unit::FOOT_LAMBERT;
461                 break;
462         default:
463                 DCPOMATIC_ASSERT (false);
464         }
465
466         film()->set_luminance (dcp::Luminance(_luminance_value->GetValue(), unit));
467 }
468
469
470 void
471 SMPTEMetadataDialog::enable_main_subtitle_changed ()
472 {
473         setup_sensitivity ();
474         bool enabled = _enable_main_subtitle_language->GetValue ();
475         if (enabled) {
476                 film()->set_subtitle_language (_main_subtitle_language->get());
477         } else {
478                 set_additional_subtitle_languages (vector<dcp::LanguageTag>());
479                 _additional_subtitle_languages->refresh ();
480                 film()->unset_subtitle_language ();
481         }
482 }
483
484
485 void
486 SMPTEMetadataDialog::setup_sensitivity ()
487 {
488         bool const enabled = _enable_main_subtitle_language->GetValue ();
489         _main_subtitle_language->enable (enabled);
490         _additional_subtitle_languages->Enable (enabled);
491 }
492
493
494 void
495 SMPTEMetadataDialog::main_subtitle_language_changed (dcp::LanguageTag tag)
496 {
497         auto existing = film()->subtitle_languages();
498         if (existing.empty()) {
499                 existing.push_back (tag);
500         } else {
501                 existing[0] = tag;
502         }
503
504         film()->set_subtitle_languages (existing);
505 }
506
507
508 vector<dcp::LanguageTag>
509 SMPTEMetadataDialog::additional_subtitle_languages ()
510 {
511         auto all = film()->subtitle_languages();
512         if (all.empty()) {
513                 return all;
514         }
515
516         return vector<dcp::LanguageTag>(all.begin() + 1, all.end());
517 }
518
519
520 void
521 SMPTEMetadataDialog::set_additional_subtitle_languages (vector<dcp::LanguageTag> languages)
522 {
523         auto all = film()->subtitle_languages();
524         DCPOMATIC_ASSERT (!all.empty());
525         all.resize (1);
526         copy (languages.begin(), languages.end(), back_inserter(all));
527         film()->set_subtitle_languages (all);
528 }
529