From 190eafe573bdb989ecebe880d4801d909d856dad Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Wed, 6 Oct 2021 06:08:46 +1100 Subject: ui: Locale-aware sorting for software list items. --- src/frontend/mame/ui/selsoft.cpp | 90 ++++++++++++++++++++-------------------- src/frontend/mame/ui/swlist.cpp | 63 +++++++++++++--------------- src/frontend/mame/ui/swlist.h | 1 - 3 files changed, 74 insertions(+), 80 deletions(-) diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index 95c02a9d853..e00a1618016 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -29,53 +29,11 @@ #include #include #include +#include namespace ui { -namespace { - -//------------------------------------------------- -// compares two items in the software list and -// sort them by parent-clone -//------------------------------------------------- - -bool compare_software(ui_software_info const &a, ui_software_info const &b) -{ - bool const clonex = !a.parentname.empty() && !a.parentlongname.empty(); - bool const cloney = !b.parentname.empty() && !b.parentlongname.empty(); - - if (!clonex && !cloney) - { - return 0 > core_stricmp(a.longname.c_str(), b.longname.c_str()); - } - else if (!clonex && cloney) - { - if ((a.shortname == b.parentname) && (a.instance == b.instance)) - return true; - else - return 0 > core_stricmp(a.longname.c_str(), b.parentlongname.c_str()); - } - else if (clonex && !cloney) - { - if ((a.parentname == b.shortname) && (a.instance == b.instance)) - return false; - else - return 0 > core_stricmp(a.parentlongname.c_str(), b.longname.c_str()); - } - else if ((a.parentname == b.parentname) && (a.instance == b.instance)) - { - return 0 > core_stricmp(a.longname.c_str(), b.longname.c_str()); - } - else - { - return 0 > core_stricmp(a.parentlongname.c_str(), b.parentlongname.c_str()); - } -} - -} // anonymous namespace - - menu_select_software::search_item::search_item(ui_software_info const &s) : software(s) , ucs_shortname(ustr_from_utf8(normalize_unicode(s.shortname, unicode_normalization_form::D, true))) @@ -455,7 +413,51 @@ void menu_select_software::build_software_list() } // sort array - std::stable_sort(m_swinfo.begin() + 1, m_swinfo.end(), compare_software); + std::collate const &coll = std::use_facet>(std::locale()); + auto const compare_names = + [&coll] (std::string const &x, std::string const &y) -> bool + { + std::wstring const wx = wstring_from_utf8(x); + std::wstring const wy = wstring_from_utf8(y); + return 0 > coll.compare(wx.data(), wx.data() + wx.size(), wy.data(), wy.data() + wy.size()); + }; + std::stable_sort( + m_swinfo.begin() + 1, + m_swinfo.end(), + [&compare_names] (ui_software_info const &a, ui_software_info const &b) -> bool + { + bool const clonex = !a.parentname.empty() && !a.parentlongname.empty(); + bool const cloney = !b.parentname.empty() && !b.parentlongname.empty(); + + if (!clonex && !cloney) + { + return compare_names(a.longname, b.longname); + } + else if (!clonex && cloney) + { + if ((a.shortname == b.parentname) && (a.instance == b.instance)) + return true; + else + return compare_names(a.longname, b.longname); + } + else if (clonex && !cloney) + { + if ((a.parentname == b.shortname) && (a.instance == b.instance)) + return false; + else + return compare_names(a.longname, b.longname); + } + else if ((a.parentname == b.parentname) && (a.instance == b.instance)) + { + return compare_names(a.longname, b.longname); + } + else + { + return compare_names(a.parentlongname, b.parentlongname); + } + }); + + m_filter_data.finalise(); } diff --git a/src/frontend/mame/ui/swlist.cpp b/src/frontend/mame/ui/swlist.cpp index 0ead9a66814..3053ecf73bc 100644 --- a/src/frontend/mame/ui/swlist.cpp +++ b/src/frontend/mame/ui/swlist.cpp @@ -17,6 +17,8 @@ #include "corestr.h" #include "softlist_dev.h" +#include + namespace ui { @@ -163,26 +165,6 @@ menu_software_list::~menu_software_list() } -//------------------------------------------------- -// compare_entries -//------------------------------------------------- - -int menu_software_list::compare_entries(const entry_info &e1, const entry_info &e2, bool shortname) -{ - int result; - const char *e1_basename = shortname ? e1.short_name.c_str() : e1.long_name.c_str(); - const char *e2_basename = shortname ? e2.short_name.c_str() : e2.long_name.c_str(); - - result = core_stricmp(e1_basename, e2_basename); - if (result == 0) - { - result = strcmp(e1_basename, e2_basename); - } - - return result; -} - - //------------------------------------------------- // append_software_entry - populate a specific list //------------------------------------------------- @@ -206,14 +188,7 @@ void menu_software_list::append_software_entry(const software_info &swinfo) // skip this if no new entry has been allocated (e.g. if the software has no matching interface for this image device) if (entry_updated) - { - // find the end of the list - auto iter = m_entrylist.begin(); - while (iter != m_entrylist.end() && compare_entries(entry, *iter, m_ordered_by_shortname) >= 0) - ++iter; - - m_entrylist.emplace(iter, std::move(entry)); - } + m_entrylist.emplace_back(std::move(entry)); } @@ -223,12 +198,31 @@ void menu_software_list::append_software_entry(const software_info &swinfo) void menu_software_list::populate(float &customtop, float &custombottom) { - // clear all entries before populating - m_entrylist.clear(); - // build up the list of entries for the menu - for (const software_info &swinfo : m_swlist->get_info()) - append_software_entry(swinfo); + if (m_entrylist.empty()) + for (const software_info &swinfo : m_swlist->get_info()) + append_software_entry(swinfo); + + if (m_ordered_by_shortname) + { + // short names are restricted to lowercase ASCII anyway, a dumb compare works + m_entrylist.sort([] (entry_info const &e1, entry_info const &e2) { return e1.short_name < e2.short_name; }); + } + else + { + std::collate const &coll = std::use_facet>(std::locale()); + m_entrylist.sort( + [&coll] (entry_info const &e1, entry_info const &e2) -> bool + { + std::wstring const xstr = wstring_from_utf8(e1.long_name); + std::wstring const ystr = wstring_from_utf8(e2.long_name); + auto const cmp = coll.compare(xstr.data(), xstr.data() + xstr.size(), ystr.data(), ystr.data() + ystr.size()); + if (cmp) + return cmp < 0; + else + return e1.short_name < e2.short_name; + }); + } // add an entry to change ordering item_append(_("Switch Item Ordering"), 0, ITEMREF_SWITCH_ITEM_ORDERING); @@ -283,8 +277,7 @@ void menu_software_list::handle() : nullptr; // if it's a perfect match for the current selection, don't move it - auto const &cur_name = m_ordered_by_shortname ? cur_selected->short_name : cur_selected->long_name; - if (!cur_selected || core_strnicmp(cur_name.c_str(), m_search.c_str(), m_search.size())) + if (!cur_selected || core_strnicmp((m_ordered_by_shortname ? cur_selected->short_name : cur_selected->long_name).c_str(), m_search.c_str(), m_search.size())) { std::string::size_type bestmatch(0); entry_info const *selected_entry(cur_selected); diff --git a/src/frontend/mame/ui/swlist.h b/src/frontend/mame/ui/swlist.h index 3c4bd5b91b7..3a4ade44c4e 100644 --- a/src/frontend/mame/ui/swlist.h +++ b/src/frontend/mame/ui/swlist.h @@ -88,7 +88,6 @@ private: bool m_ordered_by_shortname; // functions - int compare_entries(const entry_info &e1, const entry_info &e2, bool shortname); void append_software_entry(const software_info &swinfo); }; -- cgit v1.2.3