Put subtitle language back into content from the film (#1930).
authorCarl Hetherington <cth@carlh.net>
Mon, 22 Mar 2021 23:50:11 +0000 (00:50 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 31 Mar 2021 20:48:53 +0000 (22:48 +0200)
This also adds the main/additional language flag.

Of all the considerations about how to specify subtitle language,
the most important seems to be that the language specification happens
for the content where the language is; i.e. in the content text tab.

22 files changed:
src/lib/content.h
src/lib/film.cc
src/lib/film.h
src/lib/reel_writer.cc
src/lib/subtitle_encoder.cc
src/lib/text_content.cc
src/lib/text_content.h
src/lib/writer.cc
src/wx/dcp_panel.cc
src/wx/dcp_text_track_dialog.cc
src/wx/interop_metadata_dialog.cc
src/wx/interop_metadata_dialog.h
src/wx/language_tag_dialog.cc
src/wx/language_tag_widget.cc
src/wx/language_tag_widget.h
src/wx/smpte_metadata_dialog.cc
src/wx/smpte_metadata_dialog.h
src/wx/text_panel.cc
src/wx/text_panel.h
test/isdcf_name_test.cc
test/subtitle_language_test.cc
test/subtitle_metadata_test.cc

index b8626e212c34b680175f72d5d730feaf72714522..567cd5c1f78c68901ef13cdbfcf6ff49ed81fc34 100644 (file)
@@ -196,7 +196,7 @@ public:
 
        std::shared_ptr<VideoContent> video;
        std::shared_ptr<AudioContent> audio;
-       std::list<std::shared_ptr<TextContent> > text;
+       std::list<std::shared_ptr<TextContent>> text;
        std::shared_ptr<AtmosContent> atmos;
 
        std::shared_ptr<TextContent> only_text () const;
index 46074e2ada19632b09e84c30dd2fe822d5f48930..763af8f8af1ed6ddeb7519b2f4caec671bd0c20c 100644 (file)
@@ -496,9 +496,6 @@ Film::metadata (bool with_content_paths) const
        }
        root->add_child("UserExplicitContainer")->add_child_text(_user_explicit_container ? "1" : "0");
        root->add_child("UserExplicitResolution")->add_child_text(_user_explicit_resolution ? "1" : "0");
-       for (auto i: _subtitle_languages) {
-               root->add_child("SubtitleLanguage")->add_child_text(i.to_string());
-       }
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
@@ -680,10 +677,6 @@ Film::read_metadata (optional<boost::filesystem::path> path)
        _user_explicit_container = f.optional_bool_child("UserExplicitContainer").get_value_or(true);
        _user_explicit_resolution = f.optional_bool_child("UserExplicitResolution").get_value_or(true);
 
-       for (auto i: f.node_children("SubtitleLanguage")) {
-               _subtitle_languages.push_back (dcp::LanguageTag(i->content()));
-       }
-
        list<string> notes;
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
@@ -692,41 +685,6 @@ Film::read_metadata (optional<boost::filesystem::path> path)
                set_backtrace_file (file ("backtrace.txt"));
        }
 
-       /* Around 2.15.108 we removed subtitle language state from the text content and the ISDCF
-        * metadata and put it into the Film instead.  If we've loaded an old Film let's try and fish
-        * out the settings from where they were so that they don't get lost.
-        */
-
-       optional<dcp::LanguageTag> found_language;
-
-       for (auto i: f.node_child("Playlist")->node_children("Content")) {
-               auto text = i->optional_node_child("Text");
-               if (text && text->optional_string_child("Language") && !found_language) {
-                       try {
-                               found_language = dcp::LanguageTag(text->string_child("Language"));
-                       } catch (...) {}
-               }
-       }
-
-       if (_state_version >= 9) {
-               auto isdcf_language = f.node_child("ISDCFMetadata")->optional_string_child("SubtitleLanguage");
-               if (isdcf_language && !found_language) {
-                       try {
-                               found_language = dcp::LanguageTag(*isdcf_language);
-                       } catch (...) {
-                               try {
-                                       found_language = dcp::LanguageTag(boost::algorithm::to_lower_copy(*isdcf_language));
-                               } catch (...) {
-
-                               }
-                       }
-               }
-       }
-
-       if (found_language) {
-               _subtitle_languages.push_back (*found_language);
-       }
-
        _dirty = false;
        return notes;
 }
@@ -793,6 +751,30 @@ Film::mapped_audio_channels () const
        return mapped;
 }
 
+
+pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>>
+Film::subtitle_languages () const
+{
+       pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>> result;
+       for (auto i: content()) {
+               for (auto j: i->text) {
+                       if (j->use() && j->type() == TextType::OPEN_SUBTITLE && j->language()) {
+                               if (j->language_is_additional()) {
+                                       result.second.push_back (j->language().get());
+                               } else {
+                                       result.first = j->language().get();
+                               }
+                       }
+               }
+       }
+
+       std::sort (result.second.begin(), result.second.end());
+       auto last = std::unique (result.second.begin(), result.second.end());
+       result.second.erase (last, result.second.end());
+       return result;
+}
+
+
 /** @return a ISDCF-compliant name for a DCP of this film */
 string
 Film::isdcf_name (bool if_created_now) const
@@ -924,8 +906,9 @@ Film::isdcf_name (bool if_created_now) const
                }
        }
 
-       if (!_subtitle_languages.empty()) {
-               auto lang = _subtitle_languages.front().language().get_value_or("en").subtag();
+       auto sublangs = subtitle_languages();
+       if (sublangs.first && sublangs.first->language()) {
+               auto lang = sublangs.first->language()->subtag();
                if (burnt_in) {
                        transform (lang.begin(), lang.end(), lang.begin(), ::tolower);
                } else {
@@ -2037,29 +2020,6 @@ Film::set_luminance (optional<dcp::Luminance> l)
 }
 
 
-void
-Film::set_subtitle_language (dcp::LanguageTag language)
-{
-       set_subtitle_languages ({language});
-}
-
-
-void
-Film::unset_subtitle_language ()
-{
-       FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES);
-       _subtitle_languages.clear();
-}
-
-
-void
-Film::set_subtitle_languages (vector<dcp::LanguageTag> languages)
-{
-       FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES);
-       _subtitle_languages = languages;
-}
-
-
 void
 Film::set_facility (optional<string> f)
 {
index 9b45fd073c64fb1fdb3025949fe957b6afb3951b..9feb5d0d3d7d466c5566d4918126726c1123d881 100644 (file)
@@ -184,6 +184,7 @@ public:
 
        std::list<dcpomatic::DCPTimePeriod> reels () const;
        std::list<int> mapped_audio_channels () const;
+       std::pair<boost::optional<dcp::LanguageTag>, std::vector<dcp::LanguageTag>> subtitle_languages () const;
 
        std::string content_summary (dcpomatic::DCPTimePeriod period) const;
 
@@ -376,10 +377,6 @@ public:
                return _luminance;
        }
 
-       std::vector<dcp::LanguageTag> subtitle_languages () const {
-               return _subtitle_languages;
-       }
-
        /* SET */
 
        void set_directory (boost::filesystem::path);
@@ -421,9 +418,6 @@ public:
        void set_facility (boost::optional<std::string> f = boost::none);
        void set_distributor (boost::optional<std::string> d = boost::none);
        void set_luminance (boost::optional<dcp::Luminance> l = boost::none);
-       void set_subtitle_language (dcp::LanguageTag language);
-       void unset_subtitle_language ();
-       void set_subtitle_languages (std::vector<dcp::LanguageTag> languages);
 
        void add_ffoc_lfoc (Markers& markers) const;
 
@@ -529,7 +523,6 @@ private:
        boost::optional<std::string> _distributor;
        boost::optional<std::string> _facility;
        boost::optional<dcp::Luminance> _luminance;
-       std::vector<dcp::LanguageTag> _subtitle_languages;
 
        int _state_version;
 
index 366e6edc63271ac8c5152505be6910471b3d9636..2c55f0f064214bbd6e6764724a3642d46fe379a8 100644 (file)
@@ -623,8 +623,8 @@ ReelWriter::create_reel_text (
 
        if (subtitle) {
                /* We have a subtitle asset that we either made or are referencing */
-               if (!film()->subtitle_languages().empty()) {
-                       subtitle->set_language (film()->subtitle_languages().front());
+               if (auto main_language = film()->subtitle_languages().first) {
+                       subtitle->set_language (*main_language);
                }
        } else if (ensure_subtitles) {
                /* We had no subtitle asset, but we've been asked to make sure there is one */
@@ -776,7 +776,7 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const
                auto s = make_shared<dcp::InteropSubtitleAsset>();
                s->set_movie_title (film()->name());
                if (type == TextType::OPEN_SUBTITLE) {
-                       s->set_language (lang.empty() ? "Unknown" : lang.front().to_string());
+                       s->set_language (lang.first ? lang.first->to_string() : "Unknown");
                } else if (!track->language.empty()) {
                        s->set_language (track->language);
                }
@@ -786,8 +786,8 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const
                auto s = make_shared<dcp::SMPTESubtitleAsset>();
                s->set_content_title_text (film()->name());
                s->set_metadata (mxf_metadata());
-               if (type == TextType::OPEN_SUBTITLE && !lang.empty()) {
-                       s->set_language (lang.front());
+               if (type == TextType::OPEN_SUBTITLE && lang.first) {
+                       s->set_language (*lang.first);
                } else if (track && !track->language.empty()) {
                        s->set_language (dcp::LanguageTag(track->language));
                }
index 3721ee02b5835c9d8021fbb5aa500dc48ddf821a..b61876ad6568f0fada6deb82d304a556f3c987aa 100644 (file)
@@ -135,20 +135,20 @@ SubtitleEncoder::text (PlayerText subs, TextType type, optional<DCPTextTrack> tr
 
        if (!_assets[_reel_index].first) {
                shared_ptr<dcp::SubtitleAsset> asset;
-               vector<dcp::LanguageTag> lang = _film->subtitle_languages ();
+               auto lang = _film->subtitle_languages ();
                if (_film->interop ()) {
                        auto s = make_shared<dcp::InteropSubtitleAsset>();
                        s->set_movie_title (_film->name());
-                       if (!lang.empty()) {
-                               s->set_language (lang.front().to_string());
+                       if (lang.first) {
+                               s->set_language (lang.first->to_string());
                        }
                        s->set_reel_number (raw_convert<string>(_reel_index + 1));
                        _assets[_reel_index].first = s;
                } else {
                        auto s = make_shared<dcp::SMPTESubtitleAsset>();
                        s->set_content_title_text (_film->name());
-                       if (!lang.empty()) {
-                               s->set_language (lang.front());
+                       if (lang.first) {
+                               s->set_language (*lang.first);
                        } else if (!track->language.empty()) {
                                s->set_language (dcp::LanguageTag(track->language));
                        }
index 0c25e56969bc462d21dbba1a58e7161ecffa3607..c86150881418121b32c59e8d7187d72f6ca3aa18 100644 (file)
@@ -18,6 +18,7 @@
 
 */
 
+
 #include "text_content.h"
 #include "util.h"
 #include "exceptions.h"
@@ -30,6 +31,7 @@
 
 #include "i18n.h"
 
+
 using std::string;
 using std::vector;
 using std::cout;
@@ -41,6 +43,7 @@ using boost::optional;
 using dcp::raw_convert;
 using namespace dcpomatic;
 
+
 int const TextContentProperty::X_OFFSET = 500;
 int const TextContentProperty::Y_OFFSET = 501;
 int const TextContentProperty::X_SCALE = 502;
@@ -57,6 +60,9 @@ int const TextContentProperty::FADE_OUT = 512;
 int const TextContentProperty::OUTLINE_WIDTH = 513;
 int const TextContentProperty::TYPE = 514;
 int const TextContentProperty::DCP_TRACK = 515;
+int const TextContentProperty::LANGUAGE = 516;
+int const TextContentProperty::LANGUAGE_IS_ADDITIONAL = 517;
+
 
 TextContent::TextContent (Content* parent, TextType type, TextType original_type)
        : ContentPart (parent)
@@ -95,9 +101,7 @@ TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
                if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) {
                        return {};
                }
-               list<shared_ptr<TextContent>> c;
-               c.push_back (make_shared<TextContent>(parent, node, version));
-               return c;
+               return { make_shared<TextContent>(parent, node, version) };
        }
 
        if (!node->optional_node_child("Text")) {
@@ -124,6 +128,7 @@ TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version)
        , _outline_width (node->optional_number_child<int>("OutlineWidth").get_value_or(4))
        , _type (TextType::OPEN_SUBTITLE)
        , _original_type (TextType::OPEN_SUBTITLE)
+       , _language ("en-US")
 {
        if (version >= 37) {
                _use = node->bool_child ("Use");
@@ -231,6 +236,13 @@ TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version)
        if (dt) {
                _dcp_track = DCPTextTrack (dt);
        }
+
+       auto lang = node->optional_node_child("Language");
+       if (lang) {
+               _language = dcp::LanguageTag(lang->content());
+               auto add = lang->optional_bool_attribute("Additional");
+               _language_is_additional = add && *add;
+       }
 }
 
 TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c)
@@ -290,6 +302,14 @@ TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c)
                        throw JoinError (_("Content to be joined must use the same DCP track."));
                }
 
+               if (c[i]->only_text()->language() != ref->language()) {
+                       throw JoinError (_("Content to be joined must use the same text language."));
+               }
+
+               if (c[i]->only_text()->language_is_additional() != ref->language_is_additional()) {
+                       throw JoinError (_("Content to be joined must both be main subtitle languages or both additional."));
+               }
+
                auto j = ref_fonts.begin ();
                auto k = fonts.begin ();
 
@@ -316,6 +336,8 @@ TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c)
        _type = ref->type ();
        _original_type = ref->original_type ();
        _dcp_track = ref->dcp_track ();
+       _language = ref->language ();
+       _language_is_additional = ref->language_is_additional ();
 
        connect_to_fonts ();
 }
@@ -375,6 +397,11 @@ TextContent::as_xml (xmlpp::Node* root) const
        if (_dcp_track) {
                _dcp_track->as_xml(text->add_child("DCPTrack"));
        }
+       if (_language) {
+               auto lang = text->add_child("Language");
+               lang->add_child_text (_language->to_string());
+               lang->set_attribute ("Additional", _language_is_additional ? "1" : "0");
+       }
 }
 
 string
@@ -400,7 +427,7 @@ TextContent::identifier () const
                s += "_" + f->file().get_value_or("Default").string();
        }
 
-       /* The DCP track is for metadata only, and doesn't affect how this content looks */
+       /* The DCP track and language are for metadata only, and don't affect how this content looks */
 
        return s;
 }
@@ -559,6 +586,18 @@ TextContent::unset_dcp_track ()
        maybe_set (_dcp_track, optional<DCPTextTrack>(), TextContentProperty::DCP_TRACK);
 }
 
+void
+TextContent::set_language (optional<dcp::LanguageTag> language)
+{
+       maybe_set (_language, language, TextContentProperty::LANGUAGE);
+}
+
+void
+TextContent::set_language_is_additional (bool additional)
+{
+       maybe_set (_language_is_additional, additional, TextContentProperty::LANGUAGE_IS_ADDITIONAL);
+}
+
 void
 TextContent::take_settings_from (shared_ptr<const TextContent> c)
 {
@@ -595,4 +634,6 @@ TextContent::take_settings_from (shared_ptr<const TextContent> c)
        } else {
                unset_dcp_track ();
        }
+       set_language (c->_language);
+       set_language_is_additional (c->_language_is_additional);
 }
index e566d05524721fc952f4cbaeb4fd4806d8c4df95..4c6918a4220d0d5cc39610f7d20a1e89245199cf 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #ifndef DCPOMATIC_CAPTION_CONTENT_H
 #define DCPOMATIC_CAPTION_CONTENT_H
 
+
 #include "content_part.h"
 #include "dcp_text_track.h"
 #include <libcxml/cxml.h>
+#include <dcp/language_tag.h>
 #include <dcp/types.h>
 #include <boost/signals2.hpp>
 
+
 namespace dcpomatic {
        class Font;
 }
@@ -50,8 +54,11 @@ public:
        static int const OUTLINE_WIDTH;
        static int const TYPE;
        static int const DCP_TRACK;
+       static int const LANGUAGE;
+       static int const LANGUAGE_IS_ADDITIONAL;
 };
 
+
 /** @class TextContent
  *  @brief Description of how some text content should be presented.
  *
@@ -92,6 +99,8 @@ public:
        void set_type (TextType type);
        void set_dcp_track (DCPTextTrack track);
        void unset_dcp_track ();
+       void set_language (boost::optional<dcp::LanguageTag> language = boost::none);
+       void set_language_is_additional (bool additional);
 
        bool use () const {
                boost::mutex::scoped_lock lm (_mutex);
@@ -178,7 +187,17 @@ public:
                return _dcp_track;
        }
 
-       static std::list<std::shared_ptr<TextContent> > from_xml (Content* parent, cxml::ConstNodePtr, int version);
+       boost::optional<dcp::LanguageTag> language () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _language;
+       }
+
+       bool language_is_additional () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _language_is_additional;
+       }
+
+       static std::list<std::shared_ptr<TextContent>> from_xml (Content* parent, cxml::ConstNodePtr, int version);
 
 private:
        friend struct ffmpeg_pts_offset_test;
@@ -219,6 +238,8 @@ private:
        TextType _original_type;
        /** the track of closed captions that this content should be put in, or empty to put in the default (only) track */
        boost::optional<DCPTextTrack> _dcp_track;
+       boost::optional<dcp::LanguageTag> _language;
+       bool _language_is_additional = false;
 };
 
 #endif
index 839156d34be51534db48b7894373c98e9ce08cef..3448c5bcad1b5d33ab47be94c336148d496c0430 100644 (file)
@@ -663,9 +663,9 @@ Writer::finish (boost::filesystem::path output_dcp)
                cpl->set_main_picture_active_area (active_area);
        }
 
-       vector<dcp::LanguageTag> sl = film()->subtitle_languages();
-       if (sl.size() > 1) {
-               cpl->set_additional_subtitle_languages(std::vector<dcp::LanguageTag>(sl.begin() + 1, sl.end()));
+       auto sl = film()->subtitle_languages().second;
+       if (!sl.empty()) {
+               cpl->set_additional_subtitle_languages(sl);
        }
 
        auto signer = Config::instance()->signer_chain();
@@ -709,10 +709,10 @@ Writer::write_cover_sheet (boost::filesystem::path output_dcp)
        boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", film()->isdcf_metadata().audio_language);
 
        auto subtitle_languages = film()->subtitle_languages();
-       if (subtitle_languages.empty()) {
-               boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", "None");
+       if (subtitle_languages.first) {
+               boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.first->description());
        } else {
-               boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.front().description());
+               boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", "None");
        }
 
        boost::uintmax_t size = 0;
index 6de9e3d75942590c8d4b76c3647b285700dbefa8..3304f4d3c1d717d606b389afec3f4259cc7ba017 100644 (file)
@@ -456,6 +456,8 @@ DCPPanel::film_content_changed (int property)
        if (property == AudioContentProperty::STREAMS ||
            property == TextContentProperty::USE ||
            property == TextContentProperty::BURN ||
+           property == TextContentProperty::LANGUAGE ||
+           property == TextContentProperty::LANGUAGE_IS_ADDITIONAL ||
            property == VideoContentProperty::SCALE ||
            property == DCPContentProperty::REFERENCE_VIDEO ||
            property == DCPContentProperty::REFERENCE_AUDIO ||
index 3e8750cd5bff82f405a19bf85ad0fcdca895e81b..b884aba51e387c9f1cc7cfb48a8bc2561bfee4b4 100644 (file)
@@ -43,5 +43,6 @@ DCPTextTrackDialog::DCPTextTrackDialog (wxWindow* parent)
 DCPTextTrack
 DCPTextTrackDialog::get () const
 {
-       return DCPTextTrack(wx_to_std(_name->GetValue()), _language->get().to_string());
+       DCPOMATIC_ASSERT (_language->get());
+       return DCPTextTrack(wx_to_std(_name->GetValue()), _language->get()->to_string());
 }
index 7976e6d444fa5c9fbf57a0180430eed7f299aa3a..aa61984b36275c9d388005872167b1ed771793b2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
-#include "interop_metadata_dialog.h"
+
 #include "editable_list.h"
+#include "interop_metadata_dialog.h"
 #include "language_tag_widget.h"
 #include "rating_dialog.h"
 #include "lib/film.h"
 #include <dcp/types.h>
 #include <wx/gbsizer.h>
 
+
 using std::string;
 using std::vector;
 using std::weak_ptr;
@@ -35,42 +37,25 @@ using namespace boost::placeholders;
 #endif
 
 
-static string
-column (dcp::Rating r, int c)
-{
-       if (c == 0) {
-               return r.agency;
-       }
-
-       return r.label;
-}
-
 InteropMetadataDialog::InteropMetadataDialog (wxWindow* parent, weak_ptr<Film> film)
        : wxDialog (parent, wxID_ANY, _("Metadata"))
        , _film (film)
 {
-       wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+       auto overall_sizer = new wxBoxSizer (wxVERTICAL);
        SetSizer (overall_sizer);
 
-       wxFlexGridSizer* sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+       auto sizer = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
        sizer->AddGrowableCol (1, 1);
 
-       shared_ptr<Film> f = _film.lock();
+       auto f = _film.lock();
        DCPOMATIC_ASSERT (f);
 
-       _enable_subtitle_language = new wxCheckBox (this, wxID_ANY, _("Subtitle language"));
-       sizer->Add (_enable_subtitle_language, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
-       vector<dcp::LanguageTag> langs = f->subtitle_languages ();
-       _enable_subtitle_language->SetValue (!langs.empty());
-       _subtitle_language = new LanguageTagWidget (this, wxT(""), langs.empty() ? dcp::LanguageTag("en-US") : langs.front());
-       sizer->Add (_subtitle_language->sizer(), 1, wxEXPAND);
-
        {
                int flags = wxALIGN_TOP | wxLEFT | wxRIGHT | wxTOP;
 #ifdef __WXOSX__
                flags |= wxALIGN_RIGHT;
 #endif
-               wxStaticText* m = create_label (this, _("Ratings"), true);
+               auto m = create_label (this, _("Ratings"), true);
                sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
        }
 
@@ -82,7 +67,12 @@ InteropMetadataDialog::InteropMetadataDialog (wxWindow* parent, weak_ptr<Film> f
                columns,
                boost::bind(&InteropMetadataDialog::ratings, this),
                boost::bind(&InteropMetadataDialog::set_ratings, this, _1),
-               boost::bind(&column, _1, _2),
+               [](dcp::Rating r, int c) {
+                       if (c == 0) {
+                               return r.agency;
+                       }
+                       return r.label;
+               },
                true,
                false
                );
@@ -92,12 +82,12 @@ InteropMetadataDialog::InteropMetadataDialog (wxWindow* parent, weak_ptr<Film> f
        _content_version = new wxTextCtrl (this, wxID_ANY);
        sizer->Add (_content_version, 1, wxEXPAND);
 
-       vector<string> cv = f->content_versions();
+       auto cv = f->content_versions();
        _content_version->SetValue (std_to_wx(cv.empty() ? "" : cv[0]));
 
        overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
 
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxCLOSE);
+       auto buttons = CreateSeparatedButtonSizer (wxCLOSE);
        if (buttons) {
                overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
        }
@@ -105,45 +95,15 @@ InteropMetadataDialog::InteropMetadataDialog (wxWindow* parent, weak_ptr<Film> f
        overall_sizer->Layout ();
        overall_sizer->SetSizeHints (this);
 
-       _enable_subtitle_language->Bind (wxEVT_CHECKBOX, boost::bind(&InteropMetadataDialog::setup_sensitivity, this));
-       _subtitle_language->Changed.connect (boost::bind(&InteropMetadataDialog::subtitle_language_changed, this, _1));
-
        _content_version->Bind (wxEVT_TEXT, boost::bind(&InteropMetadataDialog::content_version_changed, this));
        _content_version->SetFocus ();
-
-       setup_sensitivity ();
-}
-
-
-void
-InteropMetadataDialog::setup_sensitivity ()
-{
-       bool const enabled = _enable_subtitle_language->GetValue();
-       _subtitle_language->enable (enabled);
-
-       shared_ptr<Film> film = _film.lock ();
-       DCPOMATIC_ASSERT (film);
-       if (enabled) {
-               film->set_subtitle_language (_subtitle_language->get());
-       } else {
-               film->unset_subtitle_language ();
-       }
-}
-
-
-void
-InteropMetadataDialog::subtitle_language_changed (dcp::LanguageTag language)
-{
-       shared_ptr<Film> film = _film.lock ();
-       DCPOMATIC_ASSERT (film);
-       film->set_subtitle_language (language);
 }
 
 
 vector<dcp::Rating>
 InteropMetadataDialog::ratings () const
 {
-       shared_ptr<Film> film = _film.lock ();
+       auto film = _film.lock ();
        DCPOMATIC_ASSERT (film);
        return film->ratings ();
 }
@@ -151,7 +111,7 @@ InteropMetadataDialog::ratings () const
 void
 InteropMetadataDialog::set_ratings (vector<dcp::Rating> r)
 {
-       shared_ptr<Film> film = _film.lock ();
+       auto film = _film.lock ();
        DCPOMATIC_ASSERT (film);
        film->set_ratings (r);
 }
@@ -159,7 +119,7 @@ InteropMetadataDialog::set_ratings (vector<dcp::Rating> r)
 void
 InteropMetadataDialog::content_version_changed ()
 {
-       shared_ptr<Film> film = _film.lock ();
+       auto film = _film.lock ();
        DCPOMATIC_ASSERT (film);
        vector<string> cv;
        cv.push_back (wx_to_std(_content_version->GetValue()));
index 6e4eea40eaf7e1804cabefdbb964aeb286d9dc1f..b8a1a36e61c121d0c74e0f54b66a3c5033856805 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #include "editable_list.h"
 #include <dcp/language_tag.h>
 #include <dcp/types.h>
 #include <wx/wx.h>
 #include <vector>
 
+
 class Film;
 class LanguageTagWidget;
 class RatingDialog;
@@ -38,12 +40,8 @@ private:
        std::vector<dcp::Rating> ratings () const;
        void set_ratings (std::vector<dcp::Rating> r);
        void content_version_changed ();
-       void setup_sensitivity ();
-       void subtitle_language_changed (dcp::LanguageTag tag);
 
        std::weak_ptr<Film> _film;
-       wxCheckBox* _enable_subtitle_language;
-       LanguageTagWidget* _subtitle_language;
        EditableList<dcp::Rating, RatingDialog>* _ratings;
        wxTextCtrl* _content_version;
 };
index 96e7c528390b170a09a760cdad322249e4dbe554..c72c64b313c07f0465553d297a5ac0973fe0d816 100644 (file)
@@ -125,6 +125,7 @@ LanguageTagDialog::set (dcp::LanguageTag tag)
        }
 
        _list->SetItemState (selection, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+       _list->EnsureVisible (selection);
 }
 
 
index d71b5fc7aeef9597714dda432a944dc5c86a7ce0..f0766c9d659832102265ba6d8f57c7d4156d26c1 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include <wx/wx.h>
 
 
-LanguageTagWidget::LanguageTagWidget (wxWindow* parent, wxString tooltip, dcp::LanguageTag tag)
+using boost::optional;
+
+
+LanguageTagWidget::LanguageTagWidget (wxWindow* parent, wxString tooltip, optional<dcp::LanguageTag> tag, optional<wxString> size_to_fit)
        : _parent (parent)
        , _sizer (new wxBoxSizer(wxHORIZONTAL))
 {
-       _language = new wxStaticText (parent, wxID_ANY, wxT(""));
+       _language = new wxStaticText (parent, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
        _language->SetToolTip (tooltip);
        set (tag);
+
+       if (size_to_fit) {
+               int w;
+               int h;
+               _language->GetTextExtent (*size_to_fit, &w, &h);
+               _language->SetMinSize (wxSize(w, -1));
+       }
+
        _sizer->Add (_language, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
        _edit = new Button (parent, _("Edit..."));
        _sizer->Add (_edit, 0, wxLEFT, DCPOMATIC_SIZER_GAP);
@@ -41,10 +52,17 @@ LanguageTagWidget::LanguageTagWidget (wxWindow* parent, wxString tooltip, dcp::L
 }
 
 
+LanguageTagWidget::~LanguageTagWidget ()
+{
+       _language->Destroy ();
+       _edit->Destroy ();
+}
+
+
 void
 LanguageTagWidget::edit ()
 {
-       auto d = new LanguageTagDialog(_parent, _tag);
+       auto d = new LanguageTagDialog(_parent, _tag.get_value_or(dcp::LanguageTag("en")));
        d->ShowModal ();
        set (d->get());
        Changed (d->get());
@@ -53,10 +71,14 @@ LanguageTagWidget::edit ()
 
 
 void
-LanguageTagWidget::set (dcp::LanguageTag tag)
+LanguageTagWidget::set (optional<dcp::LanguageTag> tag)
 {
        _tag = tag;
-       checked_set (_language, std_to_wx(tag.to_string()));
+       if (tag) {
+               checked_set (_language, std_to_wx(tag->to_string()));
+       } else {
+               checked_set (_language, wxT(""));
+       }
 }
 
 
index 336f7dce234316d6d2cc2d155839fdac618065a6..c2fd63d926f20c53d54130d3889b4b42f29f15d9 100644 (file)
@@ -34,16 +34,17 @@ class wxWindow;
 class LanguageTagWidget : public boost::noncopyable
 {
 public:
-       LanguageTagWidget (wxWindow* parent, wxString tooltip, dcp::LanguageTag tag);
+       LanguageTagWidget (wxWindow* parent, wxString tooltip, boost::optional<dcp::LanguageTag> tag, boost::optional<wxString> size_to_fit = boost::none);
+       ~LanguageTagWidget ();
 
        wxSizer* sizer () const {
                return _sizer;
        }
 
-       dcp::LanguageTag get () const {
+       boost::optional<dcp::LanguageTag> get () const {
                return _tag;
        }
-       void set (dcp::LanguageTag tag);
+       void set (boost::optional<dcp::LanguageTag> tag);
        void enable (bool e);
 
        boost::signals2::signal<void (dcp::LanguageTag)> Changed;
@@ -54,7 +55,7 @@ private:
        wxStaticText* _language;
        wxButton* _edit;
        wxWindow* _parent;
-       dcp::LanguageTag _tag;
+       boost::optional<dcp::LanguageTag> _tag;
        wxSizer* _sizer;
 };
 
index 36cf4848918d756e8b088c9ee83637819446ce4d..470e9317e8073a26a61ac77d66bc523d47b55af4 100644 (file)
@@ -91,38 +91,6 @@ SMPTEMetadataDialog::main_panel (wxWindow* parent)
                );
        sizer->Add (_audio_language->sizer(), 0, wxEXPAND);
 
-       _enable_main_subtitle_language = new wxCheckBox (panel, wxID_ANY, _("Main subtitle language"));
-       sizer->Add (_enable_main_subtitle_language, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_GAP);
-       auto subtitle_languages = film()->subtitle_languages();
-       _main_subtitle_language = new LanguageTagWidget(
-               panel,
-               _("The main language that is displayed in the film's subtitles"),
-               subtitle_languages.empty() ? dcp::LanguageTag("en-US") : subtitle_languages.front()
-               );
-       sizer->Add (_main_subtitle_language->sizer(), 0, wxEXPAND);
-
-       {
-               int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
-#ifdef __WXOSX__
-               flags |= wxALIGN_RIGHT;
-#endif
-               auto m = create_label (panel, _("Additional subtitle languages"), true);
-               sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
-       }
-
-       vector<EditableListColumn> columns;
-       columns.push_back (EditableListColumn("Language", 250, true));
-       _additional_subtitle_languages = new EditableList<dcp::LanguageTag, LanguageTagDialog> (
-               panel,
-               columns,
-               boost::bind(&SMPTEMetadataDialog::additional_subtitle_languages, this),
-               boost::bind(&SMPTEMetadataDialog::set_additional_subtitle_languages, this, _1),
-               boost::bind(&additional_subtitle_language_column, _1, _2),
-               true,
-               false
-               );
-       sizer->Add (_additional_subtitle_languages, 1, wxEXPAND);
-
        {
                int flags = wxALIGN_TOP | wxRIGHT | wxTOP;
 #ifdef __WXOSX__
@@ -132,7 +100,7 @@ SMPTEMetadataDialog::main_panel (wxWindow* parent)
                sizer->Add (m, 0, flags, DCPOMATIC_SIZER_GAP);
        }
 
-       columns.clear ();
+       vector<EditableListColumn> columns;
        columns.push_back (EditableListColumn("Agency", 200, true));
        columns.push_back (EditableListColumn("Label", 50, true));
        _ratings = new EditableList<dcp::Rating, RatingDialog> (
@@ -267,9 +235,7 @@ SMPTEMetadataDialog::SMPTEMetadataDialog (wxWindow* parent, weak_ptr<Film> weak_
 
        _name_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::name_language_changed, this, _1));
        _audio_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::audio_language_changed, this, _1));
-       _enable_main_subtitle_language->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_main_subtitle_changed, this));
        _edit_release_territory->Bind (wxEVT_BUTTON, boost::bind(&SMPTEMetadataDialog::edit_release_territory, this));
-       _main_subtitle_language->Changed.connect (boost::bind(&SMPTEMetadataDialog::main_subtitle_language_changed, this, _1));
        _version_number->Bind (wxEVT_SPINCTRL, boost::bind(&SMPTEMetadataDialog::version_number_changed, this));
        _status->Bind (wxEVT_CHOICE, boost::bind(&SMPTEMetadataDialog::status_changed, this));
        _enable_chain->Bind (wxEVT_CHECKBOX, boost::bind(&SMPTEMetadataDialog::enable_chain_changed, this));
@@ -362,14 +328,6 @@ SMPTEMetadataDialog::film_changed (ChangeType type, Film::Property property)
                        checked_set (_luminance_value, 4.5);
                        checked_set (_luminance_unit, 1);
                }
-       } else if (property == Film::Property::SUBTITLE_LANGUAGES) {
-               auto languages = film()->subtitle_languages();
-               checked_set (_enable_main_subtitle_language, !languages.empty());
-               if (!languages.empty()) {
-                       _main_subtitle_language->set (languages.front());
-               } else {
-                       _main_subtitle_language->set (dcp::LanguageTag("en-US"));
-               }
        }
 }
 
@@ -495,20 +453,6 @@ SMPTEMetadataDialog::luminance_changed ()
 }
 
 
-void
-SMPTEMetadataDialog::enable_main_subtitle_changed ()
-{
-       setup_sensitivity ();
-       if (_enable_main_subtitle_language->GetValue()) {
-               film()->set_subtitle_language (_main_subtitle_language->get());
-       } else {
-               set_additional_subtitle_languages (vector<dcp::LanguageTag>());
-               _additional_subtitle_languages->refresh ();
-               film()->unset_subtitle_language ();
-       }
-}
-
-
 void
 SMPTEMetadataDialog::setup_sensitivity ()
 {
@@ -521,49 +465,6 @@ SMPTEMetadataDialog::setup_sensitivity ()
        _chain->Enable (_enable_chain->GetValue());
        _distributor->Enable (_enable_distributor->GetValue());
        _facility->Enable (_enable_facility->GetValue());
-
-       {
-               auto const enabled = _enable_main_subtitle_language->GetValue();
-               _main_subtitle_language->enable (enabled);
-               _additional_subtitle_languages->Enable (enabled);
-       }
-}
-
-
-void
-SMPTEMetadataDialog::main_subtitle_language_changed (dcp::LanguageTag tag)
-{
-       auto existing = film()->subtitle_languages();
-       if (existing.empty()) {
-               existing.push_back (tag);
-       } else {
-               existing[0] = tag;
-       }
-
-       film()->set_subtitle_languages (existing);
-}
-
-
-vector<dcp::LanguageTag>
-SMPTEMetadataDialog::additional_subtitle_languages ()
-{
-       auto all = film()->subtitle_languages();
-       if (all.empty()) {
-               return all;
-       }
-
-       return vector<dcp::LanguageTag>(all.begin() + 1, all.end());
-}
-
-
-void
-SMPTEMetadataDialog::set_additional_subtitle_languages (vector<dcp::LanguageTag> languages)
-{
-       auto all = film()->subtitle_languages();
-       DCPOMATIC_ASSERT (!all.empty());
-       all.resize (1);
-       copy (languages.begin(), languages.end(), back_inserter(all));
-       film()->set_subtitle_languages (all);
 }
 
 
index 63d90249c212fc69ea2147b48a600980c2e16643..83c1ab39cde3feeceb5005f72f089e7fba2b972c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -18,6 +18,7 @@
 
 */
 
+
 #include "editable_list.h"
 #include "full_language_tag_dialog.h"
 #include "lib/film.h"
@@ -49,10 +50,6 @@ private:
        void set_content_versions (std::vector<std::string> v);
        void name_language_changed (dcp::LanguageTag tag);
        void audio_language_changed (dcp::LanguageTag tag);
-       void enable_main_subtitle_changed ();
-       void main_subtitle_language_changed (dcp::LanguageTag tag);
-       std::vector<dcp::LanguageTag> additional_subtitle_languages ();
-       void set_additional_subtitle_languages (std::vector<dcp::LanguageTag> languages);
        void edit_release_territory ();
        void version_number_changed ();
        void status_changed ();
@@ -69,9 +66,6 @@ private:
 
        LanguageTagWidget* _name_language;
        LanguageTagWidget* _audio_language;
-       wxCheckBox* _enable_main_subtitle_language;
-       LanguageTagWidget* _main_subtitle_language;
-       EditableList<dcp::LanguageTag, LanguageTagDialog>* _additional_subtitle_languages;
        wxCheckBox* _enable_release_territory;
        /** The current release territory displayed in the UI; since we can't easily convert
         *  the string in _release_territory_text to a RegionSubtag we just store the RegionSubtag
index 3b617a03144f6f550bb6ed56514ec82b17123a67..5e00d4f41b820cd1a6aee5c514ca92a99101de1b 100644 (file)
@@ -18,7 +18,6 @@
 
 */
 
-
 #include "check_box.h"
 #include "content_panel.h"
 #include "dcp_text_track_dialog.h"
@@ -27,6 +26,7 @@
 #include "film_editor.h"
 #include "film_viewer.h"
 #include "fonts_dialog.h"
+#include "language_tag_widget.h"
 #include "static_text.h"
 #include "subtitle_appearance_dialog.h"
 #include "text_panel.h"
@@ -153,7 +153,7 @@ TextPanel::setup_visibility ()
        case TextType::OPEN_SUBTITLE:
                if (_dcp_track_label) {
                        _dcp_track_label->Destroy ();
-                       _dcp_track_label = 0;
+                       _dcp_track_label = nullptr;
                }
                if (_dcp_track) {
                        _dcp_track->Destroy ();
@@ -164,23 +164,51 @@ TextPanel::setup_visibility ()
                        _outline_subtitles->Bind (wxEVT_CHECKBOX, boost::bind (&TextPanel::outline_subtitles_changed, this));
                        _grid->Add (_outline_subtitles, wxGBPosition(_outline_subtitles_row, 0), wxGBSpan(1, 2));
                }
-
+               if (!_language) {
+                       _language_label = create_label (this, _("Language"), true);
+                       add_label_to_sizer (_grid, _language_label, true, wxGBPosition(_ccap_track_or_language_row, 0));
+                       _language_sizer = new wxBoxSizer (wxHORIZONTAL);
+                       _language = new LanguageTagWidget (this, _("Language of these subtitles"), boost::none, wxString("en-US-"));
+                       _language->Changed.connect (boost::bind(&TextPanel::language_changed, this));
+                       _language_sizer->Add (_language->sizer(), 1, wxRIGHT, DCPOMATIC_SIZER_GAP);
+                       _language_type = new wxChoice (this, wxID_ANY);
+                       /// TRANSLATORS: Main and Additional here are a choice for whether a set of subtitles is in the "main" language of the
+                       /// film or an "additional" language.
+                       _language_type->Append (_("Main"));
+                       _language_type->Append (_("Additional"));
+                       _language_type->Bind (wxEVT_CHOICE, boost::bind(&TextPanel::language_is_additional_changed, this));
+                       _language_sizer->Add (_language_type, 0);
+                       _grid->Add (_language_sizer, wxGBPosition(_ccap_track_or_language_row, 1), wxGBSpan(1, 2));
+                       film_content_changed (TextContentProperty::LANGUAGE);
+                       film_content_changed (TextContentProperty::LANGUAGE_IS_ADDITIONAL);
+               }
                break;
        case TextType::CLOSED_CAPTION:
+               if (_language_label) {
+                       _language_label->Destroy ();
+                       _language_label = nullptr;
+                       _grid->Remove (_language->sizer());
+                       delete _language;
+                       _grid->Remove (_language_sizer);
+                       _language_sizer = nullptr;
+                       _language = nullptr;
+                       _language_type->Destroy ();
+                       _language_type = nullptr;
+               }
                if (!_dcp_track_label) {
                        _dcp_track_label = create_label (this, _("CCAP track"), true);
-                       add_label_to_sizer (_grid, _dcp_track_label, true, wxGBPosition(_ccap_track_row, 0));
+                       add_label_to_sizer (_grid, _dcp_track_label, true, wxGBPosition(_ccap_track_or_language_row, 0));
                }
                if (!_dcp_track) {
                        _dcp_track = new wxChoice (this, wxID_ANY);
                        _dcp_track->Bind (wxEVT_CHOICE, boost::bind(&TextPanel::dcp_track_changed, this));
-                       _grid->Add (_dcp_track, wxGBPosition(_ccap_track_row, 1), wxDefaultSpan, wxEXPAND);
+                       _grid->Add (_dcp_track, wxGBPosition(_ccap_track_or_language_row, 1), wxDefaultSpan, wxEXPAND);
                        update_dcp_tracks ();
                        film_content_changed (TextContentProperty::DCP_TRACK);
                }
                if (_outline_subtitles) {
                        _outline_subtitles->Destroy ();
-                       _outline_subtitles = 0;
+                       _outline_subtitles = nullptr;
                        clear_outline_subtitles ();
                }
                break;
@@ -249,14 +277,14 @@ TextPanel::add_to_grid ()
 
        {
                add_label_to_sizer (_grid, _line_spacing_label, true, wxGBPosition (r, 0));
-               wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               auto s = new wxBoxSizer (wxHORIZONTAL);
                s->Add (_line_spacing);
                add_label_to_sizer (s, _line_spacing_pc_label, false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL);
                _grid->Add (s, wxGBPosition (r, 1));
                ++r;
        }
 
-       _ccap_track_row = r;
+       _ccap_track_or_language_row = r;
        ++r;
 
        add_label_to_sizer (_grid, _stream_label, true, wxGBPosition (r, 0));
@@ -454,6 +482,14 @@ TextPanel::film_content_changed (int property)
                if (_dcp_track) {
                        update_dcp_track_selection ();
                }
+       } else if (property == TextContentProperty::LANGUAGE) {
+               if (_language) {
+                       _language->set (text ? text->language() : boost::none);
+               }
+       } else if (property == TextContentProperty::LANGUAGE_IS_ADDITIONAL) {
+               if (_language_type) {
+                       _language_type->SetSelection (text ? (text->language_is_additional() ? 1 : 0) : 0);
+               }
        } else if (property == DCPContentProperty::REFERENCE_TEXT) {
                if (scs) {
                        auto dcp = dynamic_pointer_cast<DCPContent> (scs);
@@ -526,10 +562,10 @@ TextPanel::setup_sensitivity ()
        auto sel = _parent->selected_text ();
        for (auto i: sel) {
                /* These are the content types that could include subtitles */
-               auto fc = std::dynamic_pointer_cast<const FFmpegContent> (i);
-               auto sc = std::dynamic_pointer_cast<const StringTextFileContent> (i);
-               auto dc = std::dynamic_pointer_cast<const DCPContent> (i);
-               auto dsc = std::dynamic_pointer_cast<const DCPSubtitleContent> (i);
+               auto fc = std::dynamic_pointer_cast<const FFmpegContent>(i);
+               auto sc = std::dynamic_pointer_cast<const StringTextFileContent>(i);
+               auto dc = std::dynamic_pointer_cast<const DCPContent>(i);
+               auto dsc = std::dynamic_pointer_cast<const DCPSubtitleContent>(i);
                if (fc) {
                        if (!fc->text.empty()) {
                                ++ffmpeg_subs;
@@ -548,7 +584,7 @@ TextPanel::setup_sensitivity ()
 
        shared_ptr<DCPContent> dcp;
        if (sel.size() == 1) {
-               dcp = dynamic_pointer_cast<DCPContent> (sel.front ());
+               dcp = dynamic_pointer_cast<DCPContent>(sel.front());
        }
 
        string why_not;
@@ -563,7 +599,7 @@ TextPanel::setup_sensitivity ()
 
        bool const reference = _reference->GetValue ();
 
-       TextType const type = current_type ();
+       auto const type = current_type ();
 
        /* Set up _type */
        _type->Clear ();
@@ -618,7 +654,7 @@ TextPanel::stream_changed ()
 
        auto a = fcs->subtitle_streams ();
        auto i = a.begin ();
-       auto const s = string_client_data (_stream->GetClientObject (_stream->GetSelection ()));
+       auto const s = string_client_data (_stream->GetClientObject(_stream->GetSelection()));
        while (i != a.end() && (*i)->identifier () != s) {
                ++i;
        }
@@ -688,6 +724,8 @@ TextPanel::content_selection_changed ()
        film_content_changed (TextContentProperty::FONTS);
        film_content_changed (TextContentProperty::TYPE);
        film_content_changed (TextContentProperty::DCP_TRACK);
+       film_content_changed (TextContentProperty::LANGUAGE);
+       film_content_changed (TextContentProperty::LANGUAGE_IS_ADDITIONAL);
        film_content_changed (DCPContentProperty::REFERENCE_TEXT);
 }
 
@@ -697,7 +735,7 @@ TextPanel::text_view_clicked ()
 {
        if (_text_view) {
                _text_view->Destroy ();
-               _text_view = 0;
+               _text_view = nullptr;
        }
 
        auto c = _parent->selected_text ();
@@ -759,7 +797,6 @@ TextPanel::appearance_dialog_clicked ()
 }
 
 
-
 /** The user has clicked on the outline subtitles check box */
 void
 TextPanel::outline_subtitles_changed ()
@@ -882,3 +919,27 @@ TextPanel::analysis_finished ()
        try_to_load_analysis ();
 }
 
+
+void
+TextPanel::language_changed ()
+{
+       for (auto i: _parent->selected_text()) {
+               auto t = i->text_of_original_type(_original_type);
+               if (t) {
+                       t->set_language (_language->get());
+               }
+       }
+}
+
+
+void
+TextPanel::language_is_additional_changed ()
+{
+       for (auto i: _parent->selected_text()) {
+               auto t = i->text_of_original_type(_original_type);
+               if (t) {
+                       t->set_language_is_additional (_language_type->GetSelection() == 1);
+               }
+       }
+}
+
index 7f38ff42be56c67ff59c1eec8567938805f6ed06..7337e5258b8c55ed4fef2ceba12bdf873a104de3 100644 (file)
@@ -23,6 +23,8 @@
 
 
 class wxCheckBox;
+class wxSpinCtrl;
+class LanguageTagWidget;
 class TextView;
 class FontsDialog;
 class SpinCtrl;
@@ -60,6 +62,8 @@ private:
        void add_to_grid ();
        void try_to_load_analysis ();
        void analysis_finished ();
+       void language_changed ();
+       void language_is_additional_changed ();
 
        void setup_sensitivity ();
        void setup_visibility ();
@@ -100,9 +104,13 @@ private:
        FontsDialog* _fonts_dialog;
        wxButton* _appearance_dialog_button;
        TextType _original_type;
+       wxStaticText* _language_label = nullptr;
+       LanguageTagWidget* _language = nullptr;
+       wxSizer* _language_sizer = nullptr;
+       wxChoice* _language_type = nullptr;
 
        int _outline_subtitles_row;
-       int _ccap_track_row;
+       int _ccap_track_or_language_row;
 
        std::weak_ptr<Content> _analysis_content;
        boost::signals2::scoped_connection _analysis_finished_connection;
index dad2ba7d971783d781c09b59b3bbedf2264ea10e..59d8ec1b02644559bc176d55b7f11e840d8ec654 100644 (file)
@@ -74,10 +74,10 @@ BOOST_AUTO_TEST_CASE (isdcf_name_test)
        film->_isdcf_date = boost::gregorian::date (2014, boost::gregorian::Jul, 4);
        film->set_audio_channels (1);
        film->set_resolution (Resolution::FOUR_K);
-       film->set_subtitle_language (dcp::LanguageTag("fr-FR"));
        shared_ptr<Content> text = content_factory("test/data/subrip.srt").front();
        BOOST_REQUIRE_EQUAL (text->text.size(), 1U);
        text->text.front()->set_burn (true);
+       text->text.front()->set_language (dcp::LanguageTag("fr-FR"));
        film->examine_and_add_content (text);
        BOOST_REQUIRE (!wait_for_jobs());
        m.content_version = 2;
index f0f2ad16247c3982b15b14eb435edec43d499721..af5dee001f7c173b369c302e9d0096e8bafc327a 100644 (file)
 
 */
 
+
 /** @file  test/subtitle_language_test.cc
  *  @brief Test that subtitle language information is correctly written to DCPs.
  */
 
 
+#include "lib/content.h"
 #include "lib/content_factory.h"
 #include "lib/film.h"
+#include "lib/text_content.h"
 #include "test.h"
 #include <dcp/language_tag.h>
 #include <boost/test/unit_test.hpp>
@@ -39,12 +42,10 @@ using std::shared_ptr;
 BOOST_AUTO_TEST_CASE (subtitle_language_interop_test)
 {
        string const name = "subtitle_language_interop_test";
-       auto film = new_test_film2 (name, { content_factory("test/data/frames.srt").front() });
+       auto fr = content_factory("test/data/frames.srt").front();
+       auto film = new_test_film2 (name, { fr });
 
-       vector<dcp::LanguageTag> langs = {
-               dcp::LanguageTag("fr-FR"), dcp::LanguageTag("de-DE")
-       };
-       film->set_subtitle_languages(langs);
+       fr->only_text()->set_language (dcp::LanguageTag("fr-FR"));
        film->set_interop (true);
 
        make_and_verify_dcp (
@@ -62,12 +63,10 @@ BOOST_AUTO_TEST_CASE (subtitle_language_interop_test)
 BOOST_AUTO_TEST_CASE (subtitle_language_smpte_test)
 {
        string const name = "subtitle_language_smpte_test";
-       auto film = new_test_film2 (name, { content_factory("test/data/frames.srt").front() });
+       auto fr = content_factory("test/data/frames.srt").front();
+       auto film = new_test_film2 (name, { fr });
 
-       vector<dcp::LanguageTag> langs = {
-               dcp::LanguageTag("fr-FR"), dcp::LanguageTag("de-DE")
-       };
-       film->set_subtitle_languages (langs);
+       fr->only_text()->set_language (dcp::LanguageTag("fr-FR"));
        film->set_interop (false);
 
        make_and_verify_dcp (
index 6eaa7710060c1577e539eb174e2d8e99a6c48373..91372d2a521cbd0924229fe98e90711b93e8a64b 100644 (file)
 #include <boost/test/unit_test.hpp>
 
 
-using std::vector;
+using std::make_shared;
 using std::shared_ptr;
+using std::vector;
 
 
 BOOST_AUTO_TEST_CASE (subtitle_metadata_test1)
 {
        using namespace boost::filesystem;
 
-       path p = test_film_dir ("subtitle_metadata_test1");
+       auto p = test_film_dir ("subtitle_metadata_test1");
        if (exists (p)) {
                remove_all (p);
        }
        create_directory (p);
 
        copy_file ("test/data/subtitle_metadata1.xml", p / "metadata.xml");
-       shared_ptr<Film> film(new Film(p));
-       film->read_metadata();
-
-       vector<dcp::LanguageTag> langs = film->subtitle_languages ();
-       BOOST_REQUIRE (!langs.empty());
-       BOOST_CHECK_EQUAL (langs.front().to_string(), "de-DE");
-}
-
-
-BOOST_AUTO_TEST_CASE (subtitle_metadata_test2)
-{
-       using namespace boost::filesystem;
-
-       path p = test_film_dir ("subtitle_metadata_test2");
-       if (exists (p)) {
-               remove_all (p);
-       }
-       create_directory (p);
-
-       copy_file ("test/data/subtitle_metadata2.xml", p / "metadata.xml");
-       shared_ptr<Film> film(new Film(p));
+       auto film = make_shared<Film>(p);
        film->read_metadata();
 
-       vector<dcp::LanguageTag> langs = film->subtitle_languages ();
-       BOOST_REQUIRE (!langs.empty());
-       BOOST_CHECK_EQUAL (langs.front().to_string(), "FR");
+       auto langs = film->subtitle_languages ();
+       BOOST_REQUIRE (langs.first);
+       BOOST_CHECK_EQUAL (langs.first->to_string(), "de-DE");
 }