summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2019-01-19 22:06:28 +1100
committer Vas Crabb <vas@vastheman.com>2019-01-19 22:58:04 +1100
commit84e83a8bbc7e3cd0878a256ba3e906135e87061d (patch)
tree42c17c457cbaf4a3a7da3f942c6fefadcfb2f398
parent04aae3c533a0d7afc8e73e95e7017cfd1cac671d (diff)
Make search apply to the filtered list in system/software selection menus, and cache stuff used for searching in software selection menu
-rw-r--r--src/frontend/mame/ui/selgame.cpp105
-rw-r--r--src/frontend/mame/ui/selsoft.cpp129
-rw-r--r--src/frontend/mame/ui/selsoft.h23
3 files changed, 143 insertions, 114 deletions
diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp
index 43deb8ae81b..9ae7cb64666 100644
--- a/src/frontend/mame/ui/selgame.cpp
+++ b/src/frontend/mame/ui/selgame.cpp
@@ -506,43 +506,57 @@ void menu_select_game::populate(float &customtop, float &custombottom)
if (!isfavorite())
{
m_populated_favorites = false;
+ m_displaylist.clear();
+ machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter());
+
+ // if search is not empty, find approximate matches
if (!m_search.empty())
{
- // if search is not empty, find approximate matches
populate_search();
+ if (flt)
+ {
+ for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it)
+ {
+ if (flt->apply(it->second))
+ m_displaylist.emplace_back(it->second);
+ }
+ }
+ else
+ {
+ std::transform(
+ m_searchlist.begin(),
+ std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)),
+ std::back_inserter(m_displaylist),
+ [] (auto const &entry) { return entry.second; });
+ }
}
else
{
- // reset search string
- m_search.clear();
- m_displaylist.clear();
-
// if filter is set on category, build category list
- machine_filter const *const flt(m_persistent_data.filter_data().get_current_filter());
std::vector<ui_system_info> const &sorted(m_persistent_data.sorted_list());
if (!flt)
std::copy(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist));
else
flt->apply(sorted.begin(), sorted.end(), std::back_inserter(m_displaylist));
+ }
- // iterate over entries
- int curitem = 0;
- for (ui_system_info const &elem : m_displaylist)
- {
- if (old_item_selected == -1 && elem.driver->name == reselect_last::driver())
- old_item_selected = curitem;
-
- bool cloneof = strcmp(elem.driver->parent, "0");
- if (cloneof)
- {
- int cx = driver_list::find(elem.driver->parent);
- if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0))
- cloneof = false;
- }
+ // iterate over entries
+ int curitem = 0;
+ for (ui_system_info const &elem : m_displaylist)
+ {
+ if (old_item_selected == -1 && elem.driver->name == reselect_last::driver())
+ old_item_selected = curitem;
- item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver);
- curitem++;
+ bool cloneof = strcmp(elem.driver->parent, "0");
+ if (cloneof)
+ {
+ int cx = driver_list::find(elem.driver->parent);
+ if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0))
+ cloneof = false;
}
+
+ item_append(elem.driver->type.fullname(), "", (cloneof) ? (flags_ui | FLAG_INVERT) : flags_ui, (void *)elem.driver);
+ curitem++;
}
}
else
@@ -996,24 +1010,11 @@ void menu_select_game::populate_search()
}
}
- // sort according to edit distance and put up to 200 in the menu
+ // sort according to edit distance
std::stable_sort(
m_searchlist.begin(),
m_searchlist.end(),
[] (auto const &lhs, auto const &rhs) { return lhs.first < rhs.first; });
- uint32_t flags_ui = FLAG_LEFT_ARROW | FLAG_RIGHT_ARROW;
- for (int curitem = 0; (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH) > curitem; ++curitem)
- {
- game_driver const &drv(*m_searchlist[curitem].second.get().driver);
- bool cloneof = strcmp(drv.parent, "0") != 0;
- if (cloneof)
- {
- int const cx = driver_list::find(drv.parent);
- if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0))
- cloneof = false;
- }
- item_append(drv.type.fullname(), "", !cloneof ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&drv);
- }
}
//-------------------------------------------------
@@ -1228,30 +1229,22 @@ render_texture *menu_select_game::get_icon_texture(int linenum, void *selectedre
void menu_select_game::inkey_export()
{
std::vector<game_driver const *> list;
- if (!m_search.empty())
+ if (m_populated_favorites)
{
- for (int curitem = 0; (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH); ++curitem)
- list.push_back(m_searchlist[curitem].second.get().driver);
+ // iterate over favorites
+ mame_machine_manager::instance()->favorite().apply(
+ [&list] (ui_software_info const &info)
+ {
+ assert(info.driver);
+ if (info.startempty)
+ list.push_back(info.driver);
+ });
}
else
{
- if (m_populated_favorites)
- {
- // iterate over favorites
- mame_machine_manager::instance()->favorite().apply(
- [&list] (ui_software_info const &info)
- {
- assert(info.driver);
- if (info.startempty)
- list.push_back(info.driver);
- });
- }
- else
- {
- list.reserve(m_displaylist.size());
- for (ui_system_info const &info : m_displaylist)
- list.emplace_back(info.driver);
- }
+ list.reserve(m_displaylist.size());
+ for (ui_system_info const &info : m_displaylist)
+ list.emplace_back(info.driver);
}
menu::stack_push<menu_export>(ui(), container(), std::move(list));
diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp
index 5ee05186028..26635c6d31a 100644
--- a/src/frontend/mame/ui/selsoft.cpp
+++ b/src/frontend/mame/ui/selsoft.cpp
@@ -75,6 +75,23 @@ bool compare_software(ui_software_info const &a, ui_software_info const &b)
} // 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)))
+ , ucs_longname(ustr_from_utf8(normalize_unicode(s.longname, unicode_normalization_form::D, true)))
+ , penalty(1.0)
+{
+}
+
+void menu_select_software::search_item::set_penalty(std::u32string const &search)
+{
+ // TODO: search alternate title as well
+ penalty = util::edit_distance(search, ucs_shortname);
+ if (penalty)
+ penalty = (std::min)(penalty, util::edit_distance(search, ucs_longname));
+}
+
+
//-------------------------------------------------
// ctor
//-------------------------------------------------
@@ -89,6 +106,8 @@ menu_select_software::menu_select_software(mame_ui_manager &mui, render_containe
, m_filters()
, m_filter_type(software_filter::ALL)
, m_swinfo()
+ , m_searchlist()
+ , m_displaylist()
{
reselect_last::reselect(false);
@@ -237,13 +256,20 @@ void menu_select_software::populate(float &customtop, float &custombottom)
m_has_empty_start = true;
int old_software = -1;
+ // FIXME: why does it do this relatively expensive operation every time?
machine_config config(m_driver, machine().options());
for (device_image_interface &image : image_interface_iterator(config.root_device()))
- if (image.filename() == nullptr && image.must_be_loaded())
+ {
+ if (!image.filename() && image.must_be_loaded())
{
m_has_empty_start = false;
break;
}
+ }
+
+ // start with an empty list
+ m_displaylist.clear();
+ filter_map::const_iterator const flt(m_filters.find(m_filter_type));
// no active search
if (m_search.empty())
@@ -252,34 +278,44 @@ void menu_select_software::populate(float &customtop, float &custombottom)
if (m_has_empty_start)
item_append("[Start empty]", "", flags_ui, (void *)&m_swinfo[0]);
- m_displaylist.clear();
- filter_map::const_iterator const it(m_filters.find(m_filter_type));
- if (m_filters.end() == it)
+ if (m_filters.end() == flt)
std::copy(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist));
else
- it->second->apply(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist));
+ flt->second->apply(std::next(m_swinfo.begin()), m_swinfo.end(), std::back_inserter(m_displaylist));
+ }
+ else
+ {
+ find_matches();
- // iterate over entries
- for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem)
+ if (m_filters.end() == flt)
+ {
+ std::transform(
+ m_searchlist.begin(),
+ std::next(m_searchlist.begin(), (std::min)(m_searchlist.size(), MAX_VISIBLE_SEARCH)),
+ std::back_inserter(m_displaylist),
+ [] (search_item const &entry) { return entry.software; });
+ }
+ else
{
- if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty())
- old_software = 0;
- else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist())
- old_software = m_has_empty_start ? curitem + 1 : curitem;
-
- item_append(
- m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype,
- m_displaylist[curitem].get().parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&m_displaylist[curitem].get());
+ for (auto it = m_searchlist.begin(); (m_searchlist.end() != it) && (MAX_VISIBLE_SEARCH > m_displaylist.size()); ++it)
+ {
+ if (flt->second->apply(it->software))
+ m_displaylist.emplace_back(it->software);
+ }
}
}
- else
- {
- find_matches(m_search.c_str(), MAX_VISIBLE_SEARCH);
- for (int curitem = 0; m_searchlist[curitem] != nullptr; ++curitem)
- item_append(m_searchlist[curitem]->longname, m_searchlist[curitem]->devicetype,
- m_searchlist[curitem]->parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui),
- (void *)m_searchlist[curitem]);
+ // iterate over entries
+ for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem)
+ {
+ if (reselect_last::software() == "[Start empty]" && !reselect_last::driver().empty())
+ old_software = 0;
+ else if (m_displaylist[curitem].get().shortname == reselect_last::software() && m_displaylist[curitem].get().listname == reselect_last::swlist())
+ old_software = m_has_empty_start ? curitem + 1 : curitem;
+
+ item_append(
+ m_displaylist[curitem].get().longname, m_displaylist[curitem].get().devicetype,
+ m_displaylist[curitem].get().parentname.empty() ? flags_ui : (FLAG_INVERT | flags_ui), (void *)&m_displaylist[curitem].get());
}
item_append(menu_item_type::SEPARATOR, flags_ui);
@@ -502,43 +538,25 @@ void menu_select_software::load_sw_custom_filters()
// find approximate matches
//-------------------------------------------------
-void menu_select_software::find_matches(const char *str, int count)
+void menu_select_software::find_matches()
{
- // allocate memory to track the penalty value
- std::vector<double> penalty(count, 1.0);
- std::u32string const search(ustr_from_utf8(normalize_unicode(str, unicode_normalization_form::D, true)));
-
- int index = 0;
- for ( ; index < m_displaylist.size(); ++index)
+ // ensure search list is populated
+ if (m_searchlist.empty())
{
- // pick the best match between shortname and longname
- // TODO: search alternate title as well
- double curpenalty(util::edit_distance(search, ustr_from_utf8(normalize_unicode(m_displaylist[index].get().shortname, unicode_normalization_form::D, true))));
- if (curpenalty)
- {
- double const tmp(util::edit_distance(search, ustr_from_utf8(normalize_unicode(m_displaylist[index].get().longname, unicode_normalization_form::D, true))));
- curpenalty = (std::min)(curpenalty, tmp);
- }
-
- // insert into the sorted table of matches
- for (int matchnum = count - 1; matchnum >= 0; --matchnum)
- {
- // stop if we're worse than the current entry
- if (curpenalty >= penalty[matchnum])
- break;
+ m_searchlist.reserve(m_swinfo.size());
+ std::copy(m_swinfo.begin(), m_swinfo.end(), std::back_inserter(m_searchlist));
+ }
- // as long as this isn't the last entry, bump this one down
- if (matchnum < count - 1)
- {
- penalty[matchnum + 1] = penalty[matchnum];
- m_searchlist[matchnum + 1] = m_searchlist[matchnum];
- }
+ // update search
+ const std::u32string ucs_search(ustr_from_utf8(normalize_unicode(m_search, unicode_normalization_form::D, true)));
+ for (search_item &entry : m_searchlist)
+ entry.set_penalty(ucs_search);
- m_searchlist[matchnum] = &m_displaylist[index].get();
- penalty[matchnum] = curpenalty;
- }
- }
- (index < count) ? m_searchlist[index] = nullptr : m_searchlist[count] = nullptr;
+ // sort according to edit distance
+ std::stable_sort(
+ m_searchlist.begin(),
+ m_searchlist.end(),
+ [] (search_item const &lhs, search_item const &rhs) { return lhs.penalty < rhs.penalty; });
}
//-------------------------------------------------
@@ -646,7 +664,6 @@ void menu_select_software::filter_selected()
{
if ((software_filter::FIRST <= m_filter_highlight) && (software_filter::LAST >= m_filter_highlight))
{
- m_search.clear();
filter_map::const_iterator it(m_filters.find(software_filter::type(m_filter_highlight)));
if (m_filters.end() == it)
it = m_filters.emplace(software_filter::type(m_filter_highlight), software_filter::create(software_filter::type(m_filter_highlight), m_filter_data)).first;
diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h
index 1becf695841..1bfdd9c1e48 100644
--- a/src/frontend/mame/ui/selsoft.h
+++ b/src/frontend/mame/ui/selsoft.h
@@ -15,6 +15,10 @@
#include "ui/selmenu.h"
#include "ui/utils.h"
+#include <map>
+#include <string>
+#include <vector>
+
namespace ui {
@@ -29,6 +33,21 @@ private:
using filter_map = std::map<software_filter::type, software_filter::ptr>;
using icon_cache = texture_lru<ui_software_info const *>;
+ struct search_item
+ {
+ search_item(ui_software_info const &s);
+ search_item(search_item const &) = default;
+ search_item(search_item &&) = default;
+ search_item &operator=(search_item const &) = default;
+ search_item &operator=(search_item &&) = default;
+ void set_penalty(std::u32string const &search);
+
+ std::reference_wrapper<ui_software_info const> software;
+ std::u32string ucs_shortname;
+ std::u32string ucs_longname;
+ double penalty;
+ };
+
virtual void populate(float &customtop, float &custombottom) override;
virtual void handle() override;
@@ -51,7 +70,7 @@ private:
virtual void inkey_export() override { throw false; }
void build_software_list();
- void find_matches(const char *str, int count);
+ void find_matches();
void load_sw_custom_filters();
// handlers
@@ -68,7 +87,7 @@ private:
software_filter::type m_filter_type;
std::vector<ui_software_info> m_swinfo;
- ui_software_info const *m_searchlist[MAX_VISIBLE_SEARCH + 1];
+ std::vector<search_item> m_searchlist;
std::vector<std::reference_wrapper<ui_software_info const> > m_displaylist;
};