summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-10-06 06:08:46 +1100
committer Vas Crabb <vas@vastheman.com>2021-10-06 06:08:46 +1100
commit190eafe573bdb989ecebe880d4801d909d856dad (patch)
treee9c13f8284c93fae7d6562fc1e9b5493404451b2
parent8c19f7e20be940c34f3a0183c9389ae226cbd1f8 (diff)
ui: Locale-aware sorting for software list items.
-rw-r--r--src/frontend/mame/ui/selsoft.cpp90
-rw-r--r--src/frontend/mame/ui/swlist.cpp63
-rw-r--r--src/frontend/mame/ui/swlist.h1
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 <algorithm>
#include <iterator>
#include <functional>
+#include <locale>
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<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(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 <locale>
+
namespace ui {
@@ -164,26 +166,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<wchar_t> const &coll = std::use_facet<std::collate<wchar_t>>(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);
};