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.
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;
}
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;
_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);
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;
}
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
}
}
- 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 {
}
-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)
{
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;
return _luminance;
}
- std::vector<dcp::LanguageTag> subtitle_languages () const {
- return _subtitle_languages;
- }
-
/* SET */
void set_directory (boost::filesystem::path);
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;
boost::optional<std::string> _distributor;
boost::optional<std::string> _facility;
boost::optional<dcp::Luminance> _luminance;
- std::vector<dcp::LanguageTag> _subtitle_languages;
int _state_version;
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 */
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);
}
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));
}
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));
}
*/
+
#include "text_content.h"
#include "util.h"
#include "exceptions.h"
#include "i18n.h"
+
using std::string;
using std::vector;
using std::cout;
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;
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)
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")) {
, _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");
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)
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 ();
_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 ();
}
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
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;
}
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)
{
} else {
unset_dcp_track ();
}
+ set_language (c->_language);
+ set_language_is_additional (c->_language_is_additional);
}
/*
- 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;
}
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.
*
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);
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;
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
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();
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;
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 ||
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());
}
/*
- 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;
#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);
}
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
);
_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());
}
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 ();
}
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);
}
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()));
/*
- 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;
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;
};
}
_list->SetItemState (selection, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ _list->EnsureVisible (selection);
}
/*
- 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);
}
+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());
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(""));
+ }
}
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;
wxStaticText* _language;
wxButton* _edit;
wxWindow* _parent;
- dcp::LanguageTag _tag;
+ boost::optional<dcp::LanguageTag> _tag;
wxSizer* _sizer;
};
);
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__
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> (
_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));
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"));
- }
}
}
}
-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 ()
{
_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);
}
/*
- 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.
*/
+
#include "editable_list.h"
#include "full_language_tag_dialog.h"
#include "lib/film.h"
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 ();
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
*/
-
#include "check_box.h"
#include "content_panel.h"
#include "dcp_text_track_dialog.h"
#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"
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 ();
_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;
{
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));
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);
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;
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;
bool const reference = _reference->GetValue ();
- TextType const type = current_type ();
+ auto const type = current_type ();
/* Set up _type */
_type->Clear ();
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;
}
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);
}
{
if (_text_view) {
_text_view->Destroy ();
- _text_view = 0;
+ _text_view = nullptr;
}
auto c = _parent->selected_text ();
}
-
/** The user has clicked on the outline subtitles check box */
void
TextPanel::outline_subtitles_changed ()
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);
+ }
+ }
+}
+
class wxCheckBox;
+class wxSpinCtrl;
+class LanguageTagWidget;
class TextView;
class FontsDialog;
class SpinCtrl;
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 ();
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;
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;
*/
+
/** @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>
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 (
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 (
#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");
}