DROPME: add test wxWidgets UI for LanguageTag editing.
authorCarl Hetherington <cth@carlh.net>
Sun, 23 Aug 2020 20:08:00 +0000 (22:08 +0200)
committerCarl Hetherington <cth@carlh.net>
Sun, 20 Sep 2020 17:30:28 +0000 (19:30 +0200)
langtest.cc [new file with mode: 0644]
langtest.sh [new file with mode: 0644]

diff --git a/langtest.cc b/langtest.cc
new file mode 100644 (file)
index 0000000..bad4a0d
--- /dev/null
@@ -0,0 +1,350 @@
+#include <dcp/language_tag.h>
+#include <wx/listctrl.h>
+#include <wx/srchctrl.h>
+#include <wx/wx.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/foreach.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2.hpp>
+#include <iostream>
+#include <iterator>
+
+
+using std::string;
+using std::vector;
+using boost::enable_shared_from_this;
+using boost::optional;
+using boost::shared_ptr;
+using boost::weak_ptr;
+
+
+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_type_and_search (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::Subtag> subtag = optional<dcp::LanguageTag::Subtag>())
+       {
+               _all_subtags = dcp::LanguageTag::get_all(type);
+               set_search (search);
+               if (subtag) {
+                       vector<dcp::LanguageTag::Subtag>::iterator i = find(_matching_subtags.begin(), _matching_subtags.end(), *subtag);
+                       if (i != _matching_subtags.end()) {
+                               SetItemState (std::distance(_matching_subtags.begin(), i), wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+                       }
+               }
+       }
+
+       void set_search (string search)
+       {
+               if (search == "") {
+                       _matching_subtags = _all_subtags;
+               } else {
+                       _matching_subtags.clear ();
+
+                       boost::algorithm::to_lower(search);
+                       BOOST_FOREACH (dcp::LanguageTag::Subtag 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::Subtag> selected_subtag () const
+       {
+               long int selected = GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+               if (selected == -1) {
+                       return optional<dcp::LanguageTag::Subtag>();
+               }
+
+               //DCPOMATIC_ASSERT (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::Subtag> _all_subtags;
+       std::vector<dcp::LanguageTag::Subtag> _matching_subtags;
+};
+
+
+class LanguageSubtagPanel : public wxPanel
+{
+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);
+               SetSizerAndFit (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));
+       }
+
+       void set_type_and_search (dcp::LanguageTag::SubtagType type, string search, optional<dcp::LanguageTag::Subtag> subtag = optional<dcp::LanguageTag::Subtag>())
+       {
+               _list->set_type_and_search (type, search, subtag);
+               _search->SetValue (wxString(search));
+       }
+
+       boost::signals2::signal<void (optional<dcp::LanguageTag::Subtag>)> SelectionChanged;
+       boost::signals2::signal<void (string)> SearchChanged;
+
+private:
+       void search_changed ()
+       {
+               _list->set_search (_search->GetValue().ToStdString());
+               SearchChanged (_search->GetValue().ToStdString());
+       }
+
+       void selection_changed ()
+       {
+               SelectionChanged (_list->selected_subtag());
+       }
+
+       wxSearchCtrl* _search;
+       SubtagListCtrl* _list;
+};
+
+
+
+
+class LanguageTagDialog : public wxDialog
+{
+public:
+       class Subtag
+       {
+       public:
+               Subtag (dcp::LanguageTag::SubtagType type_, dcp::LanguageTag::Subtag subtag_)
+                       : type (type_)
+                       , subtag (subtag_)
+               {}
+
+               dcp::LanguageTag::SubtagType type;
+               dcp::LanguageTag::Subtag subtag;
+               std::string last_search;
+       };
+
+       LanguageTagDialog (wxWindow* parent)
+               : wxDialog (parent, wxID_ANY, "Language Tag")
+       {
+               _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, 1, wxTOP | wxBOTTOM | wxEXPAND, 2);
+               _add_region = new wxButton(this, wxID_ANY, "Add region");
+               button_sizer->Add (_add_region, 1, wxTOP | wxBOTTOM | wxEXPAND, 2);
+               _add_variant = new wxButton(this, wxID_ANY, "Add variant");
+               button_sizer->Add (_add_variant, 1, wxTOP | wxBOTTOM | wxEXPAND, 2);
+               _add_external = new wxButton(this, wxID_ANY, "Add external");
+               button_sizer->Add (_add_external, 1, wxTOP | wxBOTTOM | wxEXPAND, 2);
+
+               _choose_subtag_panel = new LanguageSubtagPanel (this);
+               _choose_subtag_panel->set_type_and_search (dcp::LanguageTag::LANGUAGE, "de");
+
+               wxBoxSizer* overall_sizer = new wxBoxSizer (wxHORIZONTAL);
+               overall_sizer->Add (_current_tag_list, 1, wxALL, 8);
+               overall_sizer->Add (button_sizer, 0, wxALL, 8);
+               overall_sizer->Add (_choose_subtag_panel, 1, wxALL, 8);
+
+               SetSizerAndFit (overall_sizer);
+
+               add_to_current_tag (dcp::LanguageTag::LANGUAGE, dcp::LanguageTag::Subtag("en", "English"));
+
+               _add_script->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::SCRIPT, dcp::LanguageTag::Subtag()));
+               _add_region->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::REGION, dcp::LanguageTag::Subtag()));
+               _add_variant->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::VARIANT, dcp::LanguageTag::Subtag()));
+               _add_external->Bind (wxEVT_BUTTON, boost::bind(&LanguageTagDialog::add_to_current_tag, this, dcp::LanguageTag::EXTLANG, dcp::LanguageTag::Subtag()));
+               _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));
+       }
+
+       dcp::LanguageTag get () const
+       {
+               dcp::LanguageTag tag;
+
+               vector<string> variants;
+               vector<string> extlangs;
+
+               BOOST_FOREACH (Subtag i, _current_tag_subtags) {
+                       switch (i.type) {
+                       case dcp::LanguageTag::LANGUAGE:
+                               tag.set_language (i.subtag.subtag);
+                               break;
+                       case dcp::LanguageTag::SCRIPT:
+                               tag.set_script (i.subtag.subtag);
+                               break;
+                       case dcp::LanguageTag::REGION:
+                               tag.set_region (i.subtag.subtag);
+                               break;
+                       case dcp::LanguageTag::VARIANT:
+                               variants.push_back (i.subtag.subtag);
+                               break;
+                       case dcp::LanguageTag::EXTLANG:
+                               extlangs.push_back (i.subtag.subtag);
+                               break;
+                       }
+               }
+
+               tag.set_variants (variants);
+               tag.set_extlangs (extlangs);
+               return tag;
+       }
+
+
+private:
+
+       string subtag_type_name (dcp::LanguageTag::SubtagType type)
+       {
+               switch (type) {
+               case dcp::LanguageTag::LANGUAGE:
+                       return "Language";
+               case dcp::LanguageTag::SCRIPT:
+                       return "Script";
+               case dcp::LanguageTag::REGION:
+                       return "Region";
+               case dcp::LanguageTag::VARIANT:
+                       return "Variant";
+               case dcp::LanguageTag::EXTLANG:
+                       return "External";
+               }
+
+               return "";
+       }
+
+       void search_changed (string search)
+       {
+               long int selected = _current_tag_list->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+               if (selected >= 0) {
+                       _current_tag_subtags[selected].last_search = search;
+               }
+       }
+
+       void add_to_current_tag (dcp::LanguageTag::SubtagType type, dcp::LanguageTag::Subtag 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);
+               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_and_search (type, "");
+               setup_sensitivity ();
+       }
+
+       void current_tag_selection_changed ()
+       {
+               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_type_and_search (_current_tag_subtags[selected].type, _current_tag_subtags[selected].last_search, _current_tag_subtags[selected].subtag);
+               } else {
+                       _choose_subtag_panel->Enable (false);
+               }
+       }
+
+       void chosen_subtag_changed (optional<dcp::LanguageTag::Subtag> selection)
+       {
+               if (!selection) {
+                       return;
+               }
+
+               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);
+               }
+       }
+
+       void setup_sensitivity ()
+       {
+               _add_script->Enable ();
+               _add_region->Enable ();
+               _add_variant->Enable ();
+               _add_external->Enable ();
+               BOOST_FOREACH (Subtag const& i, _current_tag_subtags) {
+                       switch (i.type) {
+                       case dcp::LanguageTag::SCRIPT:
+                               _add_script->Enable (false);
+                               break;
+                       case dcp::LanguageTag::REGION:
+                               _add_region->Enable (false);
+                               break;
+                       case dcp::LanguageTag::VARIANT:
+                               _add_variant->Enable (false);
+                               break;
+                       case dcp::LanguageTag::EXTLANG:
+                               _add_external->Enable (false);
+                               break;
+                       }
+               }
+       }
+
+       vector<Subtag> _current_tag_subtags;
+       wxListCtrl* _current_tag_list;
+       LanguageSubtagPanel* _choose_subtag_panel;
+       wxButton* _add_script;
+       wxButton* _add_region;
+       wxButton* _add_variant;
+       wxButton* _add_external;
+};
+
+
+class MyApp : public wxApp
+{
+public:
+       virtual bool OnInit()
+       {
+               LanguageTagDialog* dialog = new LanguageTagDialog (0);
+               dialog->ShowModal ();
+               return true;
+       }
+};
+
+wxIMPLEMENT_APP(MyApp);
+
diff --git a/langtest.sh b/langtest.sh
new file mode 100644 (file)
index 0000000..5611683
--- /dev/null
@@ -0,0 +1 @@
+g++ -g -o langtest langtest.cc `wx-config --cflags --libs` -I. -Lbuild/src -ldcp-1.0