Supporters update.
[dcpomatic.git] / src / wx / full_language_tag_dialog.cc
1 /*
2     Copyright (C) 2020-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21
22 #include "full_language_tag_dialog.h"
23 #include "language_subtag_panel.h"
24 #include "subtag_list_ctrl.h"
25 #include "lib/dcpomatic_assert.h"
26 #include <dcp/language_tag.h>
27 #include <dcp/warnings.h>
28 LIBDCP_DISABLE_WARNINGS
29 #include <wx/listctrl.h>
30 #include <wx/srchctrl.h>
31 #include <wx/wx.h>
32 LIBDCP_ENABLE_WARNINGS
33 #include <boost/algorithm/string.hpp>
34 #include <boost/bind/bind.hpp>
35 #include <boost/optional.hpp>
36 #include <boost/signals2.hpp>
37 #include <iterator>
38 #include <string>
39 #include <vector>
40
41
42 using std::min;
43 using std::pair;
44 using std::shared_ptr;
45 using std::string;
46 using std::vector;
47 using std::weak_ptr;
48 using boost::optional;
49 #if BOOST_VERSION >= 106100
50 using namespace boost::placeholders;
51 #endif
52
53
54 FullLanguageTagDialog::FullLanguageTagDialog (wxWindow* parent, dcp::LanguageTag tag)
55         : wxDialog (parent, wxID_ANY, _("Language Tag"), wxDefaultPosition, wxSize(-1, 500))
56 {
57         _current_tag_list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
58         _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 200);
59         _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
60
61         auto button_sizer = new wxBoxSizer (wxVERTICAL);
62         _add_script = new wxButton(this, wxID_ANY, "Add script");
63         button_sizer->Add (_add_script, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
64         _add_region = new wxButton(this, wxID_ANY, "Add region");
65         button_sizer->Add (_add_region, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
66         _add_variant = new wxButton(this, wxID_ANY, "Add variant");
67         button_sizer->Add (_add_variant, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
68         _add_external = new wxButton(this, wxID_ANY, "Add external");
69         button_sizer->Add (_add_external, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
70         _remove = new wxButton(this, wxID_ANY, "Remove");
71         button_sizer->Add (_remove, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
72
73         _choose_subtag_panel = new LanguageSubtagPanel (this);
74         _choose_subtag_panel->set (dcp::LanguageTag::SubtagType::LANGUAGE, "");
75
76         auto ltor_sizer = new wxBoxSizer (wxHORIZONTAL);
77         ltor_sizer->Add (_current_tag_list, 1, wxALL, 8);
78         ltor_sizer->Add (button_sizer, 0, wxALL, 8);
79         ltor_sizer->Add (_choose_subtag_panel, 1, wxALL, 8);
80
81         auto overall_sizer = new wxBoxSizer (wxVERTICAL);
82         overall_sizer->Add (ltor_sizer, 0);
83
84         auto buttons = CreateSeparatedButtonSizer (wxOK);
85         if (buttons) {
86                 overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
87         }
88
89         SetSizerAndFit (overall_sizer);
90
91         set (tag);
92
93         _add_script->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::SCRIPT, boost::optional<dcp::LanguageTag::SubtagData>()));
94         _add_region->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::REGION, boost::optional<dcp::LanguageTag::SubtagData>()));
95         _add_variant->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::VARIANT, boost::optional<dcp::LanguageTag::SubtagData>()));
96         _add_external->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::EXTLANG, boost::optional<dcp::LanguageTag::SubtagData>()));
97         _remove->Bind (wxEVT_BUTTON, boost::bind(&FullLanguageTagDialog::remove_from_current_tag, this));
98         _choose_subtag_panel->SelectionChanged.connect(bind(&FullLanguageTagDialog::chosen_subtag_changed, this, _1));
99         _choose_subtag_panel->SearchChanged.connect(bind(&FullLanguageTagDialog::search_changed, this, _1));
100         _current_tag_list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&FullLanguageTagDialog::current_tag_selection_changed, this));
101         _current_tag_list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&FullLanguageTagDialog::current_tag_selection_changed, this));
102 }
103
104
105 void
106 FullLanguageTagDialog::remove_from_current_tag ()
107 {
108         auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
109         if (selected <= 0) {
110                 return;
111         }
112
113         _current_tag_subtags.erase (_current_tag_subtags.begin() + selected);
114         _current_tag_list->DeleteItem (selected);
115
116         _current_tag_list->SetItemState (min(selected, _current_tag_list->GetItemCount() - 1L), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
117
118         setup_sensitivity ();
119         current_tag_selection_changed ();
120 }
121
122
123 dcp::LanguageTag FullLanguageTagDialog::get () const
124 {
125         dcp::LanguageTag tag;
126
127         vector<dcp::LanguageTag::VariantSubtag> variants;
128         vector<dcp::LanguageTag::ExtlangSubtag> extlangs;
129
130         for (auto i: _current_tag_subtags) {
131                 if (!i.subtag) {
132                         continue;
133                 }
134                 switch (i.type) {
135                         case dcp::LanguageTag::SubtagType::LANGUAGE:
136                                 tag.set_language (i.subtag->subtag);
137                                 break;
138                         case dcp::LanguageTag::SubtagType::SCRIPT:
139                                 tag.set_script (i.subtag->subtag);
140                                 break;
141                         case dcp::LanguageTag::SubtagType::REGION:
142                                 tag.set_region (i.subtag->subtag);
143                                 break;
144                         case dcp::LanguageTag::SubtagType::VARIANT:
145                                 variants.push_back (i.subtag->subtag);
146                                 break;
147                         case dcp::LanguageTag::SubtagType::EXTLANG:
148                                 extlangs.push_back (i.subtag->subtag);
149                                 break;
150                 }
151         }
152
153         tag.set_variants (variants);
154         tag.set_extlangs (extlangs);
155         return tag;
156 }
157
158
159 void
160 FullLanguageTagDialog::set (dcp::LanguageTag tag)
161 {
162         _current_tag_subtags.clear ();
163         _current_tag_list->DeleteAllItems ();
164
165         bool have_language = false;
166         for (auto const& i: tag.subtags()) {
167                 add_to_current_tag (i.first, i.second);
168                 if (i.first == dcp::LanguageTag::SubtagType::LANGUAGE) {
169                         have_language = true;
170                 }
171         }
172
173         if (!have_language) {
174                 add_to_current_tag (dcp::LanguageTag::SubtagType::LANGUAGE, dcp::LanguageTag::SubtagData("en", "English"));
175         }
176 }
177
178
179 string FullLanguageTagDialog::subtag_type_name (dcp::LanguageTag::SubtagType type)
180 {
181         switch (type) {
182                 case dcp::LanguageTag::SubtagType::LANGUAGE:
183                         return "Language";
184                 case dcp::LanguageTag::SubtagType::SCRIPT:
185                         return "Script";
186                 case dcp::LanguageTag::SubtagType::REGION:
187                         return "Region";
188                 case dcp::LanguageTag::SubtagType::VARIANT:
189                         return "Variant";
190                 case dcp::LanguageTag::SubtagType::EXTLANG:
191                         return "External";
192         }
193
194         return {};
195 }
196
197
198 void
199 FullLanguageTagDialog::search_changed (string search)
200 {
201         long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
202         if (selected >= 0) {
203                 _current_tag_subtags[selected].last_search = search;
204         }
205 }
206
207
208 void
209 FullLanguageTagDialog::add_to_current_tag (dcp::LanguageTag::SubtagType type, optional<dcp::LanguageTag::SubtagData> subtag)
210 {
211         _current_tag_subtags.push_back (Subtag(type, subtag));
212         wxListItem it;
213         it.SetId (_current_tag_list->GetItemCount());
214         it.SetColumn (0);
215         it.SetText (subtag_type_name(type));
216         _current_tag_list->InsertItem (it);
217         it.SetColumn (1);
218         if (subtag) {
219                 it.SetText (subtag->description);
220         } else {
221                 it.SetText ("Select...");
222         }
223         _current_tag_list->SetItem (it);
224         _current_tag_list->SetItemState (_current_tag_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
225         _choose_subtag_panel->set (type, "");
226         setup_sensitivity ();
227         current_tag_selection_changed ();
228 }
229
230
231 void
232 FullLanguageTagDialog::current_tag_selection_changed ()
233 {
234         auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
235         if (selected >= 0) {
236                 _choose_subtag_panel->Enable (true);
237                 _choose_subtag_panel->set (_current_tag_subtags[selected].type, _current_tag_subtags[selected].last_search, _current_tag_subtags[selected].subtag);
238         } else {
239                 _choose_subtag_panel->Enable (false);
240         }
241 }
242
243
244 void
245 FullLanguageTagDialog::chosen_subtag_changed (optional<dcp::LanguageTag::SubtagData> selection)
246 {
247         if (!selection) {
248                 return;
249         }
250
251         auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
252         if (selected >= 0) {
253                 _current_tag_subtags[selected].subtag = *selection;
254                 _current_tag_list->SetItem (selected, 0, subtag_type_name(_current_tag_subtags[selected].type));
255                 _current_tag_list->SetItem (selected, 1, selection->description);
256         }
257
258         setup_sensitivity ();
259 }
260
261 void
262 FullLanguageTagDialog::setup_sensitivity ()
263 {
264         _add_script->Enable ();
265         _add_region->Enable ();
266         _add_variant->Enable ();
267         _add_external->Enable ();
268         for (auto const& i: _current_tag_subtags) {
269                 switch (i.type) {
270                         case dcp::LanguageTag::SubtagType::SCRIPT:
271                                 _add_script->Enable (false);
272                                 break;
273                         case dcp::LanguageTag::SubtagType::REGION:
274                                 _add_region->Enable (false);
275                                 break;
276                         case dcp::LanguageTag::SubtagType::VARIANT:
277                                 _add_variant->Enable (false);
278                                 break;
279                         case dcp::LanguageTag::SubtagType::EXTLANG:
280                                 _add_external->Enable (false);
281                                 break;
282                         default:
283                                 break;
284                 }
285         }
286         auto selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
287         _remove->Enable (selected > 0);
288 }
289