From 651d8ee692ca08541953f73d455663434f50ba02 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Wed, 9 Aug 2017 23:22:19 +1000 Subject: Polymorphic machine/software list filters: * Localisable filter names * Uniform interface for most filters without far less special-casing * Stacking year/manufacturer/etc. does OR rather than useless AND * Prevent stacking contradictory filters (e.g. parents and clones) * Fix alignment of filter list * Removed most of the lambdas added the other day * There's no longer an implicit "not BIOS" filter * Can't add (un)available to custom machine filter (will address) --- src/frontend/mame/ui/custmenu.cpp | 591 ++---------------- src/frontend/mame/ui/custmenu.h | 79 +-- src/frontend/mame/ui/optsmenu.cpp | 380 ++++++------ src/frontend/mame/ui/optsmenu.h | 13 +- src/frontend/mame/ui/selgame.cpp | 502 ++++----------- src/frontend/mame/ui/selgame.h | 2 - src/frontend/mame/ui/selsoft.cpp | 459 ++------------ src/frontend/mame/ui/selsoft.h | 9 +- src/frontend/mame/ui/utils.cpp | 1220 +++++++++++++++++++++++++++++++++++-- src/frontend/mame/ui/utils.h | 236 ++++--- src/mame/drivers/neogeo.cpp | 2 +- 11 files changed, 1761 insertions(+), 1732 deletions(-) (limited to 'src') diff --git a/src/frontend/mame/ui/custmenu.cpp b/src/frontend/mame/ui/custmenu.cpp index 45d981174d1..ea5562a0d9e 100644 --- a/src/frontend/mame/ui/custmenu.cpp +++ b/src/frontend/mame/ui/custmenu.cpp @@ -11,571 +11,108 @@ #include "emu.h" #include "ui/custmenu.h" -#include "ui/ui.h" -#include "ui/selector.h" -#include "ui/inifile.h" - -#include "rendfont.h" +#include namespace ui { -/************************************************** - MENU CUSTOM FILTER -**************************************************/ -//------------------------------------------------- -// ctor / dtor -//------------------------------------------------- -menu_custom_filter::menu_custom_filter(mame_ui_manager &mui, render_container &container, bool _single_menu) - : menu(mui, container) - , m_single_menu(_single_menu) - , m_added(false) -{ -} - -menu_custom_filter::~menu_custom_filter() -{ - if (m_single_menu) - reset_topmost(reset_options::SELECT_FIRST); - save_custom_filters(); -} - -//------------------------------------------------- -// handle -//------------------------------------------------- -void menu_custom_filter::handle() -{ - bool changed = false; - m_added = false; - - // process the menu - const event *menu_event = process(PROCESS_LR_REPEAT); - if (menu_event != nullptr && menu_event->itemref != nullptr) - { - switch ((uintptr_t)menu_event->itemref) - { - case MAIN_FILTER: - if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) - { - (menu_event->iptkey == IPT_UI_RIGHT) ? custfltr::main++ : custfltr::main--; - changed = true; - } - break; +namespace { - case ADD_FILTER: - if (menu_event->iptkey == IPT_UI_SELECT) - { - custfltr::numother++; - custfltr::other[custfltr::numother] = machine_filter::UNAVAILABLE; - ++custfltr::other[custfltr::numother]; - m_added = true; - } - break; +constexpr char const *region_lists[] = { + "arab", "arg", "asia", "aus", "aut", + "bel", "blr", "bra", + "can", "chi", "chn", "cze", + "den", + "ecu", "esp", "euro", + "fin", "fra", + "gbr", "ger", "gre", + "hkg", "hun", + "irl", "isr", "isv", "ita", + "jpn", + "kaz", "kor", + "lat", "lux", + "mex", + "ned", "nld", "nor", "nzl", + "pol", + "rus", + "slo", "spa", "sui", "swe", + "tha", "tpe", "tw", + "uk", "ukr", "usa" }; - case REMOVE_FILTER: - if (menu_event->iptkey == IPT_UI_SELECT) - { - custfltr::other[custfltr::numother] = machine_filter::UNAVAILABLE; - ++custfltr::other[custfltr::numother]; - custfltr::numother--; - changed = true; - } - break; - } - - if ((uintptr_t)menu_event->itemref >= OTHER_FILTER && (uintptr_t)menu_event->itemref < OTHER_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - OTHER_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && custfltr::other[pos] > machine_filter::UNAVAILABLE + 1) - { - custfltr::other[pos]--; - for ( ; custfltr::other[pos] > machine_filter::UNAVAILABLE && (custfltr::other[pos] == machine_filter::CATEGORY - || custfltr::other[pos] == machine_filter::FAVORITE); custfltr::other[pos]--) { }; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && custfltr::other[pos] < machine_filter::LAST - 1) - { - custfltr::other[pos]++; - for ( ; custfltr::other[pos] < machine_filter::LAST && (custfltr::other[pos] == machine_filter::CATEGORY - || custfltr::other[pos] == machine_filter::FAVORITE); custfltr::other[pos]++) { }; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - std::vector types; - std::vector names; - types.reserve(machine_filter::COUNT); - names.reserve(machine_filter::COUNT); - int sel(-1); - for (machine_filter::type index = machine_filter::FIRST; index < machine_filter::COUNT; ++index) - { - if ((index > machine_filter::UNAVAILABLE) && (index != machine_filter::CATEGORY) && (index != machine_filter::FAVORITE) && (index != machine_filter::CUSTOM)) - { - if (custfltr::other[pos] == index) - sel = types.size(); - types.emplace_back(index); - names.emplace_back(machine_filter::display_name(index)); - } - } - menu::stack_push(ui(), container(), std::move(names), sel, [this, pos, t = std::move(types)] (int selection) { custfltr::other[pos] = t[selection]; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= YEAR_FILTER && (uintptr_t)menu_event->itemref < YEAR_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - YEAR_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && custfltr::year[pos] > 0) - { - custfltr::year[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && custfltr::year[pos] < c_year::ui.size() - 1) - { - custfltr::year[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(c_year::ui), custfltr::year[pos], [this, pos] (int selection) { custfltr::year[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= MNFCT_FILTER && (uintptr_t)menu_event->itemref < MNFCT_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - MNFCT_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && custfltr::mnfct[pos] > 0) - { - custfltr::mnfct[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && custfltr::mnfct[pos] < c_mnfct::ui.size() - 1) - { - custfltr::mnfct[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(c_mnfct::ui), custfltr::mnfct[pos], [this, pos] (int selection) { custfltr::mnfct[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - } - - if (changed) - reset(reset_options::REMEMBER_REF); - else if (m_added) - reset(reset_options::SELECT_FIRST); -} +} // anonymous namespace //------------------------------------------------- -// populate +// set software regions //------------------------------------------------- -void menu_custom_filter::populate(float &customtop, float &custombottom) -{ - // add main filter - uint32_t arrow_flags = get_arrow_flags(machine_filter::ALL, machine_filter::UNAVAILABLE, custfltr::main); - item_append(_("Main filter"), machine_filter::display_name(custfltr::main), arrow_flags, (void *)(uintptr_t)MAIN_FILTER); - - // add other filters - for (int x = 1; x <= custfltr::numother; x++) - { - item_append(menu_item_type::SEPARATOR); - - // add filter items - arrow_flags = get_arrow_flags(machine_filter::UNAVAILABLE + 1, machine_filter::LAST - 1, custfltr::other[x]); - item_append(_("Other filter"), machine_filter::display_name(custfltr::other[x]), arrow_flags, (void *)(uintptr_t)(OTHER_FILTER + x)); - - if (m_added) - selected = item.size() - 2; - - // add manufacturer subitem - if (custfltr::other[x] == machine_filter::MANUFACTURER && c_mnfct::ui.size() > 0) - { - arrow_flags = get_arrow_flags(0, c_mnfct::ui.size() - 1, custfltr::mnfct[x]); - std::string fbuff(_("^!Manufacturer")); - convert_command_glyph(fbuff); - item_append(fbuff, c_mnfct::ui[custfltr::mnfct[x]], arrow_flags, (void *)(uintptr_t)(MNFCT_FILTER + x)); - } - - // add year subitem - else if (custfltr::other[x] == machine_filter::YEAR && c_year::ui.size() > 0) - { - arrow_flags = get_arrow_flags(0, c_year::ui.size() - 1, custfltr::year[x]); - std::string fbuff(_("^!Year")); - convert_command_glyph(fbuff); - item_append(fbuff, c_year::ui[custfltr::year[x]], arrow_flags, (void *)(uintptr_t)(YEAR_FILTER + x)); - } - } - - item_append(menu_item_type::SEPARATOR); - if (custfltr::numother > 0) - item_append(_("Remove last filter"), "", 0, (void *)(uintptr_t)REMOVE_FILTER); - - if (custfltr::numother < MAX_CUST_FILTER - 2) - item_append(_("Add filter"), "", 0, (void *)(uintptr_t)ADD_FILTER); - - item_append(menu_item_type::SEPARATOR); - customtop = ui().get_line_height() + 3.0f * UI_BOX_TB_BORDER; -} - -//------------------------------------------------- -// perform our special rendering -//------------------------------------------------- -void menu_custom_filter::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) +void c_sw_region::set(std::string &str) { - char const *const text[] = { _("Select custom filters:") }; - draw_text_box( - std::begin(text), std::end(text), - origx1, origx2, origy1 - top, origy1 - UI_BOX_TB_BORDER, - ui::text_layout::CENTER, ui::text_layout::NEVER, false, - UI_TEXT_COLOR, UI_GREEN_COLOR, 1.0f); + std::string name(getname(str)); + std::vector::iterator const pos(std::lower_bound(ui.begin(), ui.end(), name)); + if ((ui.end() == pos) || (*pos != str)) + ui.emplace(pos, std::move(name)); } -//------------------------------------------------- -// save custom filters info to file -//------------------------------------------------- - -void menu_custom_filter::save_custom_filters() +std::string c_sw_region::getname(std::string const &str) const { - // attempt to open the output file - emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (file.open("custom_", emulator_info::get_configname(), "_filter.ini") == osd_file::error::NONE) + std::string fullname(str); + strmakelower(fullname); + size_t found = fullname.find("("); + + if (found != std::string::npos) { - // generate custom filters info - std::ostringstream cinfo; - util::stream_format(cinfo, "Total filters = %d\n", (custfltr::numother + 1)); - util::stream_format(cinfo, "Main filter = %s\n", machine_filter::config_name(custfltr::main)); + size_t ends = fullname.find_first_not_of("abcdefghijklmnopqrstuvwxyz", found + 1); + std::string temp(fullname.substr(found + 1, ends - found - 1)); - for (int x = 1; x <= custfltr::numother; x++) - { - util::stream_format(cinfo, "Other filter = %s\n", machine_filter::config_name(custfltr::other[x])); - if (custfltr::other[x] == machine_filter::MANUFACTURER) - util::stream_format(cinfo, " Manufacturer filter = %s\n", c_mnfct::ui[custfltr::mnfct[x]]); - else if (custfltr::other[x] == machine_filter::YEAR) - util::stream_format(cinfo, " Year filter = %s\n", c_year::ui[custfltr::year[x]]); - } - file.puts(cinfo.str().c_str()); - file.close(); + for (auto & elem : region_lists) + if (temp == elem) + return (str.substr(found + 1, ends - found - 1)); } + return std::string(""); } -/************************************************** - MENU CUSTOM SOFTWARE FILTER -**************************************************/ //------------------------------------------------- -// ctor / dtor +// set software device type //------------------------------------------------- -menu_swcustom_filter::menu_swcustom_filter(mame_ui_manager &mui, render_container &container, const game_driver *_driver, s_filter &_filter) - : menu(mui, container) - , m_added(false) - , m_filter(_filter) - , m_driver(_driver) -{ -} -menu_swcustom_filter::~menu_swcustom_filter() +void c_sw_type::set(std::string &str) { - reset_topmost(reset_options::SELECT_FIRST); - save_sw_custom_filters(); + std::vector::iterator const pos(std::lower_bound(ui.begin(), ui.end(), str)); + if ((ui.end() == pos) || (*pos != str)) + ui.emplace(pos, str); } //------------------------------------------------- -// handle +// set software years //------------------------------------------------- -void menu_swcustom_filter::handle() -{ - bool changed = false; - m_added = false; - - // process the menu - const event *menu_event = process(PROCESS_LR_REPEAT); - if (menu_event != nullptr && menu_event->itemref != nullptr) - { - switch ((uintptr_t)menu_event->itemref) - { - case MAIN_FILTER: - if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) - { - (menu_event->iptkey == IPT_UI_RIGHT) ? sw_custfltr::main++ : sw_custfltr::main--; - changed = true; - } - break; - - case ADD_FILTER: - if (menu_event->iptkey == IPT_UI_SELECT) - { - sw_custfltr::numother++; - sw_custfltr::other[sw_custfltr::numother] = software_filter::UNAVAILABLE; - ++sw_custfltr::other[sw_custfltr::numother]; - m_added = true; - } - break; - - case REMOVE_FILTER: - if (menu_event->iptkey == IPT_UI_SELECT) - { - sw_custfltr::other[sw_custfltr::numother] = software_filter::UNAVAILABLE; - ++sw_custfltr::other[sw_custfltr::numother]; - sw_custfltr::numother--; - changed = true; - } - break; - } - - if ((uintptr_t)menu_event->itemref >= OTHER_FILTER && (uintptr_t)menu_event->itemref < OTHER_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - OTHER_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::other[pos] > software_filter::UNAVAILABLE + 1) - { - sw_custfltr::other[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::other[pos] < software_filter::LAST - 1) - { - sw_custfltr::other[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - std::vector types; - std::vector names; - types.reserve(software_filter::COUNT); - names.reserve(software_filter::COUNT); - uint16_t sel(0); - for (software_filter::type index = software_filter::FIRST; index < software_filter::COUNT; ++index) - { - if ((index >= software_filter::UNAVAILABLE) && (index != software_filter::CUSTOM)) - { - if (sw_custfltr::other[pos] == index) - sel = types.size(); - types.emplace_back(index); - names.emplace_back(software_filter::display_name(index)); - } - } - menu::stack_push(ui(), container(), std::move(names), sel, [this, pos, t = std::move(types)] (int selection) { sw_custfltr::other[pos] = t[selection]; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= YEAR_FILTER && (uintptr_t)menu_event->itemref < YEAR_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - YEAR_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::year[pos] > 0) - { - sw_custfltr::year[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::year[pos] < m_filter.year.ui.size() - 1) - { - sw_custfltr::year[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(m_filter.year.ui), sw_custfltr::year[pos], [this, pos] (int selection) { sw_custfltr::year[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= TYPE_FILTER && (uintptr_t)menu_event->itemref < TYPE_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - TYPE_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::type[pos] > 0) - { - sw_custfltr::type[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::type[pos] < m_filter.type.ui.size() - 1) - { - sw_custfltr::type[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(m_filter.type.ui), sw_custfltr::type[pos], [this, pos] (int selection) { sw_custfltr::type[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= MNFCT_FILTER && (uintptr_t)menu_event->itemref < MNFCT_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - MNFCT_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::mnfct[pos] > 0) - { - sw_custfltr::mnfct[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::mnfct[pos] < m_filter.publisher.ui.size() - 1) - { - sw_custfltr::mnfct[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(m_filter.publisher.ui), sw_custfltr::mnfct[pos], [this, pos] (int selection) { sw_custfltr::mnfct[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= REGION_FILTER && (uintptr_t)menu_event->itemref < REGION_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - REGION_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::region[pos] > 0) - { - sw_custfltr::region[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::region[pos] < m_filter.region.ui.size() - 1) - { - sw_custfltr::region[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(m_filter.region.ui), sw_custfltr::region[pos], [this, pos] (int selection) { sw_custfltr::region[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - else if ((uintptr_t)menu_event->itemref >= LIST_FILTER && (uintptr_t)menu_event->itemref < LIST_FILTER + MAX_CUST_FILTER) - { - int pos = (int)((uintptr_t)menu_event->itemref - LIST_FILTER); - if (menu_event->iptkey == IPT_UI_LEFT && sw_custfltr::list[pos] > 0) - { - sw_custfltr::list[pos]--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT && sw_custfltr::list[pos] < m_filter.swlist.name.size() - 1) - { - sw_custfltr::list[pos]++; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), std::vector(m_filter.swlist.description), sw_custfltr::list[pos], [this, pos] (int selection) { sw_custfltr::list[pos] = selection; reset(reset_options::REMEMBER_REF); }); - } - } - } - - if (changed) - reset(reset_options::REMEMBER_REF); - else if (m_added) - reset(reset_options::SELECT_FIRST); -} -//------------------------------------------------- -// populate -//------------------------------------------------- -void menu_swcustom_filter::populate(float &customtop, float &custombottom) +void c_sw_year::set(std::string &str) { - // add main filter - uint32_t arrow_flags = get_arrow_flags(software_filter::ALL, software_filter::UNAVAILABLE, sw_custfltr::main); - item_append(_("Main filter"), software_filter::display_name(sw_custfltr::main), arrow_flags, (void *)(uintptr_t)MAIN_FILTER); - - // add other filters - for (int x = 1; x <= sw_custfltr::numother; x++) - { - item_append(menu_item_type::SEPARATOR); - - // add filter items - arrow_flags = get_arrow_flags(software_filter::UNAVAILABLE + 1, software_filter::LAST - 1, sw_custfltr::other[x]); - item_append(_("Other filter"), software_filter::display_name(sw_custfltr::other[x]), arrow_flags, (void *)(uintptr_t)(OTHER_FILTER + x)); - - if (m_added) - selected = item.size() - 2; - - // add publisher subitem - if (sw_custfltr::other[x] == software_filter::PUBLISHERS && m_filter.publisher.ui.size()) - { - arrow_flags = get_arrow_flags(0, m_filter.publisher.ui.size() - 1, sw_custfltr::mnfct[x]); - std::string fbuff(_("^!Publisher")); - convert_command_glyph(fbuff); - item_append(fbuff, m_filter.publisher.ui[sw_custfltr::mnfct[x]], arrow_flags, (void *)(uintptr_t)(MNFCT_FILTER + x)); - } - - // add year subitem - else if (sw_custfltr::other[x] == software_filter::YEARS && m_filter.year.ui.size()) - { - arrow_flags = get_arrow_flags(0, m_filter.year.ui.size() - 1, sw_custfltr::year[x]); - std::string fbuff(_("^!Year")); - convert_command_glyph(fbuff); - item_append(fbuff, m_filter.year.ui[sw_custfltr::year[x]], arrow_flags, (void *)(uintptr_t)(YEAR_FILTER + x)); - } - - // add year subitem - else if (sw_custfltr::other[x] == software_filter::LIST && m_filter.swlist.name.size()) - { - arrow_flags = get_arrow_flags(0, m_filter.swlist.name.size() - 1, sw_custfltr::list[x]); - std::string fbuff(_("^!Software List")); - convert_command_glyph(fbuff); - item_append(fbuff, m_filter.swlist.description[sw_custfltr::list[x]], arrow_flags, (void *)(uintptr_t)(LIST_FILTER + x)); - } - - // add device type subitem - else if (sw_custfltr::other[x] == software_filter::DEVICE_TYPE && m_filter.type.ui.size()) - { - arrow_flags = get_arrow_flags(0, m_filter.type.ui.size() - 1, sw_custfltr::type[x]); - std::string fbuff(_("^!Device type")); - convert_command_glyph(fbuff); - item_append(fbuff, m_filter.type.ui[sw_custfltr::type[x]], arrow_flags, (void *)(uintptr_t)(TYPE_FILTER + x)); - } - - // add region subitem - else if (sw_custfltr::other[x] == software_filter::REGION && m_filter.region.ui.size()) - { - arrow_flags = get_arrow_flags(0, m_filter.region.ui.size() - 1, sw_custfltr::region[x]); - std::string fbuff(_("^!Region")); - convert_command_glyph(fbuff); - item_append(fbuff, m_filter.region.ui[sw_custfltr::region[x]], arrow_flags, (void *)(uintptr_t)(REGION_FILTER + x)); - } - } - - item_append(menu_item_type::SEPARATOR); - - if (sw_custfltr::numother > 0) - item_append(_("Remove last filter"), "", 0, (void *)(uintptr_t)REMOVE_FILTER); - - if (sw_custfltr::numother < MAX_CUST_FILTER - 2) - item_append(_("Add filter"), "", 0, (void *)(uintptr_t)ADD_FILTER); - - item_append(menu_item_type::SEPARATOR); - - customtop = ui().get_line_height() + 3.0f * UI_BOX_TB_BORDER; + std::vector::iterator const pos(std::lower_bound(ui.begin(), ui.end(), str)); + if ((ui.end() == pos) || (*pos != str)) + ui.emplace(pos, str); } //------------------------------------------------- -// perform our special rendering +// set software publishers //------------------------------------------------- -void menu_swcustom_filter::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2) + +void c_sw_publisher::set(std::string &str) { - char const *const text[] = { _("Select custom filters:") }; - draw_text_box( - std::begin(text), std::end(text), - origx1, origx2, origy1 - top, origy1 - UI_BOX_TB_BORDER, - ui::text_layout::CENTER, ui::text_layout::NEVER, false, - UI_TEXT_COLOR, UI_GREEN_COLOR, 1.0f); + std::string name(getname(str)); + std::vector::iterator const pos(std::lower_bound(ui.begin(), ui.end(), name)); + if ((ui.end() == pos) || (*pos != str)) + ui.emplace(pos, std::move(name)); } -//------------------------------------------------- -// save custom filters info to file -//------------------------------------------------- - -void menu_swcustom_filter::save_sw_custom_filters() +std::string c_sw_publisher::getname(std::string const &str) const { - // attempt to open the output file - emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); - if (file.open("custom_", m_driver->name, "_filter.ini") == osd_file::error::NONE) - { - // generate custom filters info - std::ostringstream cinfo; - util::stream_format(cinfo, "Total filters = %d\n", (sw_custfltr::numother + 1)); - util::stream_format(cinfo, "Main filter = %s\n", software_filter::config_name(sw_custfltr::main)); + size_t found = str.find("("); - for (int x = 1; x <= sw_custfltr::numother; x++) - { - util::stream_format(cinfo, "Other filter = %s\n", software_filter::config_name(sw_custfltr::other[x])); - if (sw_custfltr::other[x] == software_filter::PUBLISHERS) - util::stream_format(cinfo, " Manufacturer filter = %s\n", m_filter.publisher.ui[sw_custfltr::mnfct[x]]); - else if (sw_custfltr::other[x] == software_filter::LIST) - util::stream_format(cinfo, " Software List filter = %s\n", m_filter.swlist.name[sw_custfltr::list[x]]); - else if (sw_custfltr::other[x] == software_filter::YEARS) - util::stream_format(cinfo, " Year filter = %s\n", m_filter.year.ui[sw_custfltr::year[x]]); - else if (sw_custfltr::other[x] == software_filter::DEVICE_TYPE) - util::stream_format(cinfo, " Type filter = %s\n", m_filter.type.ui[sw_custfltr::type[x]]); - else if (sw_custfltr::other[x] == software_filter::REGION) - util::stream_format(cinfo, " Region filter = %s\n", m_filter.region.ui[sw_custfltr::region[x]]); - } - file.puts(cinfo.str().c_str()); - file.close(); - } + if (found != std::string::npos) + return (str.substr(0, found - 1)); + else + return str; } } // namespace ui diff --git a/src/frontend/mame/ui/custmenu.h b/src/frontend/mame/ui/custmenu.h index 77a470cf621..09911a50787 100644 --- a/src/frontend/mame/ui/custmenu.h +++ b/src/frontend/mame/ui/custmenu.h @@ -14,33 +14,29 @@ #pragma once -#include "ui/menu.h" -#include "ui/utils.h" namespace ui { + // Software region struct c_sw_region { std::vector ui; - uint16_t actual; void set(std::string &str); - std::string getname(std::string &str); + std::string getname(std::string const &str) const; }; // Software publishers struct c_sw_publisher { std::vector ui; - uint16_t actual; void set(std::string &str); - std::string getname(std::string &str); + std::string getname(std::string const &str) const; }; // Software device type struct c_sw_type { std::vector ui; - uint16_t actual; void set(std::string &str); }; @@ -49,14 +45,12 @@ struct c_sw_list { std::vector name; std::vector description; - uint16_t actual; }; // Software years struct c_sw_year { std::vector ui; - uint16_t actual; void set(std::string &str); }; @@ -69,73 +63,6 @@ struct s_filter c_sw_list swlist; }; -//------------------------------------------------- -// custom software filter menu class -//------------------------------------------------- -class menu_swcustom_filter : public menu -{ -public: - menu_swcustom_filter(mame_ui_manager &mui, render_container &container, const game_driver *_driver, s_filter &_filter); - virtual ~menu_swcustom_filter() override; - -protected: - virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - -private: - enum - { - MAIN_FILTER = 1, - ADD_FILTER, - REMOVE_FILTER, - MNFCT_FILTER, - YEAR_FILTER = MNFCT_FILTER + MAX_CUST_FILTER + 1, - REGION_FILTER = YEAR_FILTER + MAX_CUST_FILTER + 1, - TYPE_FILTER = REGION_FILTER + MAX_CUST_FILTER + 1, - LIST_FILTER = TYPE_FILTER + MAX_CUST_FILTER + 1, - OTHER_FILTER = LIST_FILTER + MAX_CUST_FILTER + 1 - }; - - virtual void populate(float &customtop, float &custombottom) override; - virtual void handle() override; - - void save_sw_custom_filters(); - - bool m_added; - s_filter &m_filter; - const game_driver *m_driver; -}; - -//------------------------------------------------- -// custom filter menu class -//------------------------------------------------- -class menu_custom_filter : public menu -{ -public: - menu_custom_filter(mame_ui_manager &mui, render_container &container, bool _single_menu = false); - virtual ~menu_custom_filter() override; - -protected: - virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - -private: - enum - { - MAIN_FILTER = 1, - ADD_FILTER, - REMOVE_FILTER, - MNFCT_FILTER, - YEAR_FILTER = MNFCT_FILTER + MAX_CUST_FILTER + 1, - SCREEN_FILTER = YEAR_FILTER + MAX_CUST_FILTER + 1, - OTHER_FILTER = SCREEN_FILTER + MAX_CUST_FILTER + 1 - }; - virtual void populate(float &customtop, float &custombottom) override; - virtual void handle() override; - - void save_custom_filters(); - - bool m_single_menu, m_added; -}; - } // namespace ui #endif /* MAME_FRONTEND_UI_CUSTMENU_H */ diff --git a/src/frontend/mame/ui/optsmenu.cpp b/src/frontend/mame/ui/optsmenu.cpp index ffeca417473..c25f4df62bc 100644 --- a/src/frontend/mame/ui/optsmenu.cpp +++ b/src/frontend/mame/ui/optsmenu.cpp @@ -42,7 +42,7 @@ menu_game_options::menu_game_options(mame_ui_manager &mui, render_container &con menu_game_options::~menu_game_options() { - main_filters::actual = machine_filter::type(m_main); + main_filters::actual = m_main; reset_topmost(reset_options::SELECT_FIRST); ui().save_ui_options(); ui_globals::switch_image = true; @@ -71,180 +71,163 @@ void menu_game_options::handle() if (menu_event != nullptr && menu_event->itemref != nullptr) switch ((uintptr_t)menu_event->itemref) { - case FILTER_MENU: + case FILTER_MENU: + if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) { - if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) - { - (menu_event->iptkey == IPT_UI_RIGHT) ? ++m_main : --m_main; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - std::vector s_sel(machine_filter::COUNT); - for (unsigned index = 0; index < s_sel.size(); ++index) - s_sel[index] = machine_filter::display_name(machine_filter::type(index)); - - menu::stack_push( - ui(), container(), std::move(s_sel), m_main, - [this] (int selection) - { - m_main = selection; - reset(reset_options::REMEMBER_REF); - }); - } - break; + (menu_event->iptkey == IPT_UI_RIGHT) ? ++m_main : --m_main; + changed = true; } - case FILE_CATEGORY_FILTER: + else if (menu_event->iptkey == IPT_UI_SELECT) { - if (menu_event->iptkey == IPT_UI_LEFT) - { - mame_machine_manager::instance()->inifile().move_file(-1); - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT) - { - mame_machine_manager::instance()->inifile().move_file(1); - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - inifile_manager &ifile = mame_machine_manager::instance()->inifile(); - int total = ifile.total(); - std::vector s_sel(total); - mame_machine_manager::instance()->inifile().set_cat(0); - for (size_t index = 0; index < total; ++index) - s_sel[index] = ifile.get_file(index); - - menu::stack_push( - ui(), container(), std::move(s_sel), ifile.cur_file(), - [this] (int selection) - { - mame_machine_manager::instance()->inifile().set_file(selection); - mame_machine_manager::instance()->inifile().set_cat(0); - reset(reset_options::REMEMBER_REF); - }); - } - break; + std::vector s_sel(machine_filter::COUNT); + for (unsigned index = 0; index < s_sel.size(); ++index) + s_sel[index] = machine_filter::display_name(machine_filter::type(index)); + + menu::stack_push( + ui(), container(), std::move(s_sel), m_main, + [this] (int selection) + { + m_main = machine_filter::type(selection); + reset(reset_options::REMEMBER_REF); + }); } - case CATEGORY_FILTER: + break; + case FILE_CATEGORY_FILTER: + if (menu_event->iptkey == IPT_UI_LEFT) { - if (menu_event->iptkey == IPT_UI_LEFT) - { - mame_machine_manager::instance()->inifile().move_cat(-1); - changed = true; - } - else if (menu_event->iptkey == IPT_UI_RIGHT) - { - mame_machine_manager::instance()->inifile().move_cat(1); - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - inifile_manager &ifile = mame_machine_manager::instance()->inifile(); - int total = ifile.cat_total(); - std::vector s_sel(total); - for (int index = 0; index < total; ++index) - s_sel[index] = ifile.get_category(index); - - menu::stack_push( - ui(), container(), std::move(s_sel), ifile.cur_cat(), - [this] (int selection) - { - mame_machine_manager::instance()->inifile().cur_cat() = selection; - mame_machine_manager::instance()->inifile().set_cat(selection); - reset(reset_options::REMEMBER_REF); - }); - } - break; + mame_machine_manager::instance()->inifile().move_file(-1); + changed = true; } - case MANUFACT_CAT_FILTER: - if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) - { - (menu_event->iptkey == IPT_UI_RIGHT) ? c_mnfct::actual++ : c_mnfct::actual--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push( - ui(), container(), std::vector(c_mnfct::ui), c_mnfct::actual, - [this] (int selection) - { - c_mnfct::actual = selection; - reset(reset_options::REMEMBER_REF); - }); - } - - break; - case YEAR_CAT_FILTER: - if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) - { - (menu_event->iptkey == IPT_UI_RIGHT) ? c_year::actual++ : c_year::actual--; - changed = true; - } - else if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push( - ui(), container(), std::vector(c_year::ui), c_year::actual, - [this] (int selection) + else if (menu_event->iptkey == IPT_UI_RIGHT) + { + mame_machine_manager::instance()->inifile().move_file(1); + changed = true; + } + else if (menu_event->iptkey == IPT_UI_SELECT) + { + inifile_manager &ifile = mame_machine_manager::instance()->inifile(); + int total = ifile.total(); + std::vector s_sel(total); + mame_machine_manager::instance()->inifile().set_cat(0); + for (size_t index = 0; index < total; ++index) + s_sel[index] = ifile.get_file(index); + + menu::stack_push( + ui(), container(), std::move(s_sel), ifile.cur_file(), + [this] (int selection) + { + mame_machine_manager::instance()->inifile().set_file(selection); + mame_machine_manager::instance()->inifile().set_cat(0); + reset(reset_options::REMEMBER_REF); + }); + } + break; + case CATEGORY_FILTER: + if (menu_event->iptkey == IPT_UI_LEFT) + { + mame_machine_manager::instance()->inifile().move_cat(-1); + changed = true; + } + else if (menu_event->iptkey == IPT_UI_RIGHT) + { + mame_machine_manager::instance()->inifile().move_cat(1); + changed = true; + } + else if (menu_event->iptkey == IPT_UI_SELECT) + { + inifile_manager &ifile = mame_machine_manager::instance()->inifile(); + int total = ifile.cat_total(); + std::vector s_sel(total); + for (int index = 0; index < total; ++index) + s_sel[index] = ifile.get_category(index); + + menu::stack_push( + ui(), container(), std::move(s_sel), ifile.cur_cat(), + [this] (int selection) + { + mame_machine_manager::instance()->inifile().cur_cat() = selection; + mame_machine_manager::instance()->inifile().set_cat(selection); + reset(reset_options::REMEMBER_REF); + }); + } + break; + case FILTER_ADJUST: + if (menu_event->iptkey == IPT_UI_LEFT) + { + changed = main_filters::filters.find(m_main)->second->adjust_left(); + } + else if (menu_event->iptkey == IPT_UI_RIGHT) + { + changed = main_filters::filters.find(m_main)->second->adjust_right(); + } + else if (menu_event->iptkey == IPT_UI_SELECT) + { + main_filters::filters.find(m_main)->second->show_ui( + ui(), + container(), + [this] (machine_filter &filter) + { + if (machine_filter::CUSTOM == filter.get_type()) { - c_year::actual = selection; - reset(reset_options::REMEMBER_REF); - }); - } - - break; - case CONF_DIR: - if (menu_event->iptkey == IPT_UI_SELECT) - menu::stack_push(ui(), container()); - break; - case MISC_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), submenu::misc_options); - ui_globals::reset = true; - } - break; - case SOUND_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container()); - ui_globals::reset = true; - } - break; - case DISPLAY_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), submenu::video_options); - ui_globals::reset = true; - } - break; - case CUSTOM_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - menu::stack_push(ui(), container()); - break; - case CONTROLLER_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - menu::stack_push(ui(), container(), submenu::control_options); - break; - case CGI_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - menu::stack_push(ui(), container()); - break; - case CUSTOM_FILTER: - if (menu_event->iptkey == IPT_UI_SELECT) - menu::stack_push(ui(), container()); - break; - case ADVANCED_MENU: - if (menu_event->iptkey == IPT_UI_SELECT) - { - menu::stack_push(ui(), container(), submenu::advanced_options); - ui_globals::reset = true; - } - break; - case SAVE_CONFIG: - if (menu_event->iptkey == IPT_UI_SELECT) - ui().save_main_option(); - break; + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file.open("custom_", emulator_info::get_configname(), "_filter.ini") == osd_file::error::NONE) + { + filter.save_ini(file, 0); + file.close(); + } + } + reset_options::REMEMBER_REF; + }); + } + break; + case CONF_DIR: + if (menu_event->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case MISC_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::misc_options); + ui_globals::reset = true; + } + break; + case SOUND_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container()); + ui_globals::reset = true; + } + break; + case DISPLAY_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::video_options); + ui_globals::reset = true; + } + break; + case CUSTOM_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case CONTROLLER_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container(), submenu::control_options); + break; + case CGI_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + menu::stack_push(ui(), container()); + break; + case ADVANCED_MENU: + if (menu_event->iptkey == IPT_UI_SELECT) + { + menu::stack_push(ui(), container(), submenu::advanced_options); + ui_globals::reset = true; + } + break; + case SAVE_CONFIG: + if (menu_event->iptkey == IPT_UI_SELECT) + ui().save_main_option(); + break; } if (changed) @@ -264,45 +247,38 @@ void menu_game_options::populate(float &customtop, float &custombottom) // add filter item uint32_t arrow_flags = get_arrow_flags(machine_filter::FIRST, machine_filter::LAST, m_main); - item_append(_("Filter"), machine_filter::display_name(machine_filter::type(m_main)), arrow_flags, (void *)(uintptr_t)FILTER_MENU); - - // add category subitem - if (m_main == machine_filter::CATEGORY && mame_machine_manager::instance()->inifile().total() > 0) + if (machine_filter::CATEGORY == m_main) { - inifile_manager &inif = mame_machine_manager::instance()->inifile(); + item_append(_("Filter"), machine_filter::display_name(m_main), arrow_flags, (void *)(uintptr_t)FILTER_MENU); + if (mame_machine_manager::instance()->inifile().total() > 0) + { + inifile_manager &inif = mame_machine_manager::instance()->inifile(); - arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(inif.total() - 1), inif.cur_file()); - fbuff = _(" ^!File"); - convert_command_glyph(fbuff); - item_append(fbuff, inif.get_file(), arrow_flags, (void *)(uintptr_t)FILE_CATEGORY_FILTER); + arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(inif.total() - 1), inif.cur_file()); + fbuff = _(" ^!File"); + convert_command_glyph(fbuff); + item_append(fbuff, inif.get_file(), arrow_flags, (void *)(uintptr_t)FILE_CATEGORY_FILTER); - arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(inif.cat_total() - 1), inif.cur_cat()); - fbuff = _(" ^!Category"); - convert_command_glyph(fbuff); - item_append(fbuff, inif.get_category(), arrow_flags, (void *)(uintptr_t)CATEGORY_FILTER); - } - // add manufacturer subitem - else if (m_main == machine_filter::MANUFACTURER && c_mnfct::ui.size() > 0) - { - arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(c_mnfct::ui.size() - 1), c_mnfct::actual); - fbuff = _("^!Manufacturer"); - convert_command_glyph(fbuff); - item_append(fbuff, c_mnfct::ui[c_mnfct::actual], arrow_flags, (void *)(uintptr_t)MANUFACT_CAT_FILTER); - } - // add year subitem - else if (m_main == machine_filter::YEAR && c_year::ui.size() > 0) - { - arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(c_year::ui.size() - 1), c_year::actual); - fbuff.assign(_("^!Year")); - convert_command_glyph(fbuff); - item_append(fbuff, c_year::ui[c_year::actual], arrow_flags, (void *)(uintptr_t)YEAR_CAT_FILTER); + arrow_flags = get_arrow_flags(uint16_t(0), uint16_t(inif.cat_total() - 1), inif.cur_cat()); + fbuff = _(" ^!Category"); + convert_command_glyph(fbuff); + item_append(fbuff, inif.get_category(), arrow_flags, (void *)(uintptr_t)CATEGORY_FILTER); + } } - // add custom subitem - else if (m_main == machine_filter::CUSTOM) + else { - fbuff = _("^!Setup custom filter"); - convert_command_glyph(fbuff); - item_append(fbuff, "", 0, (void *)(uintptr_t)CUSTOM_FILTER); + auto active_filter(main_filters::filters.find(m_main)); + if (main_filters::filters.end() == active_filter) + active_filter = main_filters::filters.emplace(m_main, machine_filter::create(m_main)).first; + item_append(_("Filter"), active_filter->second->display_name(), arrow_flags, (void *)(uintptr_t)FILTER_MENU); + + // add subitem if the filter wants it + if (active_filter->second->wants_adjuster()) + { + std::string name("^!"); + convert_command_glyph(name); + item_append(name, active_filter->second->adjust_text(), active_filter->second->arrow_flags(), (void *)(FILTER_ADJUST)); + } } item_append(menu_item_type::SEPARATOR); diff --git a/src/frontend/mame/ui/optsmenu.h b/src/frontend/mame/ui/optsmenu.h index 769d0aa9ecf..2ef9d2969c8 100644 --- a/src/frontend/mame/ui/optsmenu.h +++ b/src/frontend/mame/ui/optsmenu.h @@ -7,13 +7,14 @@ UI main options menu manager. ***************************************************************************/ - -#pragma once - #ifndef MAME_FRONTEND_UI_OPTSMENU_H #define MAME_FRONTEND_UI_OPTSMENU_H +#pragma once + #include "ui/menu.h" +#include "ui/utils.h" + namespace ui { class menu_game_options : public menu @@ -30,8 +31,7 @@ private: { FILTER_MENU = 1, FILE_CATEGORY_FILTER, - MANUFACT_CAT_FILTER, - YEAR_CAT_FILTER, + FILTER_ADJUST, CATEGORY_FILTER, CONF_DIR, DISPLAY_MENU, @@ -42,14 +42,13 @@ private: ADVANCED_MENU, SAVE_OPTIONS, CGI_MENU, - CUSTOM_FILTER, SAVE_CONFIG }; virtual void populate(float &customtop, float &custombottom) override; virtual void handle() override; - uint16_t m_main; + machine_filter::type m_main; }; } // namespace ui diff --git a/src/frontend/mame/ui/selgame.cpp b/src/frontend/mame/ui/selgame.cpp index aa3d1580e6f..1e504bb858f 100644 --- a/src/frontend/mame/ui/selgame.cpp +++ b/src/frontend/mame/ui/selgame.cpp @@ -74,49 +74,40 @@ menu_select_game::menu_select_game(mame_ui_manager &mui, render_container &conta if (!load_available_machines()) build_available_list(); - // load custom filter - load_custom_filters(); - if (first_start) { reselect_last::set_driver(moptions.last_used_machine()); + std::string tmp(moptions.last_used_filter()); - std::size_t found = tmp.find_first_of(","); + std::size_t const found = tmp.find_first_of(","); + std::string fake_ini; if (found == std::string::npos) - last_filter = tmp; + { + fake_ini = util::string_format("%s = 1\n", tmp); + } else { - last_filter = tmp.substr(0, found); - sub_filter = tmp.substr(found + 1); + std::string const sub_filter(tmp.substr(found + 1)); + tmp.resize(found); + fake_ini = util::string_format("%s = %s\n", tmp, sub_filter); } - main_filters::actual = machine_filter::ALL; - for (machine_filter::type ind = machine_filter::FIRST; ind < machine_filter::COUNT; ++ind) + emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); + if (file.open_ram(fake_ini.c_str(), fake_ini.size()) == osd_file::error::NONE) { - if (last_filter == machine_filter::config_name(ind)) + machine_filter::ptr flt(machine_filter::create(file)); + if (flt) { - main_filters::actual = ind; - break; + main_filters::actual = flt->get_type(); + main_filters::filters.emplace(main_filters::actual, std::move(flt)); } + file.close(); } - - if (main_filters::actual == machine_filter::CATEGORY) - main_filters::actual = machine_filter::ALL; - else if (main_filters::actual == machine_filter::MANUFACTURER) - { - for (size_t id = 0; id < c_mnfct::ui.size(); ++id) - if (sub_filter == c_mnfct::ui[id]) - c_mnfct::actual = id; - } - else if (main_filters::actual == machine_filter::YEAR) - { - for (size_t id = 0; id < c_year::ui.size(); ++id) - if (sub_filter == c_year::ui[id]) - c_year::actual = id; - } - first_start = false; } + // do this after processing the last used filter setting so it overwrites the placeholder + load_custom_filters(); + if (!moptions.remember_last()) reselect_last::reset(); @@ -151,11 +142,17 @@ menu_select_game::~menu_select_game() else if (swinfo && m_prev_selected) last_driver = reinterpret_cast(m_prev_selected)->shortname; - std::string filter(machine_filter::config_name(main_filters::actual)); - if (main_filters::actual == machine_filter::MANUFACTURER) - filter.append(",").append(c_mnfct::ui[c_mnfct::actual]); - else if (main_filters::actual == machine_filter::YEAR) - filter.append(",").append(c_year::ui[c_year::actual]); + std::string filter; + auto const active_filter(main_filters::filters.find(main_filters::actual)); + if (main_filters::filters.end() != active_filter) + { + char const *val(active_filter->second->filter_text()); + filter = val ? util::string_format("%s,%s", active_filter->second->config_name(), val) : active_filter->second->config_name(); + } + else + { + filter = machine_filter::config_name(main_filters::actual); + } ui_options &mopt = ui().options(); mopt.set_value(OPTION_LAST_USED_FILTER, filter.c_str(), OPTION_PRIORITY_CMDLINE); @@ -423,43 +420,38 @@ void menu_select_game::handle() if (check_filter) { m_search.clear(); - if (l_hover == machine_filter::CATEGORY) + switch (l_hover) { + case machine_filter::CATEGORY: + // FIXME: this should be unified with the other filters main_filters::actual = machine_filter::type(l_hover); menu::stack_push(ui(), container()); - } - else if (l_hover == machine_filter::CUSTOM) - { - main_filters::actual = machine_filter::type(l_hover); - menu::stack_push(ui(), container(), true); - } - else if (l_hover == machine_filter::MANUFACTURER) - { - menu::stack_push( - ui(), container(), std::vector(c_mnfct::ui), c_mnfct::actual, - [this] (int selection) - { - c_mnfct::actual = selection; - main_filters::actual = machine_filter::type(l_hover); - reset(reset_options::SELECT_FIRST); - }); - } - else if (l_hover == machine_filter::YEAR) - { - menu::stack_push( - ui(), container(), std::vector(c_year::ui), c_year::actual, - [this] (int selection) - { - c_year::actual = selection; - main_filters::actual = machine_filter::type(l_hover); - reset(reset_options::SELECT_FIRST); - }); - } - else - { + break; + default: if (l_hover >= machine_filter::ALL) - main_filters::actual = machine_filter::type(l_hover); - reset(reset_options::SELECT_FIRST); + { + auto it(main_filters::filters.find(machine_filter::type(l_hover))); + if (main_filters::filters.end() == it) + it = main_filters::filters.emplace(machine_filter::type(l_hover), machine_filter::create(machine_filter::type(l_hover))).first; + it->second->show_ui( + ui(), + container(), + [this] (machine_filter &filter) + { + machine_filter::type const new_type(filter.get_type()); + if (machine_filter::CUSTOM == new_type) + { + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file.open("custom_", emulator_info::get_configname(), "_filter.ini") == osd_file::error::NONE) + { + filter.save_ini(file, 0); + file.close(); + } + } + main_filters::actual = new_type; + reset(reset_options::SELECT_FIRST); + }); + } } } } @@ -479,7 +471,9 @@ void menu_select_game::populate(float &customtop, float &custombottom) { // if search is not empty, find approximate matches if (!m_search.empty()) + { populate_search(); + } else { // reset search string @@ -489,21 +483,27 @@ void menu_select_game::populate(float &customtop, float &custombottom) // if filter is set on category, build category list switch (main_filters::actual) { - case machine_filter::CATEGORY: - build_category(); + case machine_filter::ALL: + m_displaylist = m_sortedlist; break; - case machine_filter::MANUFACTURER: - build_list(c_mnfct::ui[c_mnfct::actual].c_str()); + case machine_filter::AVAILABLE: + m_displaylist = m_availsortedlist; break; - case machine_filter::YEAR: - build_list(c_year::ui[c_year::actual].c_str()); + case machine_filter::UNAVAILABLE: + m_displaylist = m_unavailsortedlist; break; - case machine_filter::CUSTOM: - build_custom(); + case machine_filter::CATEGORY: + build_category(); break; default: - build_list(); - break; + { + auto const it(main_filters::filters.find(main_filters::actual)); + std::copy_if( + m_sortedlist.begin(), + m_sortedlist.end(), + std::back_inserter(m_displaylist), + [&flt = *it->second] (game_driver const *drv) { return flt.apply(*drv); }); + } } // iterate over entries @@ -906,139 +906,6 @@ void menu_select_game::inkey_special(const event *menu_event) } -//------------------------------------------------- -// build list -//------------------------------------------------- - -void menu_select_game::build_list(const char *filter_text, int filter, bool bioscheck, std::vector s_drivers) -{ - if (s_drivers.empty()) - { - filter = main_filters::actual; - if (filter == machine_filter::AVAILABLE) - s_drivers = m_availsortedlist; - else if (filter == machine_filter::UNAVAILABLE) - s_drivers = m_unavailsortedlist; - else - s_drivers = m_sortedlist; - } - - for (auto & s_driver : s_drivers) - { - if (!bioscheck && filter != machine_filter::BIOS && (s_driver->flags & machine_flags::IS_BIOS_ROOT) != 0) - continue; - - switch (filter) - { - case machine_filter::ALL: - case machine_filter::AVAILABLE: - case machine_filter::UNAVAILABLE: - m_displaylist.push_back(s_driver); - break; - - case machine_filter::WORKING: - if (!(s_driver->flags & machine_flags::NOT_WORKING)) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::NOT_MECHANICAL: - if (!(s_driver->flags & machine_flags::MECHANICAL)) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::BIOS: - if (s_driver->flags & machine_flags::IS_BIOS_ROOT) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::PARENT: - case machine_filter::CLONES: - { - bool cloneof = strcmp(s_driver->parent, "0"); - if (cloneof) - { - auto cx = driver_list::find(s_driver->parent); - if (cx != -1 && ((driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT) != 0)) - cloneof = false; - } - - if (filter == machine_filter::CLONES && cloneof) - m_displaylist.push_back(s_driver); - else if (filter == machine_filter::PARENT && !cloneof) - m_displaylist.push_back(s_driver); - } - break; - case machine_filter::NOT_WORKING: - if (s_driver->flags & machine_flags::NOT_WORKING) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::MECHANICAL: - if (s_driver->flags & machine_flags::MECHANICAL) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::SAVE: - if (s_driver->flags & machine_flags::SUPPORTS_SAVE) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::NOSAVE: - if (!(s_driver->flags & machine_flags::SUPPORTS_SAVE)) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::YEAR: - if (!core_stricmp(filter_text, s_driver->year)) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::VERTICAL: - if (s_driver->flags & ORIENTATION_SWAP_XY) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::HORIZONTAL: - if (!(s_driver->flags & ORIENTATION_SWAP_XY)) - m_displaylist.push_back(s_driver); - break; - - case machine_filter::MANUFACTURER: - { - std::string name = c_mnfct::getname(s_driver->manufacturer); - if (!core_stricmp(filter_text, name.c_str())) - m_displaylist.push_back(s_driver); - } - break; - case machine_filter::CHD: - { - auto entries = rom_build_entries(s_driver->rom); - for (const rom_entry &rom : entries) - if (ROMENTRY_ISREGION(&rom) && ROMREGION_ISDISKDATA(&rom)) - { - m_displaylist.push_back(s_driver); - break; - } - } - break; - case machine_filter::NOCHD: - { - auto entries = rom_build_entries(s_driver->rom); - bool found = false; - for (const rom_entry &rom : entries) - if (ROMENTRY_ISREGION(&rom) && ROMREGION_ISDISKDATA(&rom)) - { - found = true; - break; - } - if (!found) - m_displaylist.push_back(s_driver); - } - break; - } - } -} - //------------------------------------------------- // change what's displayed in the info box //------------------------------------------------- @@ -1074,55 +941,6 @@ void menu_select_game::change_info_pane(int delta) } } -//------------------------------------------------- -// build custom display list -//------------------------------------------------- - -void menu_select_game::build_custom() -{ - std::vector s_drivers; - bool bioscheck = false; - - if (custfltr::main == machine_filter::AVAILABLE) - s_drivers = m_availsortedlist; - else if (custfltr::main == machine_filter::UNAVAILABLE) - s_drivers = m_unavailsortedlist; - else - s_drivers = m_sortedlist; - - for (auto & elem : s_drivers) - { - m_displaylist.push_back(elem); - } - - for (int count = 1; count <= custfltr::numother; ++count) - { - int filter = custfltr::other[count]; - if (filter == machine_filter::BIOS) - bioscheck = true; - } - - for (int count = 1; count <= custfltr::numother; ++count) - { - int filter = custfltr::other[count]; - s_drivers = m_displaylist; - m_displaylist.clear(); - - switch (filter) - { - case machine_filter::YEAR: - build_list(c_year::ui[custfltr::year[count]].c_str(), filter, bioscheck, s_drivers); - break; - case machine_filter::MANUFACTURER: - build_list(c_mnfct::ui[custfltr::mnfct[count]].c_str(), filter, bioscheck, s_drivers); - break; - default: - build_list(nullptr, filter, bioscheck, s_drivers); - break; - } - } -} - //------------------------------------------------- // build category list //------------------------------------------------- @@ -1378,24 +1196,27 @@ void menu_select_game::init_sorted_list() return; // generate full list + std::unordered_set manufacturers, years; for (int x = 0; x < driver_list::total(); ++x) { - const game_driver *driver = &driver_list::driver(x); - if (driver == &GAME_NAME(___empty)) - continue; - if (driver->flags & machine_flags::IS_BIOS_ROOT) - m_isabios++; - - m_sortedlist.push_back(driver); - c_mnfct::set(driver->manufacturer); - c_year::set(driver->year); - } + game_driver const &driver(driver_list::driver(x)); + if (&driver != &GAME_NAME(___empty)) + { + if (driver.flags & machine_flags::IS_BIOS_ROOT) + m_isabios++; - for (auto & e : c_mnfct::uimap) - c_mnfct::ui.emplace_back(e.first); + m_sortedlist.push_back(&driver); + manufacturers.emplace(c_mnfct::getname(driver.manufacturer)); + years.emplace(driver.year); + } + } // sort manufacturers - years and driver - std::stable_sort(c_mnfct::ui.begin(), c_mnfct::ui.end()); + for (auto it = manufacturers.begin(); manufacturers.end() != it; it = manufacturers.erase(it)) + c_mnfct::ui.emplace_back(*it); + std::sort(c_mnfct::ui.begin(), c_mnfct::ui.end(), [] (std::string const &x, std::string const &y) { return 0 > core_stricmp(x.c_str(), y.c_str()); }); + for (auto it = years.begin(); years.end() != it; it = years.erase(it)) + c_year::ui.emplace_back(*it); std::stable_sort(c_year::ui.begin(), c_year::ui.end()); std::stable_sort(m_sortedlist.begin(), m_sortedlist.end(), sorted_game_list); } @@ -1457,60 +1278,12 @@ bool menu_select_game::load_available_machines() void menu_select_game::load_custom_filters() { - // attempt to open the output file emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); if (file.open("custom_", emulator_info::get_configname(), "_filter.ini") == osd_file::error::NONE) { - char buffer[MAX_CHAR_INFO]; - - // get number of filters - file.gets(buffer, MAX_CHAR_INFO); - char *pb = strchr(buffer, '='); - custfltr::numother = atoi(++pb) - 1; - - // get main filter - file.gets(buffer, MAX_CHAR_INFO); - pb = strchr(buffer, '=') + 2; - - for (machine_filter::type y = machine_filter::FIRST; y < machine_filter::COUNT; ++y) - { - char const *const name(machine_filter::config_name(y)); - if (!strncmp(pb, name, strlen(name))) - { - custfltr::main = y; - break; - } - } - - for (int x = 1; x <= custfltr::numother; ++x) - { - file.gets(buffer, MAX_CHAR_INFO); - char *cb = strchr(buffer, '=') + 2; - for (machine_filter::type y = machine_filter::FIRST; y < machine_filter::COUNT; ++y) - { - char const *const name(machine_filter::config_name(y)); - if (!strncmp(cb, name, strlen(name))) - { - custfltr::other[x] = y; - if (y == machine_filter::MANUFACTURER) - { - file.gets(buffer, MAX_CHAR_INFO); - char *ab = strchr(buffer, '=') + 2; - for (size_t z = 0; z < c_mnfct::ui.size(); ++z) - if (!strncmp(ab, c_mnfct::ui[z].c_str(), c_mnfct::ui[z].length())) - custfltr::mnfct[x] = z; - } - else if (y == machine_filter::YEAR) - { - file.gets(buffer, MAX_CHAR_INFO); - char *db = strchr(buffer, '=') + 2; - for (size_t z = 0; z < c_year::ui.size(); ++z) - if (!strncmp(db, c_year::ui[z].c_str(), c_year::ui[z].length())) - custfltr::year[x] = z; - } - } - } - } + machine_filter::ptr flt(machine_filter::create(file)); + if (flt) + main_filters::filters[flt->get_type()] = std::move(flt); // not emplace/insert - could replace bogus filter from ui.ini line file.close(); } @@ -1527,13 +1300,13 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) if (ui_globals::panels_status == SHOW_PANELS || ui_globals::panels_status == HIDE_RIGHT_PANEL) { + auto const active_filter(main_filters::filters.find(main_filters::actual)); float origy1 = y1; float origy2 = y2; float text_size = ui().options().infos_size(); float line_height_max = line_height * text_size; float left_width = 0.0f; int line_count = machine_filter::COUNT; - int afilter = main_filters::actual; int phover = HOVER_FILTER_FIRST; float sc = y2 - y1 - (2.0f * UI_BOX_TB_BORDER); @@ -1544,7 +1317,9 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) line_height_max = line_height * text_size; } - float text_sign = ui().get_string_width("_# ", text_size); + std::string tmp("_# "); + convert_command_glyph(tmp); + float text_sign = ui().get_string_width(tmp.c_str(), text_size); for (machine_filter::type x = machine_filter::FIRST; x < machine_filter::COUNT; ++x) { float total_width; @@ -1569,7 +1344,21 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) for (machine_filter::type filter = machine_filter::FIRST; filter < machine_filter::COUNT; ++filter) { - std::string str(machine_filter::display_name(filter)); + std::string str; + if (main_filters::filters.end() != active_filter) + { + str = active_filter->second->adorned_display_name(filter); + } + else + { + if (main_filters::actual == filter) + { + str = std::string("_> ") + str; + convert_command_glyph(str); + } + str.append(machine_filter::display_name(filter)); + } + rgb_t bgcolor = UI_TEXT_BG_COLOR; rgb_t fgcolor = UI_TEXT_COLOR; @@ -1589,35 +1378,7 @@ float menu_select_game::draw_left_panel(float x1, float y1, float x2, float y2) hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); } - float x1t = x1 + text_sign; - if (afilter == machine_filter::CUSTOM) - { - if (filter == custfltr::main) - { - str = std::string("@custom1 ") + str; - x1t -= text_sign; - } - else - { - for (int count = 1; count <= custfltr::numother; ++count) - { - int cfilter = custfltr::other[count]; - if (cfilter == filter) - { - str = string_format("@custom%d %s", count + 1, str); - x1t -= text_sign; - break; - } - } - } - convert_command_glyph(str); - } - else if (filter == main_filters::actual) - { - str = std::string("_> ") + str; - x1t -= text_sign; - convert_command_glyph(str); - } + float const x1t = x1 + ((str == machine_filter::display_name(filter)) ? text_sign : 0.0f); ui().draw_text_full(container(), str.c_str(), x1t, y1, x2 - x1, ui::text_layout::LEFT, ui::text_layout::NEVER, mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, text_size); @@ -1705,33 +1466,28 @@ void menu_select_game::make_topbox_text(std::string &line0, std::string &line1, (driver_list::total() - 1), m_isabios); - std::string filtered; - if (main_filters::actual == machine_filter::CATEGORY && inifile.total() > 0) + if (isfavorite()) { - filtered = string_format(_("%1$s (%2$s - %3$s) - "), - machine_filter::display_name(main_filters::actual), - inifile.get_file(), - inifile.get_category()); + line1.clear(); } - else if (main_filters::actual == machine_filter::MANUFACTURER) + if (main_filters::actual == machine_filter::CATEGORY && inifile.total() > 0) { - filtered = string_format(_("%1$s (%2$s) - "), + line1 = string_format(_("%1$s (%2$s - %3$s) - Search: %4$s_"), machine_filter::display_name(main_filters::actual), - c_mnfct::ui[c_mnfct::actual]); + inifile.get_file(), + inifile.get_category(), + m_search); } - else if (main_filters::actual == machine_filter::YEAR) + else { - filtered = string_format(_("%1$s (%2$s) - "), - machine_filter::display_name(main_filters::actual), - c_year::ui[c_year::actual]); + auto const it(main_filters::filters.find(main_filters::actual)); + char const *const filter((main_filters::filters.end() != it) ? it->second->filter_text() : nullptr); + if (filter) + line1 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->second->display_name(), filter, m_search); + else + line1 = string_format(_("Search: %1$s_"), m_search); } - // display the current typeahead - if (isfavorite()) - line1.clear(); - else - line1 = string_format(_("%1$s Search: %2$s_"), filtered, m_search); - line2.clear(); } diff --git a/src/frontend/mame/ui/selgame.h b/src/frontend/mame/ui/selgame.h index fc8801bbab5..344713188ff 100644 --- a/src/frontend/mame/ui/selgame.h +++ b/src/frontend/mame/ui/selgame.h @@ -69,10 +69,8 @@ private: // internal methods void change_info_pane(int delta); - void build_custom(); void build_category(); void build_available_list(); - void build_list(const char *filter_text = nullptr, int filter = 0, bool bioscheck = false, std::vector vec = {}); bool isfavorite() const; void populate_search(); diff --git a/src/frontend/mame/ui/selsoft.cpp b/src/frontend/mame/ui/selsoft.cpp index c58a6a0fbc8..03a7c69cd7a 100644 --- a/src/frontend/mame/ui/selsoft.cpp +++ b/src/frontend/mame/ui/selsoft.cpp @@ -29,11 +29,6 @@ namespace ui { -static const char *region_lists[] = { "arab", "arg", "asia", "aus", "aut", "bel", "blr", "bra", "can", "chi", "chn", "cze", "den", - "ecu", "esp", "euro", "fin", "fra", "gbr", "ger", "gre", "hkg", "hun", "irl", "isr", - "isv", "ita", "jpn", "kaz", "kor", "lat", "lux", "mex", "ned", "nld", "nor", "nzl", - "pol", "rus", "slo", "spa", "sui", "swe", "tha", "tpe", "tw", "uk", "ukr", "usa" }; - //------------------------------------------------- // compares two items in the software list and // sort them by parent-clone @@ -269,41 +264,28 @@ void menu_select_software::handle() if (check_filter) { m_search.clear(); - auto handler = - [this] (uint16_t &actual, int selection) + + filter_map::const_iterator it(m_filters.find(software_filter::type(l_sw_hover))); + if (m_filters.end() == it) + it = m_filters.emplace(software_filter::type(l_sw_hover), software_filter::create(software_filter::type(l_sw_hover), m_filter_data)).first; + it->second->show_ui( + ui(), + container(), + [this, driver = m_driver] (software_filter &filter) { - actual = selection; - m_filter_type = software_filter::type(l_sw_hover); + software_filter::type const new_type(filter.get_type()); + if (software_filter::CUSTOM == new_type) + { + emu_file file(ui().options().ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS); + if (file.open("custom_", driver->name, "_filter.ini") == osd_file::error::NONE) + { + filter.save_ini(file, 0); + file.close(); + } + } + m_filter_type = new_type; reset(reset_options::SELECT_FIRST); - }; - - using std::placeholders::_1; - switch (l_sw_hover) - { - case software_filter::REGION: - menu::stack_push(ui(), container(), std::vector(m_filter.region.ui), m_filter.region.actual, std::bind(handler, std::ref(m_filter.region.actual), _1)); - break; - case software_filter::YEARS: - menu::stack_push(ui(), container(), std::vector(m_filter.year.ui), m_filter.year.actual, std::bind(handler, std::ref(m_filter.year.actual), _1)); - break; - case software_filter::LIST: - menu::stack_push(ui(), container(), std::vector(m_filter.swlist.description), m_filter.swlist.actual, std::bind(handler, std::ref(m_filter.swlist.actual), _1)); - break; - case software_filter::DEVICE_TYPE: - menu::stack_push(ui(), container(), std::vector(m_filter.type.ui), m_filter.type.actual, std::bind(handler, std::ref(m_filter.type.actual), _1)); - break; - case software_filter::PUBLISHERS: - menu::stack_push(ui(), container(), std::vector(m_filter.publisher.ui), m_filter.publisher.actual, std::bind(handler, std::ref(m_filter.publisher.actual), _1)); - break; - case software_filter::CUSTOM: - m_filter_type = software_filter::type(l_sw_hover); - menu::stack_push(ui(), container(), m_driver, m_filter); - break; - default: - m_filter_type = software_filter::type(l_sw_hover); - reset(reset_options::SELECT_FIRST); - break; - } + }); } } @@ -335,36 +317,11 @@ void menu_select_software::populate(float &customtop, float &custombottom) m_displaylist.clear(); m_tmp.clear(); - switch (m_filter_type) - { - case software_filter::PUBLISHERS: - build_list(m_tmp, m_filter.publisher.ui[m_filter.publisher.actual].c_str()); - break; - - case software_filter::LIST: - build_list(m_tmp, m_filter.swlist.name[m_filter.swlist.actual].c_str()); - break; - - case software_filter::YEARS: - build_list(m_tmp, m_filter.year.ui[m_filter.year.actual].c_str()); - break; - - case software_filter::DEVICE_TYPE: - build_list(m_tmp, m_filter.type.ui[m_filter.type.actual].c_str()); - break; - - case software_filter::REGION: - build_list(m_tmp, m_filter.region.ui[m_filter.region.actual].c_str()); - break; - - case software_filter::CUSTOM: - build_custom(); - break; - - default: - build_list(m_tmp); - break; - } + filter_map::const_iterator const it(m_filters.find(m_filter_type)); + if (m_filters.end() == it) + m_displaylist = m_sortedlist; + else + it->second->apply(std::begin(m_sortedlist), std::end(m_sortedlist), std::back_inserter(m_displaylist)); // iterate over entries for (size_t curitem = 0; curitem < m_displaylist.size(); ++curitem) @@ -419,8 +376,8 @@ void menu_select_software::build_software_list() // iterate thru all software lists for (software_list_device &swlist : software_list_device_iterator(config.root_device())) { - m_filter.swlist.name.push_back(swlist.list_name()); - m_filter.swlist.description.push_back(swlist.description()); + m_filter_data.swlist.name.push_back(swlist.list_name()); + m_filter_data.swlist.description.push_back(swlist.description()); for (const software_info &swinfo : swlist.get_info()) { const software_part &part = swinfo.parts().front(); @@ -443,10 +400,10 @@ void menu_select_software::build_software_list() ui_software_info tmpmatches(swinfo, part, *m_driver, swlist.list_name(), instance_name, type_name); - m_filter.region.set(tmpmatches.longname); - m_filter.publisher.set(tmpmatches.publisher); - m_filter.year.set(tmpmatches.year); - m_filter.type.set(tmpmatches.devicetype); + m_filter_data.region.set(tmpmatches.longname); + m_filter_data.publisher.set(tmpmatches.publisher); + m_filter_data.year.set(tmpmatches.year); + m_filter_data.type.set(tmpmatches.devicetype); m_swinfo.emplace_back(std::move(tmpmatches)); } } @@ -482,7 +439,7 @@ void menu_select_software::build_software_list() std::string searchstr, curpath; const osd::directory::entry *dir; - for (auto & elem : m_filter.swlist.name) + for (auto & elem : m_filter_data.swlist.name) { path_iterator path(machine().options().media_path()); while (path.next(curpath)) @@ -514,10 +471,10 @@ void menu_select_software::build_software_list() // sort array std::stable_sort(m_swinfo.begin() + 1, m_swinfo.end(), compare_software); - std::stable_sort(m_filter.region.ui.begin(), m_filter.region.ui.end()); - std::stable_sort(m_filter.year.ui.begin(), m_filter.year.ui.end()); - std::stable_sort(m_filter.type.ui.begin(), m_filter.type.ui.end()); - std::stable_sort(m_filter.publisher.ui.begin(), m_filter.publisher.ui.end()); + std::stable_sort(m_filter_data.region.ui.begin(), m_filter_data.region.ui.end()); + std::stable_sort(m_filter_data.year.ui.begin(), m_filter_data.year.ui.end()); + std::stable_sort(m_filter_data.type.ui.begin(), m_filter_data.type.ui.end()); + std::stable_sort(m_filter_data.publisher.ui.begin(), m_filter_data.publisher.ui.end()); for (size_t x = 1; x < m_swinfo.size(); ++x) m_sortedlist.push_back(&m_swinfo[x]); @@ -596,253 +553,13 @@ void menu_select_software::load_sw_custom_filters() emu_file file(ui().options().ui_path(), OPEN_FLAG_READ); if (file.open("custom_", m_driver->name, "_filter.ini") == osd_file::error::NONE) { - char buffer[MAX_CHAR_INFO]; - - // get number of filters - file.gets(buffer, MAX_CHAR_INFO); - char *pb = strchr(buffer, '='); - sw_custfltr::numother = atoi(++pb) - 1; - - // get main filter - file.gets(buffer, MAX_CHAR_INFO); - pb = strchr(buffer, '=') + 2; - - for (software_filter::type y = software_filter::FIRST; y < software_filter::COUNT; ++y) - { - char const *const name(software_filter::config_name(y)); - if (!strncmp(pb, name, strlen(name))) - { - sw_custfltr::main = y; - break; - } - } - - for (int x = 1; x <= sw_custfltr::numother; ++x) - { - file.gets(buffer, MAX_CHAR_INFO); - char *cb = strchr(buffer, '=') + 2; - for (software_filter::type y = software_filter::FIRST; y < software_filter::COUNT; ++y) - { - char const *const name(software_filter::config_name(y)); - if (!strncmp(cb, name, strlen(name))) - { - sw_custfltr::other[x] = y; - if (y == software_filter::PUBLISHERS) - { - file.gets(buffer, MAX_CHAR_INFO); - char *ab = strchr(buffer, '=') + 2; - for (size_t z = 0; z < m_filter.publisher.ui.size(); ++z) - if (!strncmp(ab, m_filter.publisher.ui[z].c_str(), m_filter.publisher.ui[z].length())) - sw_custfltr::mnfct[x] = z; - } - else if (y == software_filter::YEARS) - { - file.gets(buffer, MAX_CHAR_INFO); - char *db = strchr(buffer, '=') + 2; - for (size_t z = 0; z < m_filter.year.ui.size(); ++z) - if (!strncmp(db, m_filter.year.ui[z].c_str(), m_filter.year.ui[z].length())) - sw_custfltr::year[x] = z; - } - else if (y == software_filter::LIST) - { - file.gets(buffer, MAX_CHAR_INFO); - char *gb = strchr(buffer, '=') + 2; - for (size_t z = 0; z < m_filter.swlist.name.size(); ++z) - if (!strncmp(gb, m_filter.swlist.name[z].c_str(), m_filter.swlist.name[z].length())) - sw_custfltr::list[x] = z; - } - else if (y == software_filter::DEVICE_TYPE) - { - file.gets(buffer, MAX_CHAR_INFO); - char *fb = strchr(buffer, '=') + 2; - for (size_t z = 0; z < m_filter.type.ui.size(); ++z) - if (!strncmp(fb, m_filter.type.ui[z].c_str(), m_filter.type.ui[z].length())) - sw_custfltr::type[x] = z; - } - else if (y == software_filter::REGION) - { - file.gets(buffer, MAX_CHAR_INFO); - char *eb = strchr(buffer, '=') + 2; - for (size_t z = 0; z < m_filter.region.ui.size(); ++z) - if (!strncmp(eb, m_filter.region.ui[z].c_str(), m_filter.region.ui[z].length())) - sw_custfltr::region[x] = z; - } - } - } - } + software_filter::ptr flt(software_filter::create(file, m_filter_data)); + if (flt) + m_filters.emplace(flt->get_type(), std::move(flt)); file.close(); } } -//------------------------------------------------- -// set software regions -//------------------------------------------------- - -void c_sw_region::set(std::string &str) -{ - std::string name = getname(str); - if (std::find(ui.begin(), ui.end(), name) != ui.end()) - return; - - ui.push_back(name); -} - -std::string c_sw_region::getname(std::string &str) -{ - std::string fullname(str); - strmakelower(fullname); - size_t found = fullname.find("("); - - if (found != std::string::npos) - { - size_t ends = fullname.find_first_not_of("abcdefghijklmnopqrstuvwxyz", found + 1); - std::string temp(fullname.substr(found + 1, ends - found - 1)); - - for (auto & elem : region_lists) - if (temp == elem) - return (str.substr(found + 1, ends - found - 1)); - } - return std::string(""); -} - -//------------------------------------------------- -// set software device type -//------------------------------------------------- - -void c_sw_type::set(std::string &str) -{ - if (std::find(ui.begin(), ui.end(), str) != ui.end()) - return; - - ui.push_back(str); -} - -//------------------------------------------------- -// set software years -//------------------------------------------------- - -void c_sw_year::set(std::string &str) -{ - if (std::find(ui.begin(), ui.end(), str) != ui.end()) - return; - - ui.push_back(str); -} - -//------------------------------------------------- -// set software publishers -//------------------------------------------------- - -void c_sw_publisher::set(std::string &str) -{ - std::string name = getname(str); - if (std::find(ui.begin(), ui.end(), name) != ui.end()) - return; - - ui.push_back(name); -} - -std::string c_sw_publisher::getname(std::string &str) -{ - size_t found = str.find("("); - - if (found != std::string::npos) - return (str.substr(0, found - 1)); - else - return str; -} - -//------------------------------------------------- -// build display list -//------------------------------------------------- -void menu_select_software::build_list(std::vector &s_drivers, const char *filter_text, int filter) -{ - if (s_drivers.empty() && filter == -1) - { - filter = m_filter_type; - s_drivers = m_sortedlist; - } - - // iterate over entries - for (auto & s_driver : s_drivers) - { - switch (filter) - { - case software_filter::PARENTS: - if (s_driver->parentname.empty()) - m_displaylist.push_back(s_driver); - break; - - case software_filter::CLONES: - if (!s_driver->parentname.empty()) - m_displaylist.push_back(s_driver); - break; - - case software_filter::AVAILABLE: - if (s_driver->available) - m_displaylist.push_back(s_driver); - break; - - case software_filter::UNAVAILABLE: - if (!s_driver->available) - m_displaylist.push_back(s_driver); - break; - - case software_filter::SUPPORTED: - if (s_driver->supported == SOFTWARE_SUPPORTED_YES) - m_displaylist.push_back(s_driver); - break; - - case software_filter::PARTIAL_SUPPORTED: - if (s_driver->supported == SOFTWARE_SUPPORTED_PARTIAL) - m_displaylist.push_back(s_driver); - break; - - case software_filter::UNSUPPORTED: - if (s_driver->supported == SOFTWARE_SUPPORTED_NO) - m_displaylist.push_back(s_driver); - break; - - case software_filter::REGION: - { - std::string name = m_filter.region.getname(s_driver->longname); - - if(!name.empty() && name == filter_text) - m_displaylist.push_back(s_driver); - } - break; - - case software_filter::PUBLISHERS: - { - std::string name = m_filter.publisher.getname(s_driver->publisher); - - if(!name.empty() && name == filter_text) - m_displaylist.push_back(s_driver); - } - break; - - case software_filter::YEARS: - if(s_driver->year == filter_text) - m_displaylist.push_back(s_driver); - break; - - case software_filter::LIST: - if(s_driver->listname == filter_text) - m_displaylist.push_back(s_driver); - break; - - case software_filter::DEVICE_TYPE: - if(s_driver->devicetype == filter_text) - m_displaylist.push_back(s_driver); - break; - - default: - m_displaylist.push_back(s_driver); - break; - } - } -} - //------------------------------------------------- // find approximate matches //------------------------------------------------- @@ -881,46 +598,6 @@ void menu_select_software::find_matches(const char *str, int count) (index < count) ? m_searchlist[index] = nullptr : m_searchlist[count] = nullptr; } -//------------------------------------------------- -// build custom display list -//------------------------------------------------- - -void menu_select_software::build_custom() -{ - std::vector s_drivers; - - build_list(m_sortedlist, nullptr, sw_custfltr::main); - - for (int count = 1; count <= sw_custfltr::numother; ++count) - { - software_filter::type filter = sw_custfltr::other[count]; - s_drivers = m_displaylist; - m_displaylist.clear(); - - switch (filter) - { - case software_filter::YEARS: - build_list(s_drivers, m_filter.year.ui[sw_custfltr::year[count]].c_str(), filter); - break; - case software_filter::LIST: - build_list(s_drivers, m_filter.swlist.name[sw_custfltr::list[count]].c_str(), filter); - break; - case software_filter::DEVICE_TYPE: - build_list(s_drivers, m_filter.type.ui[sw_custfltr::type[count]].c_str(), filter); - break; - case software_filter::PUBLISHERS: - build_list(s_drivers, m_filter.publisher.ui[sw_custfltr::mnfct[count]].c_str(), filter); - break; - case software_filter::REGION: - build_list(s_drivers, m_filter.region.ui[sw_custfltr::region[count]].c_str(), filter); - break; - default: - build_list(s_drivers, nullptr, filter); - break; - } - } -} - //------------------------------------------------- // draw left box //------------------------------------------------- @@ -929,6 +606,9 @@ float menu_select_software::draw_left_panel(float x1, float y1, float x2, float { if (ui_globals::panels_status == SHOW_PANELS || ui_globals::panels_status == HIDE_RIGHT_PANEL) { + filter_map::const_iterator active_filter(m_filters.find(m_filter_type)); + if (m_filters.end() == active_filter) + active_filter = m_filters.emplace(m_filter_type, software_filter::create(m_filter_type, m_filter_data)).first; float origy1 = y1; float origy2 = y2; float text_size = 0.75f; @@ -946,7 +626,9 @@ float menu_select_software::draw_left_panel(float x1, float y1, float x2, float line_height = l_height * text_size; } - float text_sign = ui().get_string_width("_# ", text_size); + std::string tmp("_# "); + convert_command_glyph(tmp); + float text_sign = ui().get_string_width(tmp.c_str(), text_size); for (software_filter::type x = software_filter::FIRST; x < software_filter::COUNT; ++x) { float total_width; @@ -971,7 +653,7 @@ float menu_select_software::draw_left_panel(float x1, float y1, float x2, float for (software_filter::type filter = software_filter::FIRST; filter < software_filter::COUNT; ++filter) { - std::string str(software_filter::display_name(filter)); + std::string const str(active_filter->second->adorned_display_name(filter)); rgb_t bgcolor = UI_TEXT_BG_COLOR; rgb_t fgcolor = UI_TEXT_COLOR; @@ -994,35 +676,7 @@ float menu_select_software::draw_left_panel(float x1, float y1, float x2, float hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1)); } - float x1t = x1 + text_sign; - if (m_filter_type == software_filter::CUSTOM) - { - if (filter == sw_custfltr::main) - { - str = std::string("@custom1 ") + str; - x1t -= text_sign; - } - else - { - for (int count = 1; count <= sw_custfltr::numother; ++count) - { - int cfilter = sw_custfltr::other[count]; - if (cfilter == filter) - { - str = string_format("@custom%d %s", count + 1, str); - x1t -= text_sign; - break; - } - } - } - convert_command_glyph(str); - } - else if (filter == m_filter_type) - { - str = std::string("_> ") + str; - x1t -= text_sign; - convert_command_glyph(str); - } + float const x1t = x1 + ((str == software_filter::display_name(filter)) ? text_sign : 0.0f); ui().draw_text_full(container(), str.c_str(), x1t, y1, x2 - x1, ui::text_layout::LEFT, ui::text_layout::NEVER, mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, text_size); @@ -1097,19 +751,12 @@ void menu_select_software::make_topbox_text(std::string &line0, std::string &lin line0 = string_format(_("%1$s %2$s ( %3$d / %4$d software packages )"), emulator_info::get_appname(), bare_build_version, vis_item, m_swinfo.size() - 1); line1 = string_format(_("Driver: \"%1$s\" software list "), m_driver->type.fullname()); - std::string filtered; - if (m_filter_type == software_filter::REGION && m_filter.region.ui.size()) - filtered = string_format(_("Region: %1$s -"), m_filter.region.ui[m_filter.region.actual]); - else if (m_filter_type == software_filter::PUBLISHERS) - filtered = string_format(_("Publisher: %1$s -"), m_filter.publisher.ui[m_filter.publisher.actual]); - else if (m_filter_type == software_filter::YEARS) - filtered = string_format(_("Year: %1$s -"), m_filter.year.ui[m_filter.year.actual]); - else if (m_filter_type == software_filter::LIST) - filtered = string_format(_("Software List: %1$s -"), m_filter.swlist.description[m_filter.swlist.actual]); - else if (m_filter_type == software_filter::DEVICE_TYPE) - filtered = string_format(_("Device type: %1$s -"), m_filter.type.ui[m_filter.type.actual]); - - line2 = string_format(_("%s Search: %s_"), filtered, m_search); + filter_map::const_iterator const it(m_filters.find(m_filter_type)); + char const *const filter((m_filters.end() != it) ? it->second->filter_text() : nullptr); + if (filter) + line2 = string_format(_("%1$s: %2$s - Search: %3$s_"), it->second->display_name(), filter, m_search); + else + line2 = string_format(_("Search: %1$s_"), m_search); } diff --git a/src/frontend/mame/ui/selsoft.h b/src/frontend/mame/ui/selsoft.h index 60b6bbeda02..7e92815e08b 100644 --- a/src/frontend/mame/ui/selsoft.h +++ b/src/frontend/mame/ui/selsoft.h @@ -14,6 +14,8 @@ #include "ui/custmenu.h" #include "ui/selmenu.h" +#include "ui/utils.h" + namespace ui { @@ -30,10 +32,13 @@ protected: private: enum { VISIBLE_GAMES_IN_SEARCH = 200 }; + typedef std::map filter_map; + std::string m_search; const game_driver *m_driver; bool m_has_empty_start; - s_filter m_filter; + s_filter m_filter_data; + filter_map m_filters; software_filter::type m_filter_type; int highlight; @@ -56,8 +61,6 @@ private: std::vector m_swinfo; void build_software_list(); - void build_list(std::vector &vec, const char *filter_text = nullptr, int filter = -1); - void build_custom(); void find_matches(const char *str, int count); void load_sw_custom_filters(); diff --git a/src/frontend/mame/ui/utils.cpp b/src/frontend/mame/ui/utils.cpp index 31122ffe256..9d3274b78f0 100644 --- a/src/frontend/mame/ui/utils.cpp +++ b/src/frontend/mame/ui/utils.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Maurizio Petrarota +// copyright-holders:Maurizio Petrarota, Vas Crabb /*************************************************************************** ui/utils.cpp @@ -11,10 +11,18 @@ #include "emu.h" #include "ui/utils.h" +#include "ui/custmenu.h" // FIXME: get s_filter out of here +#include "ui/selector.h" + #include "language.h" +#include "drivenum.h" +#include "rendfont.h" #include "softlist.h" +#include +#include + namespace ui { @@ -33,8 +41,8 @@ constexpr char const *machine_filter_names[machine_filter::COUNT] = { __("BIOS"), __("Parents"), __("Clones"), - __("Manufacturers"), - __("Release Years"), + __("Manufacturer"), + __("Year"), __("Save Supported"), __("Save Unsupported"), __("CHD Required"), @@ -49,8 +57,8 @@ constexpr char const *software_filter_names[software_filter::COUNT] = { __("Unavailable"), __("Parents"), __("Clones"), - __("Release Years"), - __("Publishers"), + __("Year"), + __("Publisher"), __("Supported"), __("Partially Supported"), __("Unsupported"), @@ -59,9 +67,1090 @@ constexpr char const *software_filter_names[software_filter::COUNT] = { __("Software List"), __("Custom Filter") }; + + +//------------------------------------------------- +// base implementation for simple filters +//------------------------------------------------- + +template +class simple_filter_impl_base : public Base +{ +public: + virtual char const *config_name() const override { return Base::config_name(Type); } + virtual char const *display_name() const override { return Base::display_name(Type); } + virtual char const *filter_text() const override { return nullptr; } + + virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function &&handler) override + { + handler(*this); + } + + virtual bool wants_adjuster() const override { return false; } + virtual char const *adjust_text() const override { return filter_text(); } + virtual uint32_t arrow_flags() const override { return 0; } + virtual bool adjust_left() override { return false; } + virtual bool adjust_right() override { return false; } + + virtual void save_ini(emu_file &file, unsigned indent) const override + { + file.puts(util::string_format("%2$*1$s%3$s = 1\n", 2 * indent, "", config_name()).c_str()); + } + + virtual typename Base::type get_type() const override { return Type; } + + virtual std::string adorned_display_name(typename Base::type n) const override + { + std::string result; + if (Type == n) + { + result = "_> "; + convert_command_glyph(result); + } + result.append(Base::display_name(n)); + return result; + } + + using Base::config_name; + using Base::display_name; + +protected: + simple_filter_impl_base() { } +}; + + + +//------------------------------------------------- +// base implementation for single-choice filters +//------------------------------------------------- + +template +class choice_filter_impl_base : public simple_filter_impl_base +{ +public: + virtual char const *filter_text() const override { return selection_valid() ? selection_text().c_str() : nullptr; } + + virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function &&handler) override + { + if (m_choices.empty()) + { + handler(*this); + } + else + { + menu::stack_push( + mui, container, + std::vector(m_choices), // ouch, a vector copy! + m_selection, + [this, cb = std::move(handler)] (int selection) + { + m_selection = selection; + cb(*this); + }); + } + } + + virtual bool wants_adjuster() const override { return have_choices(); } + + virtual uint32_t arrow_flags() const override + { + return ((have_choices() && m_selection) ? menu::FLAG_LEFT_ARROW : 0) | ((m_choices.size() > (m_selection + 1)) ? menu::FLAG_RIGHT_ARROW : 0); + } + + virtual bool adjust_left() override + { + if (!have_choices() || !m_selection) + return false; + m_selection = (std::min)(m_selection - 1, unsigned(m_choices.size() - 1)); + return true; + } + + virtual bool adjust_right() override + { + if (m_choices.size() <= (m_selection + 1)) + return false; + ++m_selection; + return true; + } + + virtual void save_ini(emu_file &file, unsigned indent) const override + { + char const *const text(filter_text()); + file.puts(util::string_format("%2$*1$s%3$s = %4$s\n", 2 * indent, "", this->config_name(), text ? text : "").c_str()); + } + +protected: + choice_filter_impl_base(std::vector const &choices, char const *value) : + m_choices(choices), + m_selection(0U) + { + if (value) + { + std::vector::const_iterator const found(std::find(choices.begin(), choices.end(), value)); + if (choices.end() != found) + m_selection = std::distance(choices.begin(), found); + } + } + + bool have_choices() const { return !m_choices.empty(); } + bool selection_valid() const { return m_choices.size() > m_selection; } + std::string const &selection_text() const { return m_choices[m_selection]; } + +private: + std::vector const &m_choices; + unsigned m_selection; +}; + + + +//------------------------------------------------- +// base implementation for composite filters +//------------------------------------------------- + +template +class composite_filter_impl_base : public simple_filter_impl_base +{ +public: + virtual bool wants_adjuster() const override { return true; } + virtual char const *adjust_text() const override { return _(""); } + + virtual void save_ini(emu_file &file, unsigned indent) const override + { + auto const tail(std::find_if(std::begin(m_filters), std::end(m_filters), [] (typename Base::ptr const &flt) { return !flt; })); + file.puts(util::string_format("%2$*1$s%3$s = %4$d\n", 2 * indent, "", this->config_name(), std::distance(std::begin(m_filters), tail)).c_str()); + for (auto it = std::begin(m_filters); tail != it; ++it) + (*it)->save_ini(file, indent + 1); + } + + virtual std::string adorned_display_name(typename Base::type n) const override + { + std::string result; + if (Type == n) + { + result = "_> "; + convert_command_glyph(result); + } + else + { + for (unsigned i = 0; (MAX > i) && m_filters[i]; ++i) + { + if (m_filters[i]->get_type() == n) + { + result = util::string_format("@custom%u ", i + 1); + convert_command_glyph(result); + break; + } + } + } + result.append(Base::display_name(n)); + return result; + } + + virtual bool apply(typename Base::entry_type const &info) const override + { + std::bitset inclusions, included; + for (typename Base::ptr const &flt : m_filters) + { + if (!flt) + break; + + typename Base::type const t(flt->get_type()); + bool const r(flt->apply(info)); + if (Impl::is_inclusion(t)) + { + inclusions.set(t); + if (r) + included.set(t); + } + else if (!r) + { + return false; + } + } + return inclusions == included; + } + + virtual void show_ui(mame_ui_manager &mui, render_container &container, std::function &&handler) override; + +protected: + composite_filter_impl_base() { } + + void populate(char const *value, emu_file *file, unsigned indent) + { + // try to load filters from a file + if (value && file) + { + unsigned const cnt(unsigned((std::max)(std::min(int(MAX), std::atoi(value)), 0))); + for (unsigned i = 0; cnt > i; ++i) + { + typename Base::ptr flt(static_cast(*this).create(*file, indent + 1)); + if (!flt || !check_type(i, flt->get_type())) + break; + m_filters[i] = std::move(flt); + } + } + + // instantiate first allowed filter type if we're still empty + for (typename Base::type t = Base::FIRST; (Base::COUNT > t) && !m_filters[0]; ++t) + { + if (Impl::type_allowed(0, t)) + m_filters[0] = static_cast(*this).create(t); + } + } + +private: + static constexpr unsigned MAX = 8; + + class menu_configure : public menu + { + public: + menu_configure( + mame_ui_manager &mui, + render_container &container, + Impl &parent, + std::function &&handler) : + menu(mui, container), + m_parent(parent), + m_handler(std::move(handler)), + m_added(false) + { + } + + virtual ~menu_configure() override { m_handler(m_parent); } + + protected: + virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override + { + char const *const text[] = { _("Select custom filters:") }; + draw_text_box( + std::begin(text), std::end(text), + x, x2, y - top, y - UI_BOX_TB_BORDER, + ui::text_layout::CENTER, ui::text_layout::NEVER, false, + UI_TEXT_COLOR, UI_GREEN_COLOR, 1.0f); + } + + private: + enum : uintptr_t + { + FILTER_FIRST = 1, + FILTER_LAST = FILTER_FIRST + MAX - 1, + ADJUST_FIRST, + ADJUST_LAST = ADJUST_FIRST + MAX - 1, + REMOVE_FILTER, + ADD_FILTER + }; + + virtual void populate(float &customtop, float &custombottom) override; + virtual void handle() override; + + bool set_filter_type(unsigned pos, typename Base::type n) + { + if (!m_parent.m_filters[pos] || (m_parent.m_filters[pos]->get_type())) + { + save_filter(pos); + retrieve_filter(pos, n); + return true; + } + else + { + return false; + } + } + + bool append_filter() + { + unsigned pos = 0; + while (m_parent.m_filters[pos]) + { + if (MAX <= ++pos) + return false; + } + for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate) + { + if (m_parent.check_type(pos, candidate)) + { + set_filter_type(pos, candidate); + return true; + } + } + return false; + } + + bool drop_last_filter() + { + for (unsigned i = 2; MAX >= i; ++i) + { + if ((MAX <= i) || !m_parent.m_filters[i]) + { + save_filter(i - 1); + m_parent.m_filters[i - 1].reset(); + return true; + } + } + return false; + } + + void save_filter(unsigned pos) + { + typename Base::ptr &flt(m_parent.m_filters[pos]); + if (flt && flt->wants_adjuster()) + m_saved_filters[pos][flt->get_type()] = std::move(flt); + } + + void retrieve_filter(unsigned pos, typename Base::type n) + { + typename Base::ptr &flt(m_parent.m_filters[pos]); + auto const found(m_saved_filters[pos].find(n)); + if (m_saved_filters[pos].end() != found) + { + flt = std::move(found->second); + m_saved_filters[pos].erase(found); + } + else + { + flt = m_parent.create(n); + } + } + + uint32_t get_arrow_flags(unsigned pos) + { + uint32_t result(0); + typename Base::type const current(m_parent.m_filters[pos]->get_type()); + + // look for a lower type that's allowed and isn't contradictory + typename Base::type prev(current); + while ((Base::FIRST < prev) && !(FLAG_LEFT_ARROW & result)) + { + if (m_parent.check_type(pos, --prev)) + result |= FLAG_LEFT_ARROW; + } + + // look for a higher type that's allowed and isn't contradictory + typename Base::type next(current); + while ((Base::LAST > next) && !(FLAG_RIGHT_ARROW & result)) + { + if (m_parent.check_type(pos, ++next)) + result |= FLAG_RIGHT_ARROW; + } + + return result; + } + + Impl &m_parent; + std::map m_saved_filters[MAX]; + std::function m_handler; + bool m_added; + }; + + bool check_type(unsigned pos, typename Base::type candidate) + { + if (!Impl::type_allowed(pos, candidate)) + return false; + unsigned j = 0; + while ((MAX > j) && m_filters[j] && ((pos == j) || !Impl::types_contradictory(m_filters[j]->get_type(), candidate))) + ++j; + return (MAX <= j) || !m_filters[j]; + }; + + typename Base::ptr m_filters[MAX]; +}; + +template +void composite_filter_impl_base::show_ui( + mame_ui_manager &mui, + render_container &container, + std::function &&handler) +{ + menu::stack_push(mui, container, static_cast(*this), std::move(handler)); +} + + +template +void composite_filter_impl_base::menu_configure::populate(float &customtop, float &custombottom) +{ + // add items for each active filter + unsigned i = 0; + for (i = 0; (MAX > i) && m_parent.m_filters[i]; ++i) + { + item_append(util::string_format("Filter %u", i + 1), m_parent.m_filters[i]->display_name(), get_arrow_flags(i), (void *)(FILTER_FIRST + i)); + if (m_added) + selected = item.size() - 2; + if (m_parent.m_filters[i]->wants_adjuster()) + { + std::string name("^!"); + convert_command_glyph(name); + item_append(name, m_parent.m_filters[i]->adjust_text(), m_parent.m_filters[i]->arrow_flags(), (void *)(ADJUST_FIRST + i)); + } + item_append(menu_item_type::SEPARATOR); + } + m_added = false; + + // add remove/add handlers + if (1 < i) + item_append(_("Remove last filter"), "", 0, (void *)REMOVE_FILTER); + if (MAX > i) + item_append(_("Add filter"), "", 0, (void *)ADD_FILTER); + item_append(menu_item_type::SEPARATOR); + + // leave space for heading + customtop = ui().get_line_height() + 3.0f * UI_BOX_TB_BORDER; +} + +template +void composite_filter_impl_base::menu_configure::handle() +{ + const event *menu_event = process(PROCESS_LR_REPEAT); + if (menu_event && menu_event->itemref) + { + m_added = false; + bool changed(false); + uintptr_t const ref(reinterpret_cast(menu_event->itemref)); + switch (menu_event->iptkey) + { + case IPT_UI_LEFT: + case IPT_UI_RIGHT: + if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref)) + { + // change filter type + unsigned const pos(ref - FILTER_FIRST); + typename Base::type const current(m_parent.m_filters[pos]->get_type()); + if (IPT_UI_LEFT == menu_event->iptkey) + { + typename Base::type n(current); + while ((Base::FIRST < n) && !changed) + { + if (m_parent.check_type(pos, --n)) + changed = set_filter_type(pos, n); + } + } + else + { + typename Base::type n(current); + while ((Base::LAST > n) && !changed) + { + if (m_parent.check_type(pos, ++n)) + changed = set_filter_type(pos, n); + } + } + } + else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref)) + { + // change filter value + Base &pos(*m_parent.m_filters[ref - ADJUST_FIRST]); + changed = (IPT_UI_LEFT == menu_event->iptkey) ? pos.adjust_left() : pos.adjust_right(); + } + break; + + case IPT_UI_SELECT: + if ((FILTER_FIRST <= ref) && (FILTER_LAST >= ref)) + { + // show selector with non-contradictory types + std::vector types; + std::vector names; + types.reserve(Base::COUNT); + names.reserve(Base::COUNT); + int sel(-1); + unsigned const pos(ref - FILTER_FIRST); + typename Base::type const current(m_parent.m_filters[pos]->get_type()); + for (typename Base::type candidate = Base::FIRST; Base::COUNT > candidate; ++candidate) + { + if (Impl::type_allowed(pos, candidate)) + { + if (current == candidate) + sel = types.size(); + unsigned i = 0; + while ((MAX > i) && m_parent.m_filters[i] && ((pos == i) || !Impl::types_contradictory(m_parent.m_filters[i]->get_type(), candidate))) + ++i; + if ((MAX <= i) || !m_parent.m_filters[i]) + { + types.emplace_back(candidate); + names.emplace_back(Base::display_name(candidate)); + } + } + } + menu::stack_push( + ui(), + container(), + std::move(names), + sel, + [this, pos, t = std::move(types)] (int selection) + { + if (set_filter_type(pos, t[selection])) + reset(reset_options::REMEMBER_REF); + }); + } + else if ((ADJUST_FIRST <= ref) && (ADJUST_LAST >= ref)) + { + // show selected filter's UI + m_parent.m_filters[ref - ADJUST_FIRST]->show_ui(ui(), container(), [this] (Base &filter) { reset(reset_options::REMEMBER_REF); }); + } + else if (REMOVE_FILTER == ref) + { + changed = drop_last_filter(); + } + else if (ADD_FILTER == ref) + { + m_added = append_filter(); + } + break; + } + + // rebuild if anything changed + if (changed) + reset(reset_options::REMEMBER_REF); + else if (m_added) + reset(reset_options::SELECT_FIRST); + } +} + + + +//------------------------------------------------- +// invertable machine filters +//------------------------------------------------- + +template +class working_machine_filter_impl : public simple_filter_impl_base +{ +public: + working_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override { return !(driver.flags & machine_flags::NOT_WORKING); } +}; + + +template +class mechanical_machine_filter_impl : public simple_filter_impl_base +{ +public: + mechanical_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::MECHANICAL; } +}; + + +template +class parents_machine_filter_impl : public simple_filter_impl_base +{ +public: + parents_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override + { + bool const have_parent(strcmp(driver.parent, "0")); + auto const parent_idx(have_parent ? driver_list::find(driver.parent) : -1); + return !have_parent || (0 > parent_idx) || (driver_list::driver(parent_idx).flags & machine_flags::IS_BIOS_ROOT); + } +}; + + +template +class chd_machine_filter_impl : public simple_filter_impl_base +{ +public: + chd_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override + { + for (tiny_rom_entry const *rom = driver.rom; rom && rom->name; ++rom) + { + // FIXME: can't use the convenience macros tiny ROM entries + if ((ROMENTRYTYPE_REGION == (rom->flags & ROMENTRY_TYPEMASK)) && (ROMREGION_DATATYPEDISK == (rom->flags & ROMREGION_DATATYPEMASK))) + return true; + } + return false; + } +}; + + +template +class save_machine_filter_impl : public simple_filter_impl_base +{ +public: + save_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::SUPPORTS_SAVE; } +}; + + +template +class vertical_machine_filter_impl : public simple_filter_impl_base +{ +public: + vertical_machine_filter_impl(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::SWAP_XY; } +}; + + + +//------------------------------------------------- +// concrete machine filters +//------------------------------------------------- + +class bios_machine_filter : public simple_filter_impl_base +{ +public: + bios_machine_filter(char const *value, emu_file *file, unsigned indent) { } + + virtual bool apply(game_driver const &driver) const override { return driver.flags & machine_flags::IS_BIOS_ROOT; } +}; + + +class manufacturer_machine_filter : public choice_filter_impl_base +{ +public: + manufacturer_machine_filter(char const *value, emu_file *file, unsigned indent) : + choice_filter_impl_base(c_mnfct::ui, value) + { + } + + virtual bool apply(game_driver const &driver) const override + { + if (!have_choices()) + return true; + else if (!selection_valid()) + return false; + + std::string const name(c_mnfct::getname(driver.manufacturer)); + return !name.empty() && (selection_text() == name); + } +}; + + +class year_machine_filter : public choice_filter_impl_base +{ +public: + year_machine_filter(char const *value, emu_file *file, unsigned indent) : + choice_filter_impl_base(c_year::ui, value) + { + } + + virtual bool apply(game_driver const &driver) const override { return !have_choices() || (selection_valid() && (selection_text() == driver.year)); } +}; + + + +//------------------------------------------------- +// complementary machine filters +//------------------------------------------------- + +template