diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/content.h | 2 | ||||
| -rw-r--r-- | src/lib/film.cc | 94 | ||||
| -rw-r--r-- | src/lib/film.h | 9 | ||||
| -rw-r--r-- | src/lib/reel_writer.cc | 10 | ||||
| -rw-r--r-- | src/lib/subtitle_encoder.cc | 10 | ||||
| -rw-r--r-- | src/lib/text_content.cc | 49 | ||||
| -rw-r--r-- | src/lib/text_content.h | 25 | ||||
| -rw-r--r-- | src/lib/writer.cc | 12 | ||||
| -rw-r--r-- | src/wx/dcp_panel.cc | 2 | ||||
| -rw-r--r-- | src/wx/dcp_text_track_dialog.cc | 3 | ||||
| -rw-r--r-- | src/wx/interop_metadata_dialog.cc | 78 | ||||
| -rw-r--r-- | src/wx/interop_metadata_dialog.h | 8 | ||||
| -rw-r--r-- | src/wx/language_tag_dialog.cc | 1 | ||||
| -rw-r--r-- | src/wx/language_tag_widget.cc | 34 | ||||
| -rw-r--r-- | src/wx/language_tag_widget.h | 9 | ||||
| -rw-r--r-- | src/wx/smpte_metadata_dialog.cc | 101 | ||||
| -rw-r--r-- | src/wx/smpte_metadata_dialog.h | 10 | ||||
| -rw-r--r-- | src/wx/text_panel.cc | 95 | ||||
| -rw-r--r-- | src/wx/text_panel.h | 10 |
19 files changed, 263 insertions, 299 deletions
diff --git a/src/lib/content.h b/src/lib/content.h index b8626e212..567cd5c1f 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -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; diff --git a/src/lib/film.cc b/src/lib/film.cc index 46074e2ad..763af8f8a 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -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 { @@ -2038,29 +2021,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) { FilmChangeSignaller ch (this, Property::FACILITY); diff --git a/src/lib/film.h b/src/lib/film.h index 9b45fd073..9feb5d0d3 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -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; diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 366e6edc6..2c55f0f06 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -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)); } diff --git a/src/lib/subtitle_encoder.cc b/src/lib/subtitle_encoder.cc index 3721ee02b..b61876ad6 100644 --- a/src/lib/subtitle_encoder.cc +++ b/src/lib/subtitle_encoder.cc @@ -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)); } diff --git a/src/lib/text_content.cc b/src/lib/text_content.cc index 0c25e5696..c86150881 100644 --- a/src/lib/text_content.cc +++ b/src/lib/text_content.cc @@ -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; } @@ -560,6 +587,18 @@ TextContent::unset_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) { set_use (c->_use); @@ -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); } diff --git a/src/lib/text_content.h b/src/lib/text_content.h index e566d0552..4c6918a42 100644 --- a/src/lib/text_content.h +++ b/src/lib/text_content.h @@ -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. @@ -18,15 +18,19 @@ */ + #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 diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 839156d34..3448c5bca 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -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; diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index 6de9e3d75..3304f4d3c 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -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 || diff --git a/src/wx/dcp_text_track_dialog.cc b/src/wx/dcp_text_track_dialog.cc index 3e8750cd5..b884aba51 100644 --- a/src/wx/dcp_text_track_dialog.cc +++ b/src/wx/dcp_text_track_dialog.cc @@ -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()); } diff --git a/src/wx/interop_metadata_dialog.cc b/src/wx/interop_metadata_dialog.cc index 7976e6d44..aa61984b3 100644 --- a/src/wx/interop_metadata_dialog.cc +++ b/src/wx/interop_metadata_dialog.cc @@ -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. @@ -18,14 +18,16 @@ */ -#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())); diff --git a/src/wx/interop_metadata_dialog.h b/src/wx/interop_metadata_dialog.h index 6e4eea40e..b8a1a36e6 100644 --- a/src/wx/interop_metadata_dialog.h +++ b/src/wx/interop_metadata_dialog.h @@ -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. @@ -18,12 +18,14 @@ */ + #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; }; diff --git a/src/wx/language_tag_dialog.cc b/src/wx/language_tag_dialog.cc index 96e7c5283..c72c64b31 100644 --- a/src/wx/language_tag_dialog.cc +++ b/src/wx/language_tag_dialog.cc @@ -125,6 +125,7 @@ LanguageTagDialog::set (dcp::LanguageTag tag) } _list->SetItemState (selection, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); + _list->EnsureVisible (selection); } diff --git a/src/wx/language_tag_widget.cc b/src/wx/language_tag_widget.cc index d71b5fc7a..f0766c9d6 100644 --- a/src/wx/language_tag_widget.cc +++ b/src/wx/language_tag_widget.cc @@ -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. @@ -26,13 +26,24 @@ #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("")); + } } diff --git a/src/wx/language_tag_widget.h b/src/wx/language_tag_widget.h index 336f7dce2..c2fd63d92 100644 --- a/src/wx/language_tag_widget.h +++ b/src/wx/language_tag_widget.h @@ -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; }; diff --git a/src/wx/smpte_metadata_dialog.cc b/src/wx/smpte_metadata_dialog.cc index 36cf48489..470e9317e 100644 --- a/src/wx/smpte_metadata_dialog.cc +++ b/src/wx/smpte_metadata_dialog.cc @@ -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")); - } } } @@ -496,20 +454,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); } diff --git a/src/wx/smpte_metadata_dialog.h b/src/wx/smpte_metadata_dialog.h index 63d90249c..83c1ab39c 100644 --- a/src/wx/smpte_metadata_dialog.h +++ b/src/wx/smpte_metadata_dialog.h @@ -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 diff --git a/src/wx/text_panel.cc b/src/wx/text_panel.cc index 3b617a031..5e00d4f41 100644 --- a/src/wx/text_panel.cc +++ b/src/wx/text_panel.cc @@ -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); + } + } +} + diff --git a/src/wx/text_panel.h b/src/wx/text_panel.h index 7f38ff42b..7337e5258 100644 --- a/src/wx/text_panel.h +++ b/src/wx/text_panel.h @@ -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; |
