summaryrefslogtreecommitdiffstatshomepage
path: root/src/frontend/mame/ui/filesel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/mame/ui/filesel.cpp')
-rw-r--r--src/frontend/mame/ui/filesel.cpp446
1 files changed, 287 insertions, 159 deletions
diff --git a/src/frontend/mame/ui/filesel.cpp b/src/frontend/mame/ui/filesel.cpp
index 19567b33659..5f4dddc65fc 100644
--- a/src/frontend/mame/ui/filesel.cpp
+++ b/src/frontend/mame/ui/filesel.cpp
@@ -12,19 +12,24 @@
***************************************************************************/
#include "emu.h"
-
#include "ui/filesel.h"
+
#include "ui/ui.h"
#include "ui/utils.h"
#include "imagedev/floppy.h"
-#include "zippath.h"
+#include "uiinput.h"
+
+#include "util/corestr.h"
+#include "util/zippath.h"
#include <cstring>
#include <locale>
+
namespace ui {
+
/***************************************************************************
CONSTANTS
***************************************************************************/
@@ -45,17 +50,29 @@ namespace ui {
// ctor
//-------------------------------------------------
-menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &container, device_image_interface *image, std::string &current_directory, std::string &current_file, bool has_empty, bool has_softlist, bool has_create, menu_file_selector::result &result)
+menu_file_selector::menu_file_selector(
+ mame_ui_manager &mui,
+ render_container &container,
+ device_image_interface *image,
+ std::string_view directory,
+ std::string_view file,
+ bool has_empty,
+ bool has_softlist,
+ bool has_create,
+ handler_function &&handler)
: menu(mui, container)
+ , m_handler(std::move(handler))
, m_image(image)
- , m_current_directory(current_directory)
- , m_current_file(current_file)
+ , m_current_directory(directory)
+ , m_current_file(file)
+ , m_result(result::INVALID)
, m_has_empty(has_empty)
, m_has_softlist(has_softlist)
, m_has_create(has_create)
- , m_result(result)
+ , m_clicked_directory(std::string::npos, std::string::npos)
{
(void)m_image;
+ set_process_flags(PROCESS_IGNOREPAUSE);
}
@@ -65,6 +82,23 @@ menu_file_selector::menu_file_selector(mame_ui_manager &mui, render_container &c
menu_file_selector::~menu_file_selector()
{
+ if (m_handler)
+ m_handler(m_result, std::move(m_current_directory), std::move(m_current_file));
+}
+
+
+//-------------------------------------------------
+// recompute_metrics - recompute metrics
+//-------------------------------------------------
+
+void menu_file_selector::recompute_metrics(uint32_t width, uint32_t height, float aspect)
+{
+ menu::recompute_metrics(width, height, aspect);
+
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+
+ set_custom_space(line_height() + 3.0F * tb_border(), 0.0F);
}
@@ -72,102 +106,131 @@ menu_file_selector::~menu_file_selector()
// custom_render - perform our special rendering
//-------------------------------------------------
-void menu_file_selector::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+void menu_file_selector::custom_render(uint32_t flags, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
{
// lay out extra text
- auto layout = ui().create_layout(container());
- layout.add_text(m_current_directory.c_str());
+ if (!m_path_layout)
+ {
+ m_path_layout.emplace(create_layout());
+ m_path_layout->add_text(m_current_directory);
+ }
+ else
+ {
+ rgb_t const fgcolor = ui().colors().text_color();
+ rgb_t const bgcolor = rgb_t::transparent();
+ m_path_layout->restyle(0, m_current_directory.length(), &fgcolor, &bgcolor);
+ }
// position this extra text
- float x1, y1, x2, y2;
- extra_text_position(origx1, origx2, origy1, top, layout, -1, x1, y1, x2, y2);
+ float x2, y2;
+ extra_text_position(origx1, origx2, origy1, top, *m_path_layout, -1, m_path_position.first, m_path_position.second, x2, y2);
// draw a box
- ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
+ ui().draw_outlined_box(container(), m_path_position.first, m_path_position.second, x2, y2, ui().colors().background_color());
// take off the borders
- x1 += ui().box_lr_border() * machine().render().ui_aspect(&container());
- y1 += ui().box_tb_border();
+ m_path_position.first += lr_border();
+ m_path_position.second += tb_border();
- size_t hit_start = 0, hit_span = 0;
- if (is_mouse_hit()
- && layout.hit_test(get_mouse_x() - x1, get_mouse_y() - y1, hit_start, hit_span)
- && m_current_directory.substr(hit_start, hit_span) != PATH_SEPARATOR)
+ if (m_clicked_directory.second > m_clicked_directory.first)
{
- // we're hovering over a directory! highlight it
- auto target_dir_start = m_current_directory.rfind(PATH_SEPARATOR, hit_start) + 1;
- auto target_dir_end = m_current_directory.find(PATH_SEPARATOR, hit_start + hit_span);
- m_hover_directory = m_current_directory.substr(0, target_dir_end + strlen(PATH_SEPARATOR));
-
- // highlight the text in question
- rgb_t fgcolor = ui().colors().mouseover_color();
- rgb_t bgcolor = ui().colors().mouseover_bg_color();
- layout.restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor);
+ // see if it's still over the clicked path component
+ auto const [x, y] = pointer_location();
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ if ((start >= m_clicked_directory.first) && ((start + span) <= m_clicked_directory.second))
+ {
+ rgb_t const fgcolor = ui().colors().selected_color();
+ rgb_t const bgcolor = ui().colors().selected_bg_color();
+ m_path_layout->restyle(m_clicked_directory.first, m_clicked_directory.second - m_clicked_directory.first, &fgcolor, &bgcolor);
+ }
+ }
}
- else
+ else if (pointer_idle())
{
- // we are not hovering over anything
- m_hover_directory.clear();
+ // see if it's hovering over a path component
+ auto const [x, y] = pointer_location();
+ auto const [target_dir_start, target_dir_end] = get_directory_range(x, y);
+ if (target_dir_end > target_dir_start)
+ {
+ rgb_t const fgcolor = ui().colors().mouseover_color();
+ rgb_t const bgcolor = ui().colors().mouseover_bg_color();
+ m_path_layout->restyle(target_dir_start, target_dir_end - target_dir_start, &fgcolor, &bgcolor);
+ }
}
// draw the text within it
- layout.emit(container(), x1, y1);
+ m_path_layout->emit(container(), m_path_position.first, m_path_position.second);
}
//-------------------------------------------------
-// custom_mouse_down - perform our special mouse down
+// custom_pointer_updated - perform our special
+// pointer handling
//-------------------------------------------------
-bool menu_file_selector::custom_mouse_down()
+std::tuple<int, bool, bool> menu_file_selector::custom_pointer_updated(bool changed, ui_event const &uievt)
{
- if (m_hover_directory.length() > 0)
+ // track pointer after clicking a path component
+ if (m_clicked_directory.second > m_clicked_directory.first)
{
- m_current_directory = m_hover_directory;
- reset(reset_options::SELECT_FIRST);
- return true;
+ if (ui_event::type::POINTER_ABORT == uievt.event_type)
+ {
+ // abort always cancels
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else if (uievt.pointer_released & 0x01)
+ {
+ // releasing the primary button - check for dragging out
+ auto const [x, y] = pointer_location();
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ // abuse IPT_CUSTOM to change to the clicked directory
+ if ((start >= m_clicked_directory.first) && ((start + span) <= m_clicked_directory.second))
+ return std::make_tuple(IPT_CUSTOM, false, true);
+ }
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else if (uievt.pointer_buttons & ~u32(1))
+ {
+ // pressing more buttons cancels
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ return std::make_tuple(IPT_INVALID, false, true);
+ }
+ else
+ {
+ // keep tracking the pointer
+ return std::make_tuple(IPT_INVALID, true, false);
+ }
}
- return false;
+ // check for clicks if we have up-to-date content on-screen
+ if (m_path_layout && pointer_idle() && (uievt.pointer_buttons & 0x01) && !(uievt.pointer_buttons & ~u32(0x01)))
+ {
+ auto const [x, y] = pointer_location();
+ auto const [target_dir_start, target_dir_end] = get_directory_range(x, y);
+ if (target_dir_end > target_dir_start)
+ {
+ m_clicked_directory = std::make_pair(target_dir_start, target_dir_end);
+ return std::make_tuple(IPT_INVALID, true, true);
+ }
+ }
+
+ return std::make_tuple(IPT_INVALID, false, false);
}
//-------------------------------------------------
-// compare_file_selector_entries - sorting proc
-// for file selector entries
+// menu_activated - menu has gained focus
//-------------------------------------------------
-int menu_file_selector::compare_entries(const file_selector_entry *e1, const file_selector_entry *e2)
+void menu_file_selector::menu_activated()
{
- int result;
- const char *e1_basename = e1->basename.c_str();
- const char *e2_basename = e2->basename.c_str();
-
- if (e1->type < e2->type)
- {
- result = -1;
- }
- else if (e1->type > e2->type)
- {
- result = 1;
- }
- else
- {
- result = core_stricmp(e1_basename, e2_basename);
- if (result == 0)
- {
- result = strcmp(e1_basename, e2_basename);
- if (result == 0)
- {
- if (e1 < e2)
- result = -1;
- else if (e1 > e2)
- result = 1;
- }
- }
- }
-
- return result;
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
}
@@ -202,7 +265,7 @@ menu_file_selector::file_selector_entry &menu_file_selector::append_entry(
entry.fullpath = std::move(entry_fullpath);
// find the end of the list
- return *m_entrylist.emplace(m_entrylist.end(), std::move(entry));
+ return m_entrylist.emplace_back(std::move(entry));
}
@@ -310,18 +373,20 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
case SELECTOR_ENTRY_TYPE_DRIVE:
case SELECTOR_ENTRY_TYPE_DIRECTORY:
- // drive/directory - first check the path
{
+ // drive/directory - first check the path
util::zippath_directory::ptr dir;
- osd_file::error const err = util::zippath_directory::open(entry.fullpath, dir);
- if (err != osd_file::error::NONE)
+ std::error_condition const err = util::zippath_directory::open(entry.fullpath, dir);
+ if (err)
{
// this path is problematic; present the user with an error and bail
ui().popup_time(1, _("Error accessing %s"), entry.fullpath);
break;
}
}
- m_current_directory.assign(entry.fullpath);
+ m_current_directory = entry.fullpath;
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
reset(reset_options::SELECT_FIRST);
break;
@@ -336,74 +401,98 @@ void menu_file_selector::select_item(const file_selector_entry &entry)
//-------------------------------------------------
-// type_search_char
+// update_search
//-------------------------------------------------
-void menu_file_selector::type_search_char(char32_t ch)
+void menu_file_selector::update_search()
{
- std::string const current(m_filename);
- if (input_character(m_filename, ch, uchar_is_printable))
- {
- ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename.c_str());
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
- file_selector_entry const *const cur_selected(reinterpret_cast<file_selector_entry const *>(get_selection_ref()));
+ file_selector_entry const *const cur_selected(reinterpret_cast<file_selector_entry const *>(get_selection_ref()));
- // if it's a perfect match for the current selection, don't move it
- if (!cur_selected || core_strnicmp(cur_selected->basename.c_str(), m_filename.c_str(), m_filename.size()))
+ // if it's a perfect match for the current selection, don't move it
+ if (!cur_selected || core_strnicmp(cur_selected->basename.c_str(), m_filename.c_str(), m_filename.size()))
+ {
+ std::string::size_type bestmatch(0);
+ file_selector_entry const *selected_entry(cur_selected);
+ for (auto &entry : m_entrylist)
{
- std::string::size_type bestmatch(0);
- file_selector_entry const *selected_entry(cur_selected);
- for (auto &entry : m_entrylist)
+ // TODO: more efficient "common prefix" code
+ std::string::size_type match(0);
+ for (std::string::size_type i = 1; m_filename.size() >= i; ++i)
{
- // TODO: more efficient "common prefix" code
- std::string::size_type match(0);
- for (std::string::size_type i = 1; m_filename.size() >= i; ++i)
- {
- if (!core_strnicmp(entry.basename.c_str(), m_filename.c_str(), i))
- match = i;
- else
- break;
- }
-
- if (match > bestmatch)
- {
- bestmatch = match;
- selected_entry = &entry;
- }
+ if (!core_strnicmp(entry.basename.c_str(), m_filename.c_str(), i))
+ match = i;
+ else
+ break;
}
- if (selected_entry && (selected_entry != cur_selected))
+ if (match > bestmatch)
{
- set_selection((void *)selected_entry);
- centre_selection();
+ bestmatch = match;
+ selected_entry = &entry;
}
}
+
+ if (selected_entry && (selected_entry != cur_selected))
+ {
+ set_selection((void *)selected_entry);
+ centre_selection();
+ }
}
}
//-------------------------------------------------
+// get_directory_range
+//-------------------------------------------------
+
+std::pair<size_t, size_t> menu_file_selector::get_directory_range(float x, float y)
+{
+ size_t start = 0, span = 0;
+ if (m_path_layout->hit_test(x - m_path_position.first, y - m_path_position.second, start, span))
+ {
+ if (std::string_view(m_current_directory).substr(start, span) != PATH_SEPARATOR)
+ {
+ auto target_start = m_current_directory.rfind(PATH_SEPARATOR, start);
+ if (std::string::npos == target_start)
+ target_start = 0;
+ else
+ target_start += 1;
+
+ auto target_end = m_current_directory.find(PATH_SEPARATOR, start + span);
+ if (std::string::npos == target_end)
+ target_end = m_current_directory.length();
+
+ return std::make_pair(target_start, target_end);
+ }
+ }
+
+ return std::make_pair(std::string::npos, std::string::npos);
+}
+
+
+//-------------------------------------------------
// populate
//-------------------------------------------------
-void menu_file_selector::populate(float &customtop, float &custombottom)
+void menu_file_selector::populate()
{
const file_selector_entry *selected_entry = nullptr;
-
// clear out the menu entries
m_entrylist.clear();
// open the directory
util::zippath_directory::ptr directory;
- osd_file::error const err = util::zippath_directory::open(m_current_directory, directory);
+ std::error_condition const err = util::zippath_directory::open(m_current_directory, directory);
// add the "[empty slot]" entry if available
if (m_has_empty)
append_entry(SELECTOR_ENTRY_TYPE_EMPTY, "", "");
// add the "[create]" entry
- if (m_has_create && !directory->is_archive())
+ if (m_has_create && directory && !directory->is_archive())
append_entry(SELECTOR_ENTRY_TYPE_CREATE, "", "");
// add and select the "[software list]" entry if available
@@ -411,17 +500,18 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
selected_entry = &append_entry(SELECTOR_ENTRY_TYPE_SOFTWARE_LIST, "", "");
// add the drives
- int i = 0;
- for (char const *volume_name = osd_get_volume_name(i); volume_name; volume_name = osd_get_volume_name(++i))
+ for (std::string const &volume_name : osd_get_volume_names())
append_entry(SELECTOR_ENTRY_TYPE_DRIVE, volume_name, volume_name);
// mark first filename entry
std::size_t const first = m_entrylist.size() + 1;
// build the menu for each item
- if (osd_file::error::NONE != err)
+ if (err)
{
- osd_printf_verbose("menu_file_selector::populate: error opening directory '%s' (%d)\n", m_current_directory, int(err));
+ osd_printf_verbose(
+ "menu_file_selector::populate: error opening directory '%s' (%s:%d %s)\n",
+ m_current_directory, err.category().name(), err.value(), err.message());
}
else
{
@@ -436,24 +526,28 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
selected_entry = entry;
// do we have to select this file?
- if (!core_stricmp(m_current_file.c_str(), dirent->name))
+ if (!core_stricmp(m_current_file, dirent->name))
selected_entry = entry;
}
}
}
directory.reset();
- // sort the menu entries
- const std::collate<wchar_t> &coll = std::use_facet<std::collate<wchar_t>>(std::locale());
- std::sort(
- m_entrylist.begin() + first,
- m_entrylist.end(),
- [&coll] (file_selector_entry const &x, file_selector_entry const &y)
- {
- std::wstring const xstr = wstring_from_utf8(x.basename);
- std::wstring const ystr = wstring_from_utf8(y.basename);
- return coll.compare(xstr.data(), xstr.data()+xstr.size(), ystr.data(), ystr.data()+ystr.size()) < 0;
- });
+ if (m_entrylist.size() > first)
+ {
+ // sort the menu entries
+ std::locale const lcl;
+ std::collate<wchar_t> const &coll = std::use_facet<std::collate<wchar_t> >(lcl);
+ std::sort(
+ m_entrylist.begin() + first,
+ m_entrylist.end(),
+ [&coll] (file_selector_entry const &x, file_selector_entry const &y)
+ {
+ std::wstring const xstr = wstring_from_utf8(x.basename);
+ std::wstring const ystr = wstring_from_utf8(y.basename);
+ return coll.compare(xstr.data(), xstr.data() + xstr.size(), ystr.data(), ystr.data() + ystr.size()) < 0;
+ });
+ }
// append all of the menu entries
for (file_selector_entry const &entry : m_entrylist)
@@ -462,9 +556,6 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
// set the selection (if we have one)
if (selected_entry)
set_selection((void *)selected_entry);
-
- // set up custom render proc
- customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
}
@@ -472,31 +563,61 @@ void menu_file_selector::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_file_selector::handle()
+bool menu_file_selector::handle(event const *ev)
{
- // process the menu
- event const *const event = process(0);
- if (event && event->itemref)
+ if (!ev)
+ return false;
+
+ if (ev->iptkey == IPT_SPECIAL)
{
- // handle selections
- if (event->iptkey == IPT_UI_SELECT)
+ // if it's any other key and we're not maxed out, update
+ if (input_character(m_filename, ev->unichar, uchar_is_printable))
{
- select_item(*reinterpret_cast<file_selector_entry const *>(event->itemref));
-
- // reset the char buffer when pressing IPT_UI_SELECT
- m_filename.clear();
+ update_search();
+ return true;
}
- else if (event->iptkey == IPT_SPECIAL)
+ }
+ else if (ev->iptkey == IPT_UI_PASTE)
+ {
+ if (paste_text(m_filename, uchar_is_printable))
{
- // if it's any other key and we're not maxed out, update
- type_search_char(event->unichar);
+ update_search();
+ return true;
}
- else if (event->iptkey == IPT_UI_CANCEL)
+ }
+ else if (ev->iptkey == IPT_UI_CANCEL)
+ {
+ // reset the char buffer also in this case
+ if (!m_filename.empty())
{
- // reset the char buffer also in this case
m_filename.clear();
+ ui().popup_time(ERROR_MESSAGE_TIME, "%s", m_filename);
+ return true;
}
}
+ else if (ev->iptkey == IPT_CUSTOM)
+ {
+ // clicked a path component
+ if (m_clicked_directory.second > m_clicked_directory.first)
+ {
+ m_current_directory.resize(m_clicked_directory.second + strlen(PATH_SEPARATOR));
+ m_path_layout.reset();
+ m_clicked_directory = std::make_pair(std::string::npos, std::string::npos);
+ reset(reset_options::SELECT_FIRST);
+ return true;
+ }
+ }
+ else if (ev->itemref && (ev->iptkey == IPT_UI_SELECT))
+ {
+ // handle selections
+ select_item(*reinterpret_cast<file_selector_entry const *>(ev->itemref));
+
+ // reset the char buffer when pressing IPT_UI_SELECT
+ m_filename.clear();
+ return true;
+ }
+
+ return false;
}
@@ -509,11 +630,15 @@ void menu_file_selector::handle()
// ctor
//-------------------------------------------------
-menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container,
- bool can_in_place, result &result)
- : menu(mui, container),
- m_can_in_place(can_in_place),
- m_result(result)
+menu_select_rw::menu_select_rw(
+ mame_ui_manager &mui,
+ render_container &container,
+ bool can_in_place,
+ handler_function &&handler)
+ : menu(mui, container)
+ , m_handler(std::move(handler))
+ , m_can_in_place(can_in_place)
+ , m_result(result::INVALID)
{
}
@@ -524,6 +649,8 @@ menu_select_rw::menu_select_rw(mame_ui_manager &mui, render_container &container
menu_select_rw::~menu_select_rw()
{
+ if (m_handler)
+ m_handler(m_result);
}
@@ -531,14 +658,15 @@ menu_select_rw::~menu_select_rw()
// populate
//-------------------------------------------------
-void menu_select_rw::populate(float &customtop, float &custombottom)
+void menu_select_rw::populate()
{
- item_append(_("Select access mode"), "", FLAG_DISABLE, nullptr);
- item_append(_("Read-only"), "", 0, itemref_from_result(result::READONLY));
+ set_heading(_("Select access mode"));
+
+ item_append(_("Read-only"), 0, itemref_from_result(result::READONLY));
if (m_can_in_place)
- item_append(_("Read-write"), "", 0, itemref_from_result(result::READWRITE));
- item_append(_("Read this image, write to another image"), "", 0, itemref_from_result(result::WRITE_OTHER));
- item_append(_("Read this image, write to diff"), "", 0, itemref_from_result(result::WRITE_DIFF));
+ item_append(_("Read-write"), 0, itemref_from_result(result::READWRITE));
+ item_append(_("Read this image, write to another image"), 0, itemref_from_result(result::WRITE_OTHER));
+ item_append(_("Read this image, write to diff"), 0, itemref_from_result(result::WRITE_DIFF));
}
@@ -546,15 +674,15 @@ void menu_select_rw::populate(float &customtop, float &custombottom)
// handle
//-------------------------------------------------
-void menu_select_rw::handle()
+bool menu_select_rw::handle(event const *ev)
{
- // process the menu
- const event *event = process(0);
- if (event != nullptr && event->iptkey == IPT_UI_SELECT)
+ if (ev && ev->iptkey == IPT_UI_SELECT)
{
- m_result = result_from_itemref(event->itemref);
+ m_result = result_from_itemref(ev->itemref);
stack_pop();
}
+
+ return false;
}