2 Copyright (C) 2020 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 "lib/dcpomatic_assert.h"
23 #include "language_tag_dialog.h"
24 #include <dcp/language_tag.h>
25 #include <wx/listctrl.h>
26 #include <wx/srchctrl.h>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/bind.hpp>
30 #include <boost/foreach.hpp>
31 #include <boost/optional.hpp>
32 #include <boost/shared_ptr.hpp>
33 #include <boost/signals2.hpp>
43 using boost::optional;
44 using boost::shared_ptr;
45 using boost::weak_ptr;
46 #if BOOST_VERSION >= 106100
47 using namespace boost::placeholders;
51 class SubtagListCtrl : public wxListCtrl
54 SubtagListCtrl (wxWindow* parent)
55 : wxListCtrl (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER | wxLC_VIRTUAL)
57 AppendColumn ("", wxLIST_FORMAT_LEFT, 80);
58 AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
61 void set (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::SubtagData> subtag = optional<dcp::LanguageTag::SubtagData>())
63 _all_subtags = dcp::LanguageTag::get_all(type);
66 vector<dcp::LanguageTag::SubtagData>::iterator i = find(_matching_subtags.begin(), _matching_subtags.end(), *subtag);
67 if (i != _matching_subtags.end()) {
68 long item = std::distance(_matching_subtags.begin(), i);
69 SetItemState (item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
75 void set_search (string search)
78 _matching_subtags = _all_subtags;
80 _matching_subtags.clear ();
82 boost::algorithm::to_lower(search);
83 BOOST_FOREACH (dcp::LanguageTag::SubtagData const& i, _all_subtags) {
85 (boost::algorithm::to_lower_copy(i.subtag).find(search) != string::npos) ||
86 (boost::algorithm::to_lower_copy(i.description).find(search) != string::npos)) {
87 _matching_subtags.push_back (i);
92 SetItemCount (_matching_subtags.size());
93 if (GetItemCount() > 0) {
94 RefreshItems (0, GetItemCount() - 1);
98 optional<dcp::LanguageTag::SubtagData> selected_subtag () const
100 long int selected = GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
101 if (selected == -1) {
102 return optional<dcp::LanguageTag::SubtagData>();
105 DCPOMATIC_ASSERT (static_cast<size_t>(selected) < _matching_subtags.size());
106 return _matching_subtags[selected];
110 wxString OnGetItemText (long item, long column) const
113 return _matching_subtags[item].subtag;
115 return _matching_subtags[item].description;
119 std::vector<dcp::LanguageTag::SubtagData> _all_subtags;
120 std::vector<dcp::LanguageTag::SubtagData> _matching_subtags;
124 class LanguageSubtagPanel : public wxPanel
127 LanguageSubtagPanel (wxWindow* parent)
128 : wxPanel (parent, wxID_ANY)
131 int const height = 30;
133 int const height = -1;
136 _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, height));
137 _list = new SubtagListCtrl (this);
139 wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
140 sizer->Add (_search, 0, wxALL, 8);
141 sizer->Add (_list, 1, wxALL, 8);
144 _search->Bind (wxEVT_TEXT, boost::bind(&LanguageSubtagPanel::search_changed, this));
145 _list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
146 _list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
149 void set (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::SubtagData> subtag = optional<dcp::LanguageTag::SubtagData>())
151 _list->set (type, search, subtag);
152 _search->SetValue (wxString(search));
155 optional<dcp::LanguageTag::RegionSubtag> get () const
157 if (!_list->selected_subtag()) {
158 return optional<dcp::LanguageTag::RegionSubtag>();
161 return dcp::LanguageTag::RegionSubtag(_list->selected_subtag()->subtag);
164 boost::signals2::signal<void (optional<dcp::LanguageTag::SubtagData>)> SelectionChanged;
165 boost::signals2::signal<void (string)> SearchChanged;
168 void search_changed ()
170 _list->set_search (_search->GetValue().ToStdString());
171 SearchChanged (_search->GetValue().ToStdString());
174 void selection_changed ()
176 SelectionChanged (_list->selected_subtag());
179 wxSearchCtrl* _search;
180 SubtagListCtrl* _list;
184 LanguageTagDialog::LanguageTagDialog (wxWindow* parent, dcp::LanguageTag tag)
185 : wxDialog (parent, wxID_ANY, "Language Tag", wxDefaultPosition, wxSize(-1, 500))
187 _current_tag_list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
188 _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 200);
189 _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
191 wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL);
192 _add_script = new wxButton(this, wxID_ANY, "Add script");
193 button_sizer->Add (_add_script, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
194 _add_region = new wxButton(this, wxID_ANY, "Add region");
195 button_sizer->Add (_add_region, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
196 _add_variant = new wxButton(this, wxID_ANY, "Add variant");
197 button_sizer->Add (_add_variant, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
198 _add_external = new wxButton(this, wxID_ANY, "Add external");
199 button_sizer->Add (_add_external, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
201 _choose_subtag_panel = new LanguageSubtagPanel (this);
202 _choose_subtag_panel->set (dcp::LanguageTag::LANGUAGE, "");
204 wxBoxSizer* ltor_sizer = new wxBoxSizer (wxHORIZONTAL);
205 ltor_sizer->Add (_current_tag_list, 1, wxALL, 8);
206 ltor_sizer->Add (button_sizer, 0, wxALL, 8);
207 ltor_sizer->Add (_choose_subtag_panel, 1, wxALL, 8);
209 wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
210 overall_sizer->Add (ltor_sizer, 0);
212 wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
214 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
217 SetSizerAndFit (overall_sizer);
219 bool have_language = false;
220 vector<pair<dcp::LanguageTag::SubtagType, dcp::LanguageTag::SubtagData> > subtags = tag.subtags();
221 for (vector<pair<dcp::LanguageTag::SubtagType, dcp::LanguageTag::SubtagData> >::const_iterator i = subtags.begin(); i != subtags.end(); ++i) {
222 add_to_current_tag (i->first, i->second);
223 if (i->first == dcp::LanguageTag::LANGUAGE) {
224 have_language = true;
228 if (!have_language) {
229 add_to_current_tag (dcp::LanguageTag::LANGUAGE, dcp::LanguageTag::SubtagData("en", "English"));
232 _add_script->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SCRIPT, boost::optional<dcp::LanguageTag::SubtagData>()));
233 _add_region->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::REGION, boost::optional<dcp::LanguageTag::SubtagData>()));
234 _add_variant->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::VARIANT, boost::optional<dcp::LanguageTag::SubtagData>()));
235 _add_external->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::EXTLANG, boost::optional<dcp::LanguageTag::SubtagData>()));
236 _choose_subtag_panel->SelectionChanged.connect(bind(&LanguageTagDialog::chosen_subtag_changed, this, _1));
237 _choose_subtag_panel->SearchChanged.connect(bind(&LanguageTagDialog::search_changed, this, _1));
238 _current_tag_list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&LanguageTagDialog::current_tag_selection_changed, this));
239 _current_tag_list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&LanguageTagDialog::current_tag_selection_changed, this));
243 dcp::LanguageTag LanguageTagDialog::get () const
245 dcp::LanguageTag tag;
247 vector<dcp::LanguageTag::VariantSubtag> variants;
248 vector<dcp::LanguageTag::ExtlangSubtag> extlangs;
250 BOOST_FOREACH (Subtag i, _current_tag_subtags) {
255 case dcp::LanguageTag::LANGUAGE:
256 tag.set_language (i.subtag->subtag);
258 case dcp::LanguageTag::SCRIPT:
259 tag.set_script (i.subtag->subtag);
261 case dcp::LanguageTag::REGION:
262 tag.set_region (i.subtag->subtag);
264 case dcp::LanguageTag::VARIANT:
265 variants.push_back (i.subtag->subtag);
267 case dcp::LanguageTag::EXTLANG:
268 extlangs.push_back (i.subtag->subtag);
273 tag.set_variants (variants);
274 tag.set_extlangs (extlangs);
279 string LanguageTagDialog::subtag_type_name (dcp::LanguageTag::SubtagType type)
282 case dcp::LanguageTag::LANGUAGE:
284 case dcp::LanguageTag::SCRIPT:
286 case dcp::LanguageTag::REGION:
288 case dcp::LanguageTag::VARIANT:
290 case dcp::LanguageTag::EXTLANG:
299 LanguageTagDialog::search_changed (string search)
301 long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
303 _current_tag_subtags[selected].last_search = search;
309 LanguageTagDialog::add_to_current_tag (dcp::LanguageTag::SubtagType type, optional<dcp::LanguageTag::SubtagData> subtag)
311 _current_tag_subtags.push_back (Subtag(type, subtag));
313 it.SetId (_current_tag_list->GetItemCount());
315 it.SetText (subtag_type_name(type));
316 _current_tag_list->InsertItem (it);
319 it.SetText (subtag->description);
321 it.SetText ("Select...");
323 _current_tag_list->SetItem (it);
324 _current_tag_list->SetItemState (_current_tag_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
325 _choose_subtag_panel->set (type, "");
326 setup_sensitivity ();
327 current_tag_selection_changed ();
332 LanguageTagDialog::current_tag_selection_changed ()
334 long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
336 _choose_subtag_panel->Enable (true);
337 _choose_subtag_panel->set (_current_tag_subtags[selected].type, _current_tag_subtags[selected].last_search, _current_tag_subtags[selected].subtag);
339 _choose_subtag_panel->Enable (false);
345 LanguageTagDialog::chosen_subtag_changed (optional<dcp::LanguageTag::SubtagData> selection)
351 long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
353 _current_tag_subtags[selected].subtag = *selection;
354 _current_tag_list->SetItem (selected, 0, subtag_type_name(_current_tag_subtags[selected].type));
355 _current_tag_list->SetItem (selected, 1, selection->description);
360 LanguageTagDialog::setup_sensitivity ()
362 _add_script->Enable ();
363 _add_region->Enable ();
364 _add_variant->Enable ();
365 _add_external->Enable ();
366 BOOST_FOREACH (Subtag const& i, _current_tag_subtags) {
368 case dcp::LanguageTag::SCRIPT:
369 _add_script->Enable (false);
371 case dcp::LanguageTag::REGION:
372 _add_region->Enable (false);
374 case dcp::LanguageTag::VARIANT:
375 _add_variant->Enable (false);
377 case dcp::LanguageTag::EXTLANG:
378 _add_external->Enable (false);
387 RegionSubtagDialog::RegionSubtagDialog (wxWindow* parent, dcp::LanguageTag::RegionSubtag region)
388 : wxDialog (parent, wxID_ANY, _("Region"), wxDefaultPosition, wxSize(-1, 500))
389 , _panel (new LanguageSubtagPanel (this))
391 wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
392 sizer->Add (_panel, 1);
394 wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
396 sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
401 _panel->set (dcp::LanguageTag::REGION, "", *dcp::LanguageTag::get_subtag_data(region));
405 optional<dcp::LanguageTag::RegionSubtag>
406 RegionSubtagDialog::get () const
408 return _panel->get ();