std::shared_ptr
[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 std::shared_ptr;
36 using std::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         , WeakFilm (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 void
371 SMPTEMetadataDialog::version_number_changed ()
372 {
373         film()->set_version_number (_version_number->GetValue());
374 }
375
376
377 void
378 SMPTEMetadataDialog::status_changed ()
379 {
380         switch (_status->GetSelection()) {
381         case 0:
382                 film()->set_status (dcp::TEMP);
383                 break;
384         case 1:
385                 film()->set_status (dcp::PRE);
386                 break;
387         case 2:
388                 film()->set_status (dcp::FINAL);
389                 break;
390         }
391 }
392
393
394 void
395 SMPTEMetadataDialog::chain_changed ()
396 {
397         film()->set_chain (wx_to_std(_chain->GetValue()));
398 }
399
400
401 void
402 SMPTEMetadataDialog::distributor_changed ()
403 {
404         film()->set_distributor (wx_to_std(_distributor->GetValue()));
405 }
406
407
408 void
409 SMPTEMetadataDialog::facility_changed ()
410 {
411         film()->set_facility (wx_to_std(_facility->GetValue()));
412 }
413
414
415 void
416 SMPTEMetadataDialog::luminance_changed ()
417 {
418         dcp::Luminance::Unit unit;
419         switch (_luminance_unit->GetSelection()) {
420         case 0:
421                 unit = dcp::Luminance::CANDELA_PER_SQUARE_METRE;
422                 break;
423         case 1:
424                 unit = dcp::Luminance::FOOT_LAMBERT;
425                 break;
426         default:
427                 DCPOMATIC_ASSERT (false);
428         }
429
430         film()->set_luminance (dcp::Luminance(_luminance_value->GetValue(), unit));
431 }
432
433
434 void
435 SMPTEMetadataDialog::enable_main_subtitle_changed ()
436 {
437         setup_sensitivity ();
438         bool enabled = _enable_main_subtitle_language->GetValue ();
439         if (enabled) {
440                 film()->set_subtitle_language (_main_subtitle_language->get());
441         } else {
442                 set_additional_subtitle_languages (vector<dcp::LanguageTag>());
443                 _additional_subtitle_languages->refresh ();
444                 film()->unset_subtitle_language ();
445         }
446 }
447
448
449 void
450 SMPTEMetadataDialog::setup_sensitivity ()
451 {
452         bool const enabled = _enable_main_subtitle_language->GetValue ();
453         _main_subtitle_language->enable (enabled);
454         _additional_subtitle_languages->Enable (enabled);
455 }
456
457
458 void
459 SMPTEMetadataDialog::main_subtitle_language_changed (dcp::LanguageTag tag)
460 {
461         vector<dcp::LanguageTag> existing = film()->subtitle_languages();
462         if (existing.empty()) {
463                 existing.push_back (tag);
464         } else {
465                 existing[0] = tag;
466         }
467
468         film()->set_subtitle_languages (existing);
469 }
470
471
472 vector<dcp::LanguageTag>
473 SMPTEMetadataDialog::additional_subtitle_languages ()
474 {
475         vector<dcp::LanguageTag> all = film()->subtitle_languages();
476         if (all.empty()) {
477                 return all;
478         }
479
480         return vector<dcp::LanguageTag>(all.begin() + 1, all.end());
481 }
482
483
484 void
485 SMPTEMetadataDialog::set_additional_subtitle_languages (vector<dcp::LanguageTag> languages)
486 {
487         vector<dcp::LanguageTag> all = film()->subtitle_languages();
488         DCPOMATIC_ASSERT (!all.empty());
489         all.resize (1);
490         copy (languages.begin(), languages.end(), back_inserter(all));
491         film()->set_subtitle_languages (all);
492 }
493