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