2 Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 #include "full_language_tag_dialog.h"
23 #include "subtag_list_ctrl.h"
24 #include "lib/dcpomatic_assert.h"
25 #include <dcp/language_tag.h>
26 #include <dcp/warnings.h>
27 LIBDCP_DISABLE_WARNINGS
28 #include <wx/listctrl.h>
29 #include <wx/srchctrl.h>
31 LIBDCP_ENABLE_WARNINGS
32 #include <boost/algorithm/string.hpp>
33 #include <boost/bind/bind.hpp>
34 #include <boost/optional.hpp>
35 #include <boost/signals2.hpp>
43 using std::shared_ptr;
47 using boost::optional;
48 #if BOOST_VERSION >= 106100
49 using namespace boost::placeholders;
53 class LanguageSubtagPanel : public wxPanel
56 LanguageSubtagPanel (wxWindow* parent)
57 : wxPanel (parent, wxID_ANY)
60 int const height = 30;
62 int const height = -1;
65 _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, height));
66 _list = new SubtagListCtrl (this);
68 auto sizer = new wxBoxSizer (wxVERTICAL);
69 sizer->Add (_search, 0, wxALL, 8);
70 sizer->Add (_list, 1, wxALL, 8);
73 _search->Bind (wxEVT_TEXT, boost::bind(&LanguageSubtagPanel::search_changed, this));
74 _list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
75 _list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
78 void set (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::SubtagData> subtag = optional<dcp::LanguageTag::SubtagData>())
80 _list->set (type, search, subtag);
81 _search->SetValue (wxString(search));
84 optional<dcp::LanguageTag::RegionSubtag> get () const
86 if (!_list->selected_subtag()) {
90 return dcp::LanguageTag::RegionSubtag(_list->selected_subtag()->subtag);
93 boost::signals2::signal<void (optional<dcp::LanguageTag::SubtagData>)> SelectionChanged;
94 boost::signals2::signal<void (string)> SearchChanged;
97 void search_changed ()
99 auto search = _search->GetValue();
100 _list->set_search (search.ToStdString());
101 if (search.Length() > 0 && _list->GetItemCount() > 0) {
102 _list->EnsureVisible (0);
104 SearchChanged (_search->GetValue().ToStdString());
107 void selection_changed ()
109 SelectionChanged (_list->selected_subtag());
112 wxSearchCtrl* _search;
113 SubtagListCtrl* _list;
117 FullLanguageTagDialog::FullLanguageTagDialog (wxWindow* parent, dcp::LanguageTag tag)
118 : wxDialog (parent, wxID_ANY, _("Language Tag"), wxDefaultPosition, wxSize(-1, 500))
120 _current_tag_list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
121 _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 200);
122 _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
124 auto button_sizer = new wxBoxSizer (wxVERTICAL);
125 _add_script = new wxButton(this, wxID_ANY, "Add script");
126 button_sizer->Add (_add_script, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
127 _add_region = new wxButton(this, wxID_ANY, "Add region");
128 button_sizer->Add (_add_region, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
129 _add_variant = new wxButton(this, wxID_ANY, "Add variant");
130 button_sizer->Add (_add_variant, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
131 _add_external = new wxButton(this, wxID_ANY, "Add external");
132 button_sizer->Add (_add_external, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
133 _remove = new wxButton(this, wxID_ANY, "Remove");
134 button_sizer->Add (_remove, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
136 _choose_subtag_panel = new LanguageSubtagPanel (this);
137 _choose_subtag_panel->set (dcp::LanguageTag::SubtagType::LANGUAGE, "");
139 auto ltor_sizer = new wxBoxSizer (wxHORIZONTAL);
140 ltor_sizer->Add (_current_tag_list, 1, wxALL, 8);
141 ltor_sizer->Add (button_sizer, 0, wxALL, 8);
142 ltor_sizer->Add (_choose_subtag_panel, 1, wxALL, 8);
144 auto overall_sizer = new wxBoxSizer (wxVERTICAL);
145 overall_sizer->Add (ltor_sizer, 0);
147 auto buttons = CreateSeparatedButtonSizer (wxOK);
149 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
152 SetSizerAndFit (overall_sizer);
156 _add_script->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::SCRIPT, boost::optional<dcp::LanguageTag::SubtagData>()));
157 _add_region->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::REGION, boost::optional<dcp::LanguageTag::SubtagData>()));
158 _add_variant->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::VARIANT, boost::optional<dcp::LanguageTag::SubtagData>()));
159 _add_external->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::EXTLANG, boost::optional<dcp::LanguageTag::SubtagData>()));
160 _remove->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::remove_from_current_tag, this));
161 _choose_subtag_panel->SelectionChanged.connect(bind(&FullLanguageTagDialog::chosen_subtag_changed, this, _1));
162 _choose_subtag_panel->SearchChanged.connect(bind(&FullLanguageTagDialog::search_changed, this, _1));
163 _current_tag_list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&FullLanguageTagDialog::current_tag_selection_changed, this));
164 _current_tag_list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&FullLanguageTagDialog::current_tag_selection_changed, this));
169 FullLanguageTagDialog::remove_from_current_tag ()
171 auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
176 _current_tag_subtags.erase (_current_tag_subtags.begin() + selected);
177 _current_tag_list->DeleteItem (selected);
179 _current_tag_list->SetItemState (min(selected, _current_tag_list->GetItemCount() - 1L), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
181 setup_sensitivity ();
182 current_tag_selection_changed ();
186 dcp::LanguageTag FullLanguageTagDialog::get () const
188 dcp::LanguageTag tag;
190 vector<dcp::LanguageTag::VariantSubtag> variants;
191 vector<dcp::LanguageTag::ExtlangSubtag> extlangs;
193 for (auto i: _current_tag_subtags) {
198 case dcp::LanguageTag::SubtagType::LANGUAGE:
199 tag.set_language (i.subtag->subtag);
201 case dcp::LanguageTag::SubtagType::SCRIPT:
202 tag.set_script (i.subtag->subtag);
204 case dcp::LanguageTag::SubtagType::REGION:
205 tag.set_region (i.subtag->subtag);
207 case dcp::LanguageTag::SubtagType::VARIANT:
208 variants.push_back (i.subtag->subtag);
210 case dcp::LanguageTag::SubtagType::EXTLANG:
211 extlangs.push_back (i.subtag->subtag);
216 tag.set_variants (variants);
217 tag.set_extlangs (extlangs);
223 FullLanguageTagDialog::set (dcp::LanguageTag tag)
225 _current_tag_subtags.clear ();
226 _current_tag_list->DeleteAllItems ();
228 bool have_language = false;
229 for (auto const& i: tag.subtags()) {
230 add_to_current_tag (i.first, i.second);
231 if (i.first == dcp::LanguageTag::SubtagType::LANGUAGE) {
232 have_language = true;
236 if (!have_language) {
237 add_to_current_tag (dcp::LanguageTag::SubtagType::LANGUAGE, dcp::LanguageTag::SubtagData("en", "English"));
242 string FullLanguageTagDialog::subtag_type_name (dcp::LanguageTag::SubtagType type)
245 case dcp::LanguageTag::SubtagType::LANGUAGE:
247 case dcp::LanguageTag::SubtagType::SCRIPT:
249 case dcp::LanguageTag::SubtagType::REGION:
251 case dcp::LanguageTag::SubtagType::VARIANT:
253 case dcp::LanguageTag::SubtagType::EXTLANG:
262 FullLanguageTagDialog::search_changed (string search)
264 long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
266 _current_tag_subtags[selected].last_search = search;
272 FullLanguageTagDialog::add_to_current_tag (dcp::LanguageTag::SubtagType type, optional<dcp::LanguageTag::SubtagData> subtag)
274 _current_tag_subtags.push_back (Subtag(type, subtag));
276 it.SetId (_current_tag_list->GetItemCount());
278 it.SetText (subtag_type_name(type));
279 _current_tag_list->InsertItem (it);
282 it.SetText (subtag->description);
284 it.SetText ("Select...");
286 _current_tag_list->SetItem (it);
287 _current_tag_list->SetItemState (_current_tag_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
288 _choose_subtag_panel->set (type, "");
289 setup_sensitivity ();
290 current_tag_selection_changed ();
295 FullLanguageTagDialog::current_tag_selection_changed ()
297 auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
299 _choose_subtag_panel->Enable (true);
300 _choose_subtag_panel->set (_current_tag_subtags[selected].type, _current_tag_subtags[selected].last_search, _current_tag_subtags[selected].subtag);
302 _choose_subtag_panel->Enable (false);
308 FullLanguageTagDialog::chosen_subtag_changed (optional<dcp::LanguageTag::SubtagData> selection)
314 auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
316 _current_tag_subtags[selected].subtag = *selection;
317 _current_tag_list->SetItem (selected, 0, subtag_type_name(_current_tag_subtags[selected].type));
318 _current_tag_list->SetItem (selected, 1, selection->description);
321 setup_sensitivity ();
325 FullLanguageTagDialog::setup_sensitivity ()
327 _add_script->Enable ();
328 _add_region->Enable ();
329 _add_variant->Enable ();
330 _add_external->Enable ();
331 for (auto const& i: _current_tag_subtags) {
333 case dcp::LanguageTag::SubtagType::SCRIPT:
334 _add_script->Enable (false);
336 case dcp::LanguageTag::SubtagType::REGION:
337 _add_region->Enable (false);
339 case dcp::LanguageTag::SubtagType::VARIANT:
340 _add_variant->Enable (false);
342 case dcp::LanguageTag::SubtagType::EXTLANG:
343 _add_external->Enable (false);
349 auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
350 _remove->Enable (selected > 0);
354 RegionSubtagDialog::RegionSubtagDialog (wxWindow* parent, dcp::LanguageTag::RegionSubtag region)
355 : wxDialog (parent, wxID_ANY, _("Region"), wxDefaultPosition, wxSize(-1, 500))
356 , _panel (new LanguageSubtagPanel (this))
358 auto sizer = new wxBoxSizer (wxVERTICAL);
359 sizer->Add (_panel, 1);
361 auto buttons = CreateSeparatedButtonSizer (wxOK);
363 sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
368 _panel->set (dcp::LanguageTag::SubtagType::REGION, "", *dcp::LanguageTag::get_subtag_data(region));
372 optional<dcp::LanguageTag::RegionSubtag>
373 RegionSubtagDialog::get () const
375 return _panel->get ();