470e9317e8073a26a61ac77d66bc523d47b55af4
[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         {
95                 int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
96 #ifdef __WXOSX__
97                 flags |= wxALIGN_RIGHT;
98 #endif
99                 auto m = create_label (panel, _("Ratings"), true);
100                 sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
101         }
102
103         vector<EditableListColumn> columns;
104         columns.push_back (EditableListColumn("Agency", 200, true));
105         columns.push_back (EditableListColumn("Label", 50, true));
106         _ratings = new EditableList<dcp::Rating, RatingDialog> (
107                 panel,
108                 columns,
109                 boost::bind(&SMPTEMetadataDialog::ratings, this),
110                 boost::bind(&SMPTEMetadataDialog::set_ratings, this, _1),
111                 boost::bind(&ratings_column, _1, _2),
112                 true,
113                 false
114                 );
115         sizer->Add (_ratings, 1, wxEXPAND);
116
117         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
118         overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
119         panel->SetSizer (overall_sizer);
120
121         return panel;
122 }
123
124
125 wxPanel *
126 SMPTEMetadataDialog::advanced_panel (wxWindow* parent)
127 {
128         auto panel = new wxPanel (parent, wxID_ANY);
129
130         auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
131         sizer->AddGrowableCol (1, 1);
132
133         _enable_release_territory = new wxCheckBox (panel, wxID_ANY, _("Release territory"));
134         sizer->Add (_enable_release_territory, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
135         {
136                 auto s = new wxBoxSizer (wxHORIZONTAL);
137                 _release_territory_text = new wxStaticText (panel, wxID_ANY, wxT(""));
138                 s->Add (_release_territory_text, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
139                 _edit_release_territory = new Button (panel, _("Edit..."));
140                 s->Add (_edit_release_territory, 0, wxLEFT, DCPOMATIC_SIZER_GAP);
141                 sizer->Add (s, 0, wxEXPAND);
142         }
143
144         add_label_to_sizer (sizer, panel, _("Version number"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
145         _version_number = new wxSpinCtrl (panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 1, 1000);
146         sizer->Add (_version_number, 0);
147
148         add_label_to_sizer (sizer, panel, _("Status"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
149         _status = new wxChoice (panel, wxID_ANY);
150         sizer->Add (_status, 0);
151
152         _enable_chain = new wxCheckBox (panel, wxID_ANY, _("Chain"));
153         sizer->Add (_enable_chain, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
154         _chain = new wxTextCtrl (panel, wxID_ANY);
155         sizer->Add (_chain, 1, wxEXPAND);
156
157         _enable_distributor = new wxCheckBox (panel, wxID_ANY, _("Distributor"));
158         sizer->Add (_enable_distributor, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
159         _distributor = new wxTextCtrl (panel, wxID_ANY);
160         sizer->Add (_distributor, 1, wxEXPAND);
161
162         _enable_facility = new wxCheckBox (panel, wxID_ANY, _("Facility"));
163         sizer->Add (_enable_facility, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
164         _facility = new wxTextCtrl (panel, wxID_ANY);
165         sizer->Add (_facility, 1, wxEXPAND);
166
167         add_label_to_sizer (sizer, panel, _("Luminance"), true, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL);
168         {
169                 auto s = new wxBoxSizer (wxHORIZONTAL);
170                 _luminance_value = new wxSpinCtrlDouble (panel, wxID_ANY);
171                 _luminance_value->SetDigits (1);
172                 _luminance_value->SetIncrement (0.1);
173                 s->Add (_luminance_value, 0);
174                 _luminance_unit = new wxChoice (panel, wxID_ANY);
175                 s->Add (_luminance_unit, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
176                 sizer->Add (s, 1, wxEXPAND);
177         }
178
179         {
180                 int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
181 #ifdef __WXOSX__
182                 flags |= wxALIGN_RIGHT;
183 #endif
184                 auto m = create_label (panel, _("Content versions"), true);
185                 sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
186         }
187
188         vector<EditableListColumn> columns;
189         columns.push_back (EditableListColumn("Version", 350, true));
190         _content_versions = new EditableList<string, ContentVersionDialog> (
191                 panel,
192                 columns,
193                 boost::bind(&SMPTEMetadataDialog::content_versions, this),
194                 boost::bind(&SMPTEMetadataDialog::set_content_versions, this, _1),
195                 boost::bind(&content_versions_column, _1, _2),
196                 true,
197                 false
198                 );
199         sizer->Add (_content_versions, 1, wxEXPAND);
200
201         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
202         overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
203         panel->SetSizer (overall_sizer);
204
205         return panel;
206 }
207
208
209 SMPTEMetadataDialog::SMPTEMetadataDialog (wxWindow* parent, weak_ptr<Film> weak_film)
210         : wxDialog (parent, wxID_ANY, _("Metadata"))
211         , WeakFilm (weak_film)
212 {
213         auto notebook = new wxNotebook (this, wxID_ANY);
214         notebook->AddPage (main_panel(notebook), _("Standard"));
215         notebook->AddPage (advanced_panel(notebook), _("Advanced"));
216
217         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
218         overall_sizer->Add (notebook, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
219
220         auto buttons = CreateSeparatedButtonSizer (wxCLOSE);
221         if (buttons) {
222                 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
223         }
224
225         SetSizer (overall_sizer);
226         overall_sizer->Layout ();
227         overall_sizer->SetSizeHints (this);
228
229         _status->Append (_("Temporary"));
230         _status->Append (_("Pre-release"));
231         _status->Append (_("Final"));
232
233         _luminance_unit->Append (wxString::FromUTF8(_("candela per m²")));
234         _luminance_unit->Append (_("foot lambert"));
235
236         _name_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::name_language_changed, this, _1));
237         _audio_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::audio_language_changed, this, _1));
238         _edit_release_territory->Bind (wxEVT_BUTTON, boost::bind(&SMPTEMetadataDialog::edit_release_territory, this));
239         _version_number->Bind (wxEVT_SPINCTRL, boost::bind(&SMPTEMetadataDialog::version_number_changed, this));
240         _status->Bind (wxEVT_CHOICE, boost::bind(&SMPTEMetadataDialog::status_changed, this));
241         _enable_chain->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_chain_changed, this));
242         _chain->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::chain_changed, this));
243         _enable_distributor->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_distributor_changed, this));
244         _distributor->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::distributor_changed, this));
245         _enable_facility->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_facility_changed, this));
246         _facility->Bind (wxEVT_TEXT, boost::bind(&SMPTEMetadataDialog::facility_changed, this));
247         _luminance_value->Bind (wxEVT_SPINCTRLDOUBLE, boost::bind(&SMPTEMetadataDialog::luminance_changed, this));
248         _luminance_unit->Bind (wxEVT_CHOICE, boost::bind(&SMPTEMetadataDialog::luminance_changed, this));
249         _enable_release_territory->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_release_territory_changed, this));
250
251         _version_number->SetFocus ();
252
253         _film_changed_connection = film()->Change.connect(boost::bind(&SMPTEMetadataDialog::film_changed, this, _1, _2));
254
255         film_changed (ChangeType::DONE, Film::Property::NAME_LANGUAGE);
256         film_changed (ChangeType::DONE, Film::Property::RELEASE_TERRITORY);
257         film_changed (ChangeType::DONE, Film::Property::VERSION_NUMBER);
258         film_changed (ChangeType::DONE, Film::Property::STATUS);
259         film_changed (ChangeType::DONE, Film::Property::CHAIN);
260         film_changed (ChangeType::DONE, Film::Property::DISTRIBUTOR);
261         film_changed (ChangeType::DONE, Film::Property::FACILITY);
262         film_changed (ChangeType::DONE, Film::Property::CONTENT_VERSIONS);
263         film_changed (ChangeType::DONE, Film::Property::LUMINANCE);
264         film_changed (ChangeType::DONE, Film::Property::SUBTITLE_LANGUAGES);
265
266         setup_sensitivity ();
267 }
268
269
270 void
271 SMPTEMetadataDialog::film_changed (ChangeType type, Film::Property property)
272 {
273         if (type != ChangeType::DONE || film()->interop()) {
274                 return;
275         }
276
277         if (property == Film::Property::NAME_LANGUAGE) {
278                 _name_language->set (film()->name_language());
279         } else if (property == Film::Property::RELEASE_TERRITORY) {
280                 auto rt = film()->release_territory();
281                 checked_set (_enable_release_territory, static_cast<bool>(rt));
282                 if (rt) {
283                         _release_territory = *rt;
284                         checked_set (_release_territory_text, std_to_wx(*dcp::LanguageTag::get_subtag_description(*_release_territory)));
285                 }
286         } else if (property == Film::Property::VERSION_NUMBER) {
287                 checked_set (_version_number, film()->version_number());
288         } else if (property == Film::Property::STATUS) {
289                 switch (film()->status()) {
290                 case dcp::Status::TEMP:
291                         checked_set (_status, 0);
292                         break;
293                 case dcp::Status::PRE:
294                         checked_set (_status, 1);
295                         break;
296                 case dcp::Status::FINAL:
297                         checked_set (_status, 2);
298                         break;
299                 }
300         } else if (property == Film::Property::CHAIN) {
301                 checked_set (_enable_chain, static_cast<bool>(film()->chain()));
302                 if (film()->chain()) {
303                         checked_set (_chain, *film()->chain());
304                 }
305         } else if (property == Film::Property::DISTRIBUTOR) {
306                 checked_set (_enable_distributor, static_cast<bool>(film()->distributor()));
307                 if (film()->distributor()) {
308                         checked_set (_distributor, *film()->distributor());
309                 }
310         } else if (property == Film::Property::FACILITY) {
311                 checked_set (_enable_facility, static_cast<bool>(film()->facility()));
312                 if (film()->facility()) {
313                         checked_set (_facility, *film()->facility());
314                 }
315         } else if (property == Film::Property::LUMINANCE) {
316                 auto lum = film()->luminance();
317                 if (lum) {
318                         checked_set (_luminance_value, lum->value());
319                         switch (lum->unit()) {
320                         case dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE:
321                                 checked_set (_luminance_unit, 0);
322                                 break;
323                         case dcp::Luminance::Unit::FOOT_LAMBERT:
324                                 checked_set (_luminance_unit, 1);
325                                 break;
326                         }
327                 } else {
328                         checked_set (_luminance_value, 4.5);
329                         checked_set (_luminance_unit, 1);
330                 }
331         }
332 }
333
334
335 vector<dcp::Rating>
336 SMPTEMetadataDialog::ratings () const
337 {
338         return film()->ratings ();
339 }
340
341
342 void
343 SMPTEMetadataDialog::set_ratings (vector<dcp::Rating> r)
344 {
345         film()->set_ratings (r);
346 }
347
348
349 vector<string>
350 SMPTEMetadataDialog::content_versions () const
351 {
352         return film()->content_versions ();
353 }
354
355
356 void
357 SMPTEMetadataDialog::set_content_versions (vector<string> cv)
358 {
359         film()->set_content_versions (cv);
360 }
361
362
363 void
364 SMPTEMetadataDialog::name_language_changed (dcp::LanguageTag tag)
365 {
366         film()->set_name_language (tag);
367 }
368
369
370 void
371 SMPTEMetadataDialog::audio_language_changed (dcp::LanguageTag tag)
372 {
373         film()->set_audio_language (tag);
374 }
375
376
377 void
378 SMPTEMetadataDialog::edit_release_territory ()
379 {
380         DCPOMATIC_ASSERT (film()->release_territory());
381         auto d = new RegionSubtagDialog(this, *film()->release_territory());
382         d->ShowModal ();
383         auto tag = d->get();
384         if (tag) {
385                 _release_territory = *tag;
386                 film()->set_release_territory(*tag);
387         }
388         d->Destroy ();
389 }
390
391
392 void
393 SMPTEMetadataDialog::version_number_changed ()
394 {
395         film()->set_version_number (_version_number->GetValue());
396 }
397
398
399 void
400 SMPTEMetadataDialog::status_changed ()
401 {
402         switch (_status->GetSelection()) {
403         case 0:
404                 film()->set_status(dcp::Status::TEMP);
405                 break;
406         case 1:
407                 film()->set_status(dcp::Status::PRE);
408                 break;
409         case 2:
410                 film()->set_status(dcp::Status::FINAL);
411                 break;
412         }
413 }
414
415
416 void
417 SMPTEMetadataDialog::chain_changed ()
418 {
419         film()->set_chain (wx_to_std(_chain->GetValue()));
420 }
421
422
423 void
424 SMPTEMetadataDialog::distributor_changed ()
425 {
426         film()->set_distributor (wx_to_std(_distributor->GetValue()));
427 }
428
429
430 void
431 SMPTEMetadataDialog::facility_changed ()
432 {
433         film()->set_facility (wx_to_std(_facility->GetValue()));
434 }
435
436
437 void
438 SMPTEMetadataDialog::luminance_changed ()
439 {
440         dcp::Luminance::Unit unit;
441         switch (_luminance_unit->GetSelection()) {
442         case 0:
443                 unit = dcp::Luminance::Unit::CANDELA_PER_SQUARE_METRE;
444                 break;
445         case 1:
446                 unit = dcp::Luminance::Unit::FOOT_LAMBERT;
447                 break;
448         default:
449                 DCPOMATIC_ASSERT (false);
450         }
451
452         film()->set_luminance (dcp::Luminance(_luminance_value->GetValue(), unit));
453 }
454
455
456 void
457 SMPTEMetadataDialog::setup_sensitivity ()
458 {
459         {
460                 auto const enabled = _enable_release_territory->GetValue();
461                 _release_territory_text->Enable (enabled);
462                 _edit_release_territory->Enable (enabled);
463         }
464
465         _chain->Enable (_enable_chain->GetValue());
466         _distributor->Enable (_enable_distributor->GetValue());
467         _facility->Enable (_enable_facility->GetValue());
468 }
469
470
471 void
472 SMPTEMetadataDialog::enable_release_territory_changed ()
473 {
474         setup_sensitivity ();
475         if (_enable_release_territory->GetValue()) {
476                 film()->set_release_territory (_release_territory.get_value_or(dcp::LanguageTag::RegionSubtag("US")));
477         } else {
478                 film()->set_release_territory ();
479         }
480 }
481
482
483 void
484 SMPTEMetadataDialog::enable_chain_changed ()
485 {
486         setup_sensitivity ();
487         if (_enable_chain->GetValue()) {
488                 film()->set_chain (wx_to_std(_chain->GetValue()));
489         } else {
490                 film()->set_chain ();
491         }
492 }
493
494
495 void
496 SMPTEMetadataDialog::enable_distributor_changed ()
497 {
498         setup_sensitivity ();
499         if (_enable_distributor->GetValue()) {
500                 film()->set_distributor (wx_to_std(_distributor->GetValue()));
501         } else {
502                 film()->set_distributor ();
503         }
504 }
505
506
507 void
508 SMPTEMetadataDialog::enable_facility_changed ()
509 {
510         setup_sensitivity ();
511         if (_enable_facility->GetValue()) {
512                 film()->set_facility (wx_to_std(_facility->GetValue()));
513         } else {
514                 film()->set_facility ();
515         }
516 }
517
518