C++11 tidying.
[dcpomatic.git] / src / wx / language_tag_dialog.cc
index 6968b1911b6868e3bfa3bcdb66b8c18d05b452c9..ca08996c681d01a5d2c47b24ecbfd9000948316b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 */
 
 
-#include "lib/dcpomatic_assert.h"
+#include "full_language_tag_dialog.h"
 #include "language_tag_dialog.h"
-#include <dcp/language_tag.h>
+#include "wx_util.h"
+#include "lib/config.h"
 #include <wx/listctrl.h>
-#include <wx/srchctrl.h>
 #include <wx/wx.h>
-#include <boost/algorithm/string.hpp>
-#include <boost/bind/bind.hpp>
-#include <boost/optional.hpp>
-#include <boost/signals2.hpp>
-#include <iostream>
-#include <iterator>
-#include <string>
-#include <vector>
-
-
-using std::min;
-using std::pair;
-using std::string;
-using std::vector;
-using boost::optional;
-using std::shared_ptr;
-using std::weak_ptr;
-#if BOOST_VERSION >= 106100
-using namespace boost::placeholders;
-#endif
-
-
-class SubtagListCtrl : public wxListCtrl
-{
-public:
-       SubtagListCtrl (wxWindow* parent)
-               : wxListCtrl (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER | wxLC_VIRTUAL)
-       {
-               AppendColumn ("", wxLIST_FORMAT_LEFT, 80);
-               AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
-       }
-
-       void set (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::SubtagData> subtag = optional<dcp::LanguageTag::SubtagData>())
-       {
-               _all_subtags = dcp::LanguageTag::get_all(type);
-               set_search (search);
-               if (subtag) {
-                       vector<dcp::LanguageTag::SubtagData>::iterator i = find(_matching_subtags.begin(), _matching_subtags.end(), *subtag);
-                       if (i != _matching_subtags.end()) {
-                               long item = std::distance(_matching_subtags.begin(), i);
-                               SetItemState (item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
-                               EnsureVisible (item);
-                       }
-               } else {
-                       if (GetItemCount() > 0) {
-                               /* The new list sometimes isn't visible without this */
-                               EnsureVisible (0);
-                       }
-               }
-       }
-
-       void set_search (string search)
-       {
-               if (search == "") {
-                       _matching_subtags = _all_subtags;
-               } else {
-                       _matching_subtags.clear ();
-
-                       boost::algorithm::to_lower(search);
-                       for (auto const& i: _all_subtags) {
-                               if (
-                                       (boost::algorithm::to_lower_copy(i.subtag).find(search) != string::npos) ||
-                                       (boost::algorithm::to_lower_copy(i.description).find(search) != string::npos)) {
-                                       _matching_subtags.push_back (i);
-                               }
-                       }
-               }
-
-               SetItemCount (_matching_subtags.size());
-               if (GetItemCount() > 0) {
-                       RefreshItems (0, GetItemCount() - 1);
-               }
-       }
-
-       optional<dcp::LanguageTag::SubtagData> selected_subtag () const
-       {
-               long int selected = GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-               if (selected == -1) {
-                       return optional<dcp::LanguageTag::SubtagData>();
-               }
+#include <dcp/language_tag.h>
 
-               DCPOMATIC_ASSERT (static_cast<size_t>(selected) < _matching_subtags.size());
-               return _matching_subtags[selected];
-       }
 
-private:
-       wxString OnGetItemText (long item, long column) const
-       {
-               if (column == 0) {
-                       return _matching_subtags[item].subtag;
-               } else {
-                       return _matching_subtags[item].description;
-               }
-       }
-
-       std::vector<dcp::LanguageTag::SubtagData> _all_subtags;
-       std::vector<dcp::LanguageTag::SubtagData> _matching_subtags;
-};
+using std::vector;
 
 
-class LanguageSubtagPanel : public wxPanel
+LanguageTagDialog::LanguageTagDialog (wxWindow* parent, dcp::LanguageTag tag)
+       : wxDialog (parent, wxID_ANY, _("Language Tag"))
 {
-public:
-       LanguageSubtagPanel (wxWindow* parent)
-               : wxPanel (parent, wxID_ANY)
-       {
-#ifdef __WXGTK3__
-               int const height = 30;
-#else
-               int const height = -1;
-#endif
-
-               _search = new wxSearchCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200, height));
-               _list = new SubtagListCtrl (this);
-
-               wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
-               sizer->Add (_search, 0, wxALL, 8);
-               sizer->Add (_list, 1, wxALL, 8);
-               SetSizer (sizer);
-
-               _search->Bind (wxEVT_TEXT, boost::bind(&LanguageSubtagPanel::search_changed, this));
-               _list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
-               _list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&LanguageSubtagPanel::selection_changed, this));
-       }
+       _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize(600, 700), wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
+       _list->AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
+       _list->AppendColumn ("", wxLIST_FORMAT_LEFT, 150);
+       auto add = new wxButton (this, wxID_ANY, _("Add language..."));
 
-       void set (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::SubtagData> subtag = optional<dcp::LanguageTag::SubtagData>())
-       {
-               _list->set (type, search, subtag);
-               _search->SetValue (wxString(search));
-       }
-
-       optional<dcp::LanguageTag::RegionSubtag> get () const
-       {
-               if (!_list->selected_subtag()) {
-                       return {};
-               }
-
-               return dcp::LanguageTag::RegionSubtag(_list->selected_subtag()->subtag);
-       }
-
-       boost::signals2::signal<void (optional<dcp::LanguageTag::SubtagData>)> SelectionChanged;
-       boost::signals2::signal<void (string)> SearchChanged;
-
-private:
-       void search_changed ()
-       {
-               auto search = _search->GetValue();
-               _list->set_search (search.ToStdString());
-               if (search.Length() > 0) {
-                       _list->EnsureVisible (0);
-               }
-               SearchChanged (_search->GetValue().ToStdString());
-       }
+       auto overall_sizer = new wxBoxSizer (wxVERTICAL);
+       overall_sizer->Add (_list, 0, wxALL, DCPOMATIC_SIZER_GAP);
+       overall_sizer->Add (add, 0, wxALL, DCPOMATIC_SIZER_GAP);
 
-       void selection_changed ()
-       {
-               SelectionChanged (_list->selected_subtag());
-       }
-
-       wxSearchCtrl* _search;
-       SubtagListCtrl* _list;
-};
-
-
-LanguageTagDialog::LanguageTagDialog (wxWindow* parent, dcp::LanguageTag tag)
-       : wxDialog (parent, wxID_ANY, "Language Tag", wxDefaultPosition, wxSize(-1, 500))
-{
-       _current_tag_list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_NO_HEADER);
-       _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 200);
-       _current_tag_list->AppendColumn ("", wxLIST_FORMAT_LEFT, 400);
-
-       wxBoxSizer* button_sizer = new wxBoxSizer (wxVERTICAL);
-       _add_script = new wxButton(this, wxID_ANY, "Add script");
-       button_sizer->Add (_add_script, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
-       _add_region = new wxButton(this, wxID_ANY, "Add region");
-       button_sizer->Add (_add_region, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
-       _add_variant = new wxButton(this, wxID_ANY, "Add variant");
-       button_sizer->Add (_add_variant, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
-       _add_external = new wxButton(this, wxID_ANY, "Add external");
-       button_sizer->Add (_add_external, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
-       _remove = new wxButton(this, wxID_ANY, "Remove");
-       button_sizer->Add (_remove, 0, wxTOP | wxBOTTOM | wxEXPAND, 2);
-
-       _choose_subtag_panel = new LanguageSubtagPanel (this);
-       _choose_subtag_panel->set (dcp::LanguageTag::SubtagType::LANGUAGE, "");
-
-       wxBoxSizer* ltor_sizer = new wxBoxSizer (wxHORIZONTAL);
-       ltor_sizer->Add (_current_tag_list, 1, wxALL, 8);
-       ltor_sizer->Add (button_sizer, 0, wxALL, 8);
-       ltor_sizer->Add (_choose_subtag_panel, 1, wxALL, 8);
-
-       wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
-       overall_sizer->Add (ltor_sizer, 0);
-
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
+       auto buttons = CreateSeparatedButtonSizer (wxOK);
        if (buttons) {
                overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
        }
 
        SetSizerAndFit (overall_sizer);
 
-       set (tag);
-
-       _add_script->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::SCRIPT, boost::optional<dcp::LanguageTag::SubtagData>()));
-       _add_region->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::REGION, boost::optional<dcp::LanguageTag::SubtagData>()));
-       _add_variant->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::VARIANT, boost::optional<dcp::LanguageTag::SubtagData>()));
-       _add_external->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SubtagType::EXTLANG, boost::optional<dcp::LanguageTag::SubtagData>()));
-       _remove->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::remove_from_current_tag, this));
-       _choose_subtag_panel->SelectionChanged.connect(bind(&LanguageTagDialog::chosen_subtag_changed, this, _1));
-       _choose_subtag_panel->SearchChanged.connect(bind(&LanguageTagDialog::search_changed, this, _1));
-       _current_tag_list->Bind (wxEVT_LIST_ITEM_SELECTED, boost::bind(&LanguageTagDialog::current_tag_selection_changed, this));
-       _current_tag_list->Bind (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&LanguageTagDialog::current_tag_selection_changed, this));
-}
-
-
-void
-LanguageTagDialog::remove_from_current_tag ()
-{
-       long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       if (selected <= 0) {
-               return;
+       for (auto const& i: dcp::dcnc_tags()) {
+               _presets.push_back (dcp::LanguageTag(i.first));
        }
 
-       _current_tag_subtags.erase (_current_tag_subtags.begin() + selected);
-       _current_tag_list->DeleteItem (selected);
-
-       _current_tag_list->SetItemState (min(selected, _current_tag_list->GetItemCount() - 1L), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+       std::sort (_presets.begin(), _presets.end(), [](dcp::LanguageTag const& i, dcp::LanguageTag const& j) {
+               return i.description() < j.description();
+       });
 
-       setup_sensitivity ();
-       current_tag_selection_changed ();
-}
+       _custom = Config::instance()->custom_languages ();
 
+       populate_list ();
 
-dcp::LanguageTag LanguageTagDialog::get () const
-{
-       dcp::LanguageTag tag;
-
-       vector<dcp::LanguageTag::VariantSubtag> variants;
-       vector<dcp::LanguageTag::ExtlangSubtag> extlangs;
-
-       for (auto i: _current_tag_subtags) {
-               if (!i.subtag) {
-                       continue;
-               }
-               switch (i.type) {
-                       case dcp::LanguageTag::SubtagType::LANGUAGE:
-                               tag.set_language (i.subtag->subtag);
-                               break;
-                       case dcp::LanguageTag::SubtagType::SCRIPT:
-                               tag.set_script (i.subtag->subtag);
-                               break;
-                       case dcp::LanguageTag::SubtagType::REGION:
-                               tag.set_region (i.subtag->subtag);
-                               break;
-                       case dcp::LanguageTag::SubtagType::VARIANT:
-                               variants.push_back (i.subtag->subtag);
-                               break;
-                       case dcp::LanguageTag::SubtagType::EXTLANG:
-                               extlangs.push_back (i.subtag->subtag);
-                               break;
-               }
-       }
+       set (tag);
 
-       tag.set_variants (variants);
-       tag.set_extlangs (extlangs);
-       return tag;
+       add->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_language, this));
 }
 
 
 void
-LanguageTagDialog::set (dcp::LanguageTag tag)
-{
-       _current_tag_subtags.clear ();
-       _current_tag_list->DeleteAllItems ();
-
-       bool have_language = false;
-       for (auto const& i: tag.subtags()) {
-               add_to_current_tag (i.first, i.second);
-               if (i.first == dcp::LanguageTag::SubtagType::LANGUAGE) {
-                       have_language = true;
-               }
-       }
-
-       if (!have_language) {
-               add_to_current_tag (dcp::LanguageTag::SubtagType::LANGUAGE, dcp::LanguageTag::SubtagData("en", "English"));
-       }
-}
-
-
-string LanguageTagDialog::subtag_type_name (dcp::LanguageTag::SubtagType type)
+LanguageTagDialog::add_language ()
 {
-       switch (type) {
-               case dcp::LanguageTag::SubtagType::LANGUAGE:
-                       return "Language";
-               case dcp::LanguageTag::SubtagType::SCRIPT:
-                       return "Script";
-               case dcp::LanguageTag::SubtagType::REGION:
-                       return "Region";
-               case dcp::LanguageTag::SubtagType::VARIANT:
-                       return "Variant";
-               case dcp::LanguageTag::SubtagType::EXTLANG:
-                       return "External";
+       auto full = new FullLanguageTagDialog (GetParent());
+       auto r = full->ShowModal ();
+       if (r == wxID_OK) {
+               Config::instance()->add_custom_language (full->get());
+               set (full->get());
        }
-
-       return "";
+       full->Destroy ();
 }
 
 
 void
-LanguageTagDialog::search_changed (string search)
+LanguageTagDialog::populate_list ()
 {
-       long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       if (selected >= 0) {
-               _current_tag_subtags[selected].last_search = search;
-       }
-}
-
+       _list->DeleteAllItems ();
+
+       auto add = [this](vector<dcp::LanguageTag> const& tags) {
+               for (auto const& i: tags) {
+                       wxListItem it;
+                       it.SetId (_list->GetItemCount());
+                       it.SetColumn (0);
+                       it.SetText (std_to_wx(i.description()));
+                       _list->InsertItem (it);
+                       it.SetColumn (1);
+                       it.SetText (std_to_wx(i.to_string()));
+                       _list->SetItem (it);
+               }
+       };
 
-void
-LanguageTagDialog::add_to_current_tag (dcp::LanguageTag::SubtagType type, optional<dcp::LanguageTag::SubtagData> subtag)
-{
-       _current_tag_subtags.push_back (Subtag(type, subtag));
-       wxListItem it;
-       it.SetId (_current_tag_list->GetItemCount());
-       it.SetColumn (0);
-       it.SetText (subtag_type_name(type));
-       _current_tag_list->InsertItem (it);
-       it.SetColumn (1);
-       if (subtag) {
-               it.SetText (subtag->description);
-       } else {
-               it.SetText ("Select...");
-       }
-       _current_tag_list->SetItem (it);
-       _current_tag_list->SetItemState (_current_tag_list->GetItemCount() - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
-       _choose_subtag_panel->set (type, "");
-       setup_sensitivity ();
-       current_tag_selection_changed ();
+       add (_presets);
+       add (_custom);
 }
 
 
 void
-LanguageTagDialog::current_tag_selection_changed ()
+LanguageTagDialog::set (dcp::LanguageTag tag)
 {
-       long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       if (selected >= 0) {
-               _choose_subtag_panel->Enable (true);
-               _choose_subtag_panel->set (_current_tag_subtags[selected].type, _current_tag_subtags[selected].last_search, _current_tag_subtags[selected].subtag);
+       size_t selection = 0;
+
+       auto iter = find(_presets.begin(), _presets.end(), tag);
+       if (iter == _presets.end()) {
+               iter = find(_custom.begin(), _custom.end(), tag);
+               if (iter == _custom.end()) {
+                       _custom.push_back (tag);
+                       selection = _presets.size() + _custom.size() - 1;
+                       populate_list ();
+                       if (_list->GetItemCount() > 0) {
+                               _list->EnsureVisible (_list->GetItemCount() - 1);
+                       }
+               } else {
+                       selection = _presets.size() + std::distance(_custom.begin(), iter);
+               }
        } else {
-               _choose_subtag_panel->Enable (false);
+               selection = std::distance(_presets.begin(), iter);
        }
-}
 
-
-void
-LanguageTagDialog::chosen_subtag_changed (optional<dcp::LanguageTag::SubtagData> selection)
-{
-       if (!selection) {
-               return;
+       _list->SetItemState (selection, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+       if (static_cast<int>(selection) < _list->GetItemCount()) {
+               _list->EnsureVisible (selection);
        }
-
-       long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       if (selected >= 0) {
-               _current_tag_subtags[selected].subtag = *selection;
-               _current_tag_list->SetItem (selected, 0, subtag_type_name(_current_tag_subtags[selected].type));
-               _current_tag_list->SetItem (selected, 1, selection->description);
-       }
-
-       setup_sensitivity ();
-}
-
-void
-LanguageTagDialog::setup_sensitivity ()
-{
-       _add_script->Enable ();
-       _add_region->Enable ();
-       _add_variant->Enable ();
-       _add_external->Enable ();
-       for (auto const& i: _current_tag_subtags) {
-               switch (i.type) {
-                       case dcp::LanguageTag::SubtagType::SCRIPT:
-                               _add_script->Enable (false);
-                               break;
-                       case dcp::LanguageTag::SubtagType::REGION:
-                               _add_region->Enable (false);
-                               break;
-                       case dcp::LanguageTag::SubtagType::VARIANT:
-                               _add_variant->Enable (false);
-                               break;
-                       case dcp::LanguageTag::SubtagType::EXTLANG:
-                               _add_external->Enable (false);
-                               break;
-                       default:
-                               break;
-               }
-       }
-       long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
-       _remove->Enable (selected > 0);
 }
 
 
-RegionSubtagDialog::RegionSubtagDialog (wxWindow* parent, dcp::LanguageTag::RegionSubtag region)
-       : wxDialog (parent, wxID_ANY, _("Region"), wxDefaultPosition, wxSize(-1, 500))
-       , _panel (new LanguageSubtagPanel (this))
+dcp::LanguageTag
+LanguageTagDialog::get () const
 {
-       wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
-       sizer->Add (_panel, 1);
+       auto selected = _list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+       DCPOMATIC_ASSERT (selected >= 0);
 
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
-       if (buttons) {
-               sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+       if (selected < static_cast<long>(_presets.size())) {
+               return _presets[selected];
        }
 
-       SetSizer (sizer);
-
-       _panel->set (dcp::LanguageTag::SubtagType::REGION, "", *dcp::LanguageTag::get_subtag_data(region));
-}
-
+       selected -= _presets.size();
 
-optional<dcp::LanguageTag::RegionSubtag>
-RegionSubtagDialog::get () const
-{
-       return _panel->get ();
+       DCPOMATIC_ASSERT (selected < static_cast<long>(_custom.size()));
+       return _custom[selected];
 }
 
-