1 // license:BSD-3-Clause
2 // copyright-holders:Nicola Salmoria, Aaron Giles, Nathan Woods, Vas Crabb
3 /***************************************************************************
4 
5     ui/selmenu.cpp
6 
7     MAME system/software selection menu.
8 
9 ***************************************************************************/
10 
11 #include "emu.h"
12 #include "ui/selmenu.h"
13 
14 #include "ui/datmenu.h"
15 #include "ui/info.h"
16 #include "ui/inifile.h"
17 
18 // these hold static bitmap images
19 #include "ui/defimg.ipp"
20 #include "ui/starimg.ipp"
21 #include "ui/toolbar.ipp"
22 
23 #include "audit.h"
24 #include "cheat.h"
25 #include "mame.h"
26 #include "mameopts.h"
27 
28 #include "drivenum.h"
29 #include "emuopts.h"
30 #include "rendfont.h"
31 #include "rendutil.h"
32 #include "romload.h"
33 #include "softlist.h"
34 #include "softlist_dev.h"
35 #include "uiinput.h"
36 #include "luaengine.h"
37 
38 #include <algorithm>
39 #include <cmath>
40 #include <cstring>
41 #include <utility>
42 
43 
44 namespace ui {
45 
46 namespace {
47 
48 enum
49 {
50 	FIRST_VIEW = 0,
51 	SNAPSHOT_VIEW = FIRST_VIEW,
52 	CABINETS_VIEW,
53 	CPANELS_VIEW,
54 	PCBS_VIEW,
55 	FLYERS_VIEW,
56 	TITLES_VIEW,
57 	ENDS_VIEW,
58 	ARTPREV_VIEW,
59 	BOSSES_VIEW,
60 	LOGOS_VIEW,
61 	VERSUS_VIEW,
62 	GAMEOVER_VIEW,
63 	HOWTO_VIEW,
64 	SCORES_VIEW,
65 	SELECT_VIEW,
66 	MARQUEES_VIEW,
67 	COVERS_VIEW,
68 	LAST_VIEW = COVERS_VIEW
69 };
70 
71 std::pair<char const *, char const *> const arts_info[] =
72 {
73 	{ __("Snapshots"),       OPTION_SNAPSHOT_DIRECTORY },
74 	{ __("Cabinets"),        OPTION_CABINETS_PATH },
75 	{ __("Control Panels"),  OPTION_CPANELS_PATH },
76 	{ __("PCBs"),            OPTION_PCBS_PATH },
77 	{ __("Flyers"),          OPTION_FLYERS_PATH },
78 	{ __("Titles"),          OPTION_TITLES_PATH },
79 	{ __("Ends"),            OPTION_ENDS_PATH },
80 	{ __("Artwork Preview"), OPTION_ARTPREV_PATH },
81 	{ __("Bosses"),          OPTION_BOSSES_PATH },
82 	{ __("Logos"),           OPTION_LOGOS_PATH },
83 	{ __("Versus"),          OPTION_VERSUS_PATH },
84 	{ __("Game Over"),       OPTION_GAMEOVER_PATH },
85 	{ __("HowTo"),           OPTION_HOWTO_PATH },
86 	{ __("Scores"),          OPTION_SCORES_PATH },
87 	{ __("Select"),          OPTION_SELECT_PATH },
88 	{ __("Marquees"),        OPTION_MARQUEES_PATH },
89 	{ __("Covers"),          OPTION_COVER_PATH },
90 };
91 
92 char const *const hover_msg[] = {
93 	__("Add or remove favorites"),
94 	__("Export displayed list to file"),
95 	__("Show DATs view"),
96 };
97 
98 
load_image(bitmap_argb32 & bitmap,emu_file & file,std::string const & base)99 void load_image(bitmap_argb32 &bitmap, emu_file &file, std::string const &base)
100 {
101 	if (file.open(base + ".png") == osd_file::error::NONE)
102 	{
103 		render_load_png(bitmap, file);
104 		file.close();
105 	}
106 
107 	if (!bitmap.valid() && (file.open(base + ".jpg") == osd_file::error::NONE))
108 	{
109 		render_load_jpeg(bitmap, file);
110 		file.close();
111 	}
112 
113 	if (!bitmap.valid() && (file.open(base + ".bmp") == osd_file::error::NONE))
114 	{
115 		render_load_msdib(bitmap, file);
116 		file.close();
117 	}
118 }
119 
120 
load_driver_image(bitmap_argb32 & bitmap,emu_file & file,game_driver const & driver)121 void load_driver_image(bitmap_argb32 &bitmap, emu_file &file, game_driver const &driver)
122 {
123 	// try to load snapshot first from saved "0000.png" file
124 	std::string fullname = driver.name;
125 	load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000");
126 
127 	// if fail, attempt to load from standard file
128 	if (!bitmap.valid())
129 		load_image(bitmap, file, fullname);
130 
131 	// if fail again, attempt to load from parent file
132 	if (!bitmap.valid())
133 	{
134 		// ignore BIOS sets
135 		bool isclone = strcmp(driver.parent, "0") != 0;
136 		if (isclone)
137 		{
138 			int const cx = driver_list::find(driver.parent);
139 			if ((0 <= cx) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT))
140 				isclone = false;
141 		}
142 
143 		if (isclone)
144 		{
145 			fullname = driver.parent;
146 			load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000");
147 
148 			if (!bitmap.valid())
149 				load_image(bitmap, file, fullname);
150 		}
151 	}
152 }
153 
154 } // anonymous namespace
155 
156 constexpr std::size_t menu_select_launch::MAX_VISIBLE_SEARCH; // stupid non-inline semantics
157 
158 
159 class menu_select_launch::software_parts : public menu
160 {
161 public:
162 	software_parts(mame_ui_manager &mui, render_container &container, s_parts &&parts, ui_software_info const &ui_info);
163 	virtual ~software_parts() override;
164 
165 protected:
166 	virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
167 
168 private:
169 	virtual void populate(float &customtop, float &custombottom) override;
170 	virtual void handle() override;
171 
172 	ui_software_info const &m_uiinfo;
173 	s_parts const          m_parts;
174 };
175 
176 class menu_select_launch::bios_selection : public menu
177 {
178 public:
179 	bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, game_driver const &driver, bool inlist);
180 	bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, ui_software_info const &swinfo, bool inlist);
181 	virtual ~bios_selection() override;
182 
183 protected:
184 	virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
185 
186 private:
187 	bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, void const *driver, bool software, bool inlist);
188 
189 	virtual void populate(float &customtop, float &custombottom) override;
190 	virtual void handle() override;
191 
192 	void const  *m_driver;
193 	bool        m_software, m_inlist;
194 	s_bios      m_bios;
195 };
196 
197 std::string menu_select_launch::reselect_last::s_driver;
198 std::string menu_select_launch::reselect_last::s_software;
199 std::string menu_select_launch::reselect_last::s_swlist;
200 bool menu_select_launch::reselect_last::s_reselect = false;
201 
202 std::mutex menu_select_launch::s_cache_guard;
203 menu_select_launch::cache_ptr_map menu_select_launch::s_caches;
204 
205 // instantiate possible variants of these so derived classes don't get link errors
206 template bool menu_select_launch::select_bios(game_driver const &, bool);
207 template bool menu_select_launch::select_bios(ui_software_info const &, bool);
208 template float menu_select_launch::draw_left_panel<machine_filter>(machine_filter::type current, std::map<machine_filter::type, machine_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
209 template float menu_select_launch::draw_left_panel<software_filter>(software_filter::type current, std::map<software_filter::type, software_filter::ptr> const &filters, float x1, float y1, float x2, float y2);
210 
211 
system_flags(machine_static_info const & info)212 menu_select_launch::system_flags::system_flags(machine_static_info const &info)
213 	: m_machine_flags(info.machine_flags())
214 	, m_unemulated_features(info.unemulated_features())
215 	, m_imperfect_features(info.imperfect_features())
216 	, m_has_keyboard(info.has_keyboard())
217 	, m_has_analog(info.has_analog())
218 	, m_status_color(info.status_color())
219 {
220 }
221 
222 
reset()223 void menu_select_launch::reselect_last::reset()
224 {
225 	s_driver.clear();
226 	s_software.clear();
227 	s_swlist.clear();
228 	reselect(false);
229 }
230 
set_driver(std::string const & name)231 void menu_select_launch::reselect_last::set_driver(std::string const &name)
232 {
233 	s_driver = name;
234 	s_software.clear();
235 	s_swlist.clear();
236 }
237 
set_software(game_driver const & driver,ui_software_info const & swinfo)238 void menu_select_launch::reselect_last::set_software(game_driver const &driver, ui_software_info const &swinfo)
239 {
240 	s_driver = driver.name;
241 	if (swinfo.startempty)
242 	{
243 		// FIXME: magic strings are bad...
244 		s_software = "[Start empty]";
245 		s_swlist.clear();
246 	}
247 	else
248 	{
249 		s_software = swinfo.shortname;
250 		s_swlist = swinfo.listname;
251 	}
252 }
253 
254 
255 //-------------------------------------------------
256 //  ctor
257 //-------------------------------------------------
258 
software_parts(mame_ui_manager & mui,render_container & container,s_parts && parts,ui_software_info const & ui_info)259 menu_select_launch::software_parts::software_parts(mame_ui_manager &mui, render_container &container, s_parts &&parts, ui_software_info const &ui_info)
260 	: menu(mui, container)
261 	, m_uiinfo(ui_info)
262 	, m_parts(std::move(parts))
263 {
264 }
265 
266 //-------------------------------------------------
267 //  dtor
268 //-------------------------------------------------
269 
~software_parts()270 menu_select_launch::software_parts::~software_parts()
271 {
272 }
273 
274 //-------------------------------------------------
275 //  populate
276 //-------------------------------------------------
277 
populate(float & customtop,float & custombottom)278 void menu_select_launch::software_parts::populate(float &customtop, float &custombottom)
279 {
280 	std::vector<s_parts::const_iterator> parts;
281 	parts.reserve(m_parts.size());
282 	for (s_parts::const_iterator it = m_parts.begin(); m_parts.end() != it; ++it)
283 		parts.push_back(it);
284 	std::sort(parts.begin(), parts.end(), [] (auto const &left, auto const &right) { return 0 > core_stricmp(left->first.c_str(), right->first.c_str()); });
285 	for (auto const &elem : parts)
286 		item_append(elem->first, elem->second, 0, (void *)&*elem);
287 
288 	item_append(menu_item_type::SEPARATOR);
289 	customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
290 }
291 
292 //-------------------------------------------------
293 //  handle
294 //-------------------------------------------------
295 
handle()296 void menu_select_launch::software_parts::handle()
297 {
298 	// process the menu
299 	const event *menu_event = process(0);
300 	if (menu_event && (menu_event->iptkey) == IPT_UI_SELECT && menu_event->itemref)
301 	{
302 		for (auto const &elem : m_parts)
303 		{
304 			if ((void*)&elem == menu_event->itemref)
305 			{
306 				launch_system(ui(), *m_uiinfo.driver, &m_uiinfo, &elem.first, nullptr);
307 				break;
308 			}
309 		}
310 	}
311 }
312 
313 //-------------------------------------------------
314 //  perform our special rendering
315 //-------------------------------------------------
316 
custom_render(void * selectedref,float top,float bottom,float origx1,float origy1,float origx2,float origy2)317 void menu_select_launch::software_parts::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
318 {
319 	char const *const text[] = { _("Software part selection:") };
320 	draw_text_box(
321 			std::begin(text), std::end(text),
322 			origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(),
323 			ui::text_layout::CENTER, ui::text_layout::TRUNCATE, false,
324 			ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
325 }
326 
327 
328 //-------------------------------------------------
329 //  ctor
330 //-------------------------------------------------
331 
bios_selection(mame_ui_manager & mui,render_container & container,s_bios && biosname,game_driver const & driver,bool inlist)332 menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, game_driver const &driver, bool inlist)
333 	: bios_selection(mui, container, std::move(biosname), reinterpret_cast<void const *>(&driver), false, inlist)
334 {
335 }
336 
bios_selection(mame_ui_manager & mui,render_container & container,s_bios && biosname,ui_software_info const & swinfo,bool inlist)337 menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, ui_software_info const &swinfo, bool inlist)
338 	: bios_selection(mui, container, std::move(biosname), reinterpret_cast<void const *>(&swinfo), true, inlist)
339 {
340 }
341 
bios_selection(mame_ui_manager & mui,render_container & container,s_bios && biosname,void const * driver,bool software,bool inlist)342 menu_select_launch::bios_selection::bios_selection(mame_ui_manager &mui, render_container &container, s_bios &&biosname, void const *driver, bool software, bool inlist)
343 	: menu(mui, container)
344 	, m_driver(driver)
345 	, m_software(software)
346 	, m_inlist(inlist)
347 	, m_bios(std::move(biosname))
348 {
349 }
350 
351 //-------------------------------------------------
352 //  dtor
353 //-------------------------------------------------
354 
~bios_selection()355 menu_select_launch::bios_selection::~bios_selection()
356 {
357 }
358 
359 //-------------------------------------------------
360 //  populate
361 //-------------------------------------------------
362 
populate(float & customtop,float & custombottom)363 void menu_select_launch::bios_selection::populate(float &customtop, float &custombottom)
364 {
365 	for (auto & elem : m_bios)
366 		item_append(elem.first, "", 0, (void *)&elem.first);
367 
368 	item_append(menu_item_type::SEPARATOR);
369 	customtop = ui().get_line_height() + (3.0f * ui().box_tb_border());
370 }
371 
372 //-------------------------------------------------
373 //  handle
374 //-------------------------------------------------
375 
handle()376 void menu_select_launch::bios_selection::handle()
377 {
378 	// process the menu
379 	const event *menu_event = process(0);
380 	if (menu_event && menu_event->iptkey == IPT_UI_SELECT && menu_event->itemref)
381 	{
382 		for (auto & elem : m_bios)
383 		{
384 			if ((void*)&elem.first == menu_event->itemref)
385 			{
386 				if (!m_software)
387 				{
388 					const game_driver *s_driver = (const game_driver *)m_driver;
389 					if (m_inlist)
390 					{
391 						ui_software_info empty(*s_driver);
392 						launch_system(ui(), *s_driver, &empty, nullptr, &elem.second);
393 					}
394 					else
395 					{
396 						reselect_last::reselect(true);
397 						launch_system(ui(), *s_driver, nullptr, nullptr, &elem.second);
398 					}
399 				}
400 				else
401 				{
402 					ui_software_info *ui_swinfo = (ui_software_info *)m_driver;
403 					machine().options().set_value(OPTION_BIOS, elem.second, OPTION_PRIORITY_CMDLINE); // oh dear, relying on this persisting through the part selection menu
404 					driver_enumerator drivlist(machine().options(), *ui_swinfo->driver);
405 					drivlist.next();
406 					software_list_device *swlist = software_list_device::find_by_name(*drivlist.config(), ui_swinfo->listname);
407 					const software_info *swinfo = swlist->find(ui_swinfo->shortname);
408 					if (!select_part(ui(), container(), *swinfo, *ui_swinfo))
409 					{
410 						reselect_last::reselect(true);
411 						launch_system(ui(), drivlist.driver(), ui_swinfo, nullptr, &elem.second);
412 					}
413 				}
414 			}
415 		}
416 	}
417 }
418 
419 //-------------------------------------------------
420 //  perform our special rendering
421 //-------------------------------------------------
422 
custom_render(void * selectedref,float top,float bottom,float origx1,float origy1,float origx2,float origy2)423 void menu_select_launch::bios_selection::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
424 {
425 	char const *const text[] = { _("BIOS selection:") };
426 	draw_text_box(
427 			std::begin(text), std::end(text),
428 			origx1, origx2, origy1 - top, origy1 - ui().box_tb_border(),
429 			ui::text_layout::CENTER, ui::text_layout::TRUNCATE, false,
430 			ui().colors().text_color(), UI_GREEN_COLOR, 1.0f);
431 }
432 
433 
cache(running_machine & machine)434 menu_select_launch::cache::cache(running_machine &machine)
435 	: m_snapx_bitmap(std::make_unique<bitmap_argb32>(0, 0))
436 	, m_snapx_texture(nullptr, machine.render())
437 	, m_snapx_driver(nullptr)
438 	, m_snapx_software(nullptr)
439 	, m_no_avail_bitmap(256, 256)
440 	, m_star_bitmap(32, 32)
441 	, m_star_texture(nullptr, machine.render())
442 	, m_toolbar_bitmap()
443 	, m_sw_toolbar_bitmap()
444 	, m_toolbar_texture()
445 	, m_sw_toolbar_texture()
446 {
447 	render_manager &render(machine.render());
448 
449 	// create a texture for snapshot
450 	m_snapx_texture.reset(render.texture_alloc(render_texture::hq_scale));
451 
452 	std::memcpy(&m_no_avail_bitmap.pix(0), no_avail_bmp, 256 * 256 * sizeof(uint32_t));
453 
454 	std::memcpy(&m_star_bitmap.pix(0), favorite_star_bmp, 32 * 32 * sizeof(uint32_t));
455 	m_star_texture.reset(render.texture_alloc());
456 	m_star_texture->set_bitmap(m_star_bitmap, m_star_bitmap.cliprect(), TEXFORMAT_ARGB32);
457 
458 	m_toolbar_bitmap.reserve(UI_TOOLBAR_BUTTONS);
459 	m_sw_toolbar_bitmap.reserve(UI_TOOLBAR_BUTTONS);
460 	m_toolbar_texture.reserve(UI_TOOLBAR_BUTTONS);
461 	m_sw_toolbar_texture.reserve(UI_TOOLBAR_BUTTONS);
462 
463 	for (std::size_t i = 0; i < UI_TOOLBAR_BUTTONS; ++i)
464 	{
465 		m_toolbar_bitmap.emplace_back(32, 32);
466 		m_sw_toolbar_bitmap.emplace_back(32, 32);
467 		m_toolbar_texture.emplace_back(render.texture_alloc(), render);
468 		m_sw_toolbar_texture.emplace_back(render.texture_alloc(), render);
469 
470 		std::memcpy(&m_toolbar_bitmap.back().pix(0), toolbar_bitmap_bmp[i], 32 * 32 * sizeof(uint32_t));
471 		if (m_toolbar_bitmap.back().valid())
472 			m_toolbar_texture.back()->set_bitmap(m_toolbar_bitmap.back(), m_toolbar_bitmap.back().cliprect(), TEXFORMAT_ARGB32);
473 		else
474 			m_toolbar_bitmap.back().reset();
475 
476 		if ((i == 0U) || (i == 2U))
477 		{
478 			std::memcpy(&m_sw_toolbar_bitmap.back().pix(0), toolbar_bitmap_bmp[i], 32 * 32 * sizeof(uint32_t));
479 			if (m_sw_toolbar_bitmap.back().valid())
480 				m_sw_toolbar_texture.back()->set_bitmap(m_sw_toolbar_bitmap.back(), m_sw_toolbar_bitmap.back().cliprect(), TEXFORMAT_ARGB32);
481 			else
482 				m_sw_toolbar_bitmap.back().reset();
483 		}
484 		else
485 		{
486 			m_sw_toolbar_bitmap.back().reset();
487 		}
488 	}
489 }
490 
491 
~cache()492 menu_select_launch::cache::~cache()
493 {
494 }
495 
496 
~menu_select_launch()497 menu_select_launch::~menu_select_launch()
498 {
499 }
500 
501 
menu_select_launch(mame_ui_manager & mui,render_container & container,bool is_swlist)502 menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &container, bool is_swlist)
503 	: menu(mui, container)
504 	, m_prev_selected(nullptr)
505 	, m_total_lines(0)
506 	, m_topline_datsview(0)
507 	, m_filter_highlight(0)
508 	, m_ui_error(false)
509 	, m_info_driver(nullptr)
510 	, m_info_software(nullptr)
511 	, m_info_view(-1)
512 	, m_items_list()
513 	, m_info_buffer()
514 	, m_cache()
515 	, m_is_swlist(is_swlist)
516 	, m_focus(focused_menu::MAIN)
517 	, m_pressed(false)
518 	, m_repeat(0)
519 	, m_right_visible_lines(0)
520 	, m_has_icons(false)
521 	, m_switch_image(false)
522 	, m_default_image(true)
523 	, m_image_view(FIRST_VIEW)
524 	, m_flags(256)
525 {
526 	// set up persistent cache for machine run
527 	{
528 		std::lock_guard<std::mutex> guard(s_cache_guard);
529 		auto const found(s_caches.find(&machine()));
530 		if (found != s_caches.end())
531 		{
532 			assert(found->second);
533 			m_cache = found->second;
534 		}
535 		else
536 		{
537 			m_cache = std::make_shared<cache>(machine());
538 			s_caches.emplace(&machine(), m_cache);
539 			add_cleanup_callback(&menu_select_launch::exit);
540 		}
541 	}
542 }
543 
544 
next_image_view()545 void menu_select_launch::next_image_view()
546 {
547 	if (LAST_VIEW > m_image_view)
548 	{
549 		++m_image_view;
550 		set_switch_image();
551 		m_default_image = false;
552 	}
553 }
554 
555 
previous_image_view()556 void menu_select_launch::previous_image_view()
557 {
558 	if (FIRST_VIEW < m_image_view)
559 	{
560 		--m_image_view;
561 		set_switch_image();
562 		m_default_image = false;
563 	}
564 }
565 
566 
dismiss_error()567 bool menu_select_launch::dismiss_error()
568 {
569 	bool const result = m_ui_error;
570 	if (result)
571 	{
572 		m_ui_error = false;
573 		m_error_text.clear();
574 		machine().ui_input().reset();
575 	}
576 	return result;
577 }
578 
set_error(reset_options ropt,std::string && message)579 void menu_select_launch::set_error(reset_options ropt, std::string &&message)
580 {
581 	reset(ropt);
582 	m_ui_error = true;
583 	m_error_text = std::move(message);
584 }
585 
586 
587 //-------------------------------------------------
588 //  get overall emulation status for a system
589 //-------------------------------------------------
590 
get_system_flags(game_driver const & driver)591 menu_select_launch::system_flags const &menu_select_launch::get_system_flags(game_driver const &driver)
592 {
593 	// try the cache
594 	flags_cache::const_iterator const found(m_flags.find(&driver));
595 	if (m_flags.end() != found)
596 		return found->second;
597 
598 	// aggregate flags
599 	emu_options clean_options;
600 	machine_config const mconfig(driver, clean_options);
601 	return m_flags.emplace(&driver, machine_static_info(ui().options(), mconfig)).first->second;
602 }
603 
604 
605 //-------------------------------------------------
606 //  actually start an emulation session
607 //-------------------------------------------------
608 
launch_system(mame_ui_manager & mui,game_driver const & driver,ui_software_info const * swinfo,std::string const * part,int const * bios)609 void menu_select_launch::launch_system(mame_ui_manager &mui, game_driver const &driver, ui_software_info const *swinfo, std::string const *part, int const *bios)
610 {
611 	emu_options &moptions(mui.machine().options());
612 	moptions.set_system_name(driver.name);
613 
614 	if (swinfo)
615 	{
616 		if (!swinfo->startempty)
617 		{
618 			if (part)
619 				moptions.set_value(swinfo->instance, util::string_format("%s:%s:%s", swinfo->listname, swinfo->shortname, *part), OPTION_PRIORITY_CMDLINE);
620 			else
621 				moptions.set_value(OPTION_SOFTWARENAME, util::string_format("%s:%s", swinfo->listname, swinfo->shortname), OPTION_PRIORITY_CMDLINE);
622 
623 			moptions.set_value(OPTION_SNAPNAME, util::string_format("%s%s%s", swinfo->listname, PATH_SEPARATOR, swinfo->shortname), OPTION_PRIORITY_CMDLINE);
624 		}
625 		reselect_last::set_software(driver, *swinfo);
626 	}
627 	else
628 	{
629 		reselect_last::set_driver(driver);
630 	}
631 
632 	if (bios)
633 		moptions.set_value(OPTION_BIOS, *bios, OPTION_PRIORITY_CMDLINE);
634 
635 	mame_machine_manager::instance()->schedule_new_driver(driver);
636 	mui.machine().schedule_hard_reset();
637 	stack_reset(mui.machine());
638 }
639 
640 
641 //-------------------------------------------------
642 //  perform our special rendering
643 //-------------------------------------------------
644 
custom_render(void * selectedref,float top,float bottom,float origx1,float origy1,float origx2,float origy2)645 void menu_select_launch::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
646 {
647 	std::string tempbuf[5];
648 
649 	// determine the text for the header
650 	make_topbox_text(tempbuf[0], tempbuf[1], tempbuf[2]);
651 	float const y1 = origy1 - 3.0f * ui().box_tb_border() - ui().get_line_height();
652 	draw_text_box(
653 			tempbuf, tempbuf + 3,
654 			origx1, origx2, origy1 - top, y1,
655 			ui::text_layout::CENTER, ui::text_layout::NEVER, true,
656 			ui().colors().text_color(), ui().colors().background_color(), 1.0f);
657 
658 	// draw toolbar
659 	draw_toolbar(origx1, y1, origx2, origy1 - ui().box_tb_border());
660 
661 	// determine the text to render below
662 	ui_software_info const *swinfo;
663 	game_driver const *driver;
664 	get_selection(swinfo, driver);
665 
666 	bool isstar = false;
667 	rgb_t color = ui().colors().background_color();
668 	if (swinfo && ((swinfo->startempty != 1) || !driver))
669 	{
670 		isstar = mame_machine_manager::instance()->favorite().is_favorite_system_software(*swinfo);
671 
672 		// first line is long name or system
673 		tempbuf[0] = make_software_description(*swinfo);
674 
675 		// next line is year, publisher
676 		tempbuf[1] = string_format(_("%1$s, %2$-.100s"), swinfo->year, swinfo->publisher);
677 
678 		// next line is parent/clone
679 		if (!swinfo->parentname.empty())
680 			tempbuf[2] = string_format(_("Software is clone of: %1$-.100s"), !swinfo->parentlongname.empty() ? swinfo->parentlongname : swinfo->parentname);
681 		else
682 			tempbuf[2] = _("Software is parent");
683 
684 		// next line is supported status
685 		if (swinfo->supported == SOFTWARE_SUPPORTED_NO)
686 		{
687 			tempbuf[3] = _("Supported: No");
688 			color = UI_RED_COLOR;
689 		}
690 		else if (swinfo->supported == SOFTWARE_SUPPORTED_PARTIAL)
691 		{
692 			tempbuf[3] = _("Supported: Partial");
693 			color = UI_YELLOW_COLOR;
694 		}
695 		else
696 		{
697 			tempbuf[3] = _("Supported: Yes");
698 			color = UI_GREEN_COLOR;
699 		}
700 
701 		// last line is romset name
702 		tempbuf[4] = string_format(_("romset: %1$-.100s"), swinfo->shortname);
703 	}
704 	else if (driver)
705 	{
706 		isstar = mame_machine_manager::instance()->favorite().is_favorite_system(*driver);
707 
708 		// first line is game description/game name
709 		tempbuf[0] = make_driver_description(*driver);
710 
711 		// next line is year, manufacturer
712 		tempbuf[1] = string_format(_("%1$s, %2$-.100s"), driver->year, driver->manufacturer);
713 
714 		// next line is clone/parent status
715 		int cloneof = driver_list::non_bios_clone(*driver);
716 
717 		if (cloneof != -1)
718 			tempbuf[2] = string_format(_("Driver is clone of: %1$-.100s"), driver_list::driver(cloneof).type.fullname());
719 		else
720 			tempbuf[2] = _("Driver is parent");
721 
722 		// next line is overall driver status
723 		system_flags const &flags(get_system_flags(*driver));
724 		if (flags.machine_flags() & machine_flags::NOT_WORKING)
725 			tempbuf[3] = _("Overall: NOT WORKING");
726 		else if ((flags.unemulated_features() | flags.imperfect_features()) & device_t::feature::PROTECTION)
727 			tempbuf[3] = _("Overall: Unemulated Protection");
728 		else
729 			tempbuf[3] = _("Overall: Working");
730 
731 		// next line is graphics, sound status
732 		if (flags.unemulated_features() & device_t::feature::GRAPHICS)
733 			tempbuf[4] = _("Graphics: Unimplemented, ");
734 		else if ((flags.unemulated_features() | flags.imperfect_features()) & (device_t::feature::GRAPHICS | device_t::feature::PALETTE))
735 			tempbuf[4] = _("Graphics: Imperfect, ");
736 		else
737 			tempbuf[4] = _("Graphics: OK, ");
738 
739 		if (driver->flags & machine_flags::NO_SOUND_HW)
740 			tempbuf[4].append(_("Sound: None"));
741 		else if (flags.unemulated_features() & device_t::feature::SOUND)
742 			tempbuf[4].append(_("Sound: Unimplemented"));
743 		else if (flags.imperfect_features() & device_t::feature::SOUND)
744 			tempbuf[4].append(_("Sound: Imperfect"));
745 		else
746 			tempbuf[4].append(_("Sound: OK"));
747 
748 		color = flags.status_color();
749 	}
750 	else
751 	{
752 		std::string copyright(emulator_info::get_copyright());
753 		size_t found = copyright.find('\n');
754 
755 		tempbuf[0].clear();
756 		tempbuf[1] = string_format(_("%1$s %2$s"), emulator_info::get_appname(), build_version);
757 		tempbuf[2] = copyright.substr(0, found);
758 		tempbuf[3] = copyright.substr(found + 1);
759 		tempbuf[4].clear();
760 	}
761 
762 	// draw the footer
763 	draw_text_box(
764 			std::begin(tempbuf), std::end(tempbuf),
765 			origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
766 			ui::text_layout::CENTER, ui::text_layout::NEVER, true,
767 			ui().colors().text_color(), color, 1.0f);
768 
769 	// is favorite? draw the star
770 	if (isstar)
771 		draw_star(origx1 + ui().box_lr_border() * machine().render().ui_aspect(&container()), origy2 + (2.0f * ui().box_tb_border()));
772 }
773 
774 
rotate_focus(int dir)775 void menu_select_launch::rotate_focus(int dir)
776 {
777 	switch (get_focus())
778 	{
779 	case focused_menu::MAIN:
780 		if (selected_index() <= m_available_items)
781 		{
782 			m_prev_selected = get_selection_ref();
783 			if ((0 < dir) || (ui_globals::panels_status == HIDE_BOTH))
784 				set_selected_index(m_available_items + 1);
785 			else if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
786 				set_focus(focused_menu::LEFT);
787 			else
788 				set_focus(focused_menu::RIGHTBOTTOM);
789 		}
790 		else
791 		{
792 			if ((0 > dir) || (ui_globals::panels_status == HIDE_BOTH))
793 				select_prev();
794 			else if (ui_globals::panels_status == HIDE_LEFT_PANEL)
795 				set_focus(focused_menu::RIGHTTOP);
796 			else
797 				set_focus(focused_menu::LEFT);
798 		}
799 		break;
800 
801 	case focused_menu::LEFT:
802 		if (0 > dir)
803 		{
804 			set_focus(focused_menu::MAIN);
805 			set_selected_index(m_available_items + 1);
806 		}
807 		else if (ui_globals::panels_status != HIDE_RIGHT_PANEL)
808 		{
809 			set_focus(focused_menu::RIGHTTOP);
810 		}
811 		else
812 		{
813 			set_focus(focused_menu::MAIN);
814 			select_prev();
815 		}
816 		break;
817 
818 	case focused_menu::RIGHTTOP:
819 		if (0 < dir)
820 		{
821 			set_focus(focused_menu::RIGHTBOTTOM);
822 		}
823 		else if (ui_globals::panels_status != HIDE_LEFT_PANEL)
824 		{
825 			set_focus(focused_menu::LEFT);
826 		}
827 		else
828 		{
829 			set_focus(focused_menu::MAIN);
830 			set_selected_index(m_available_items + 1);
831 		}
832 		break;
833 
834 	case focused_menu::RIGHTBOTTOM:
835 		if (0 > dir)
836 		{
837 			set_focus(focused_menu::RIGHTTOP);
838 		}
839 		else
840 		{
841 			set_focus(focused_menu::MAIN);
842 			select_prev();
843 		}
844 		break;
845 	}
846 }
847 
848 
inkey_dats()849 void menu_select_launch::inkey_dats()
850 {
851 	ui_software_info const *software;
852 	game_driver const *driver;
853 	get_selection(software, driver);
854 	if (software)
855 	{
856 		if (software->startempty && mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", software->driver->name, true))
857 			menu::stack_push<menu_dats_view>(ui(), container(), software->driver);
858 		else if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str()) || !software->usage.empty())
859 			menu::stack_push<menu_dats_view>(ui(), container(), software);
860 	}
861 	else if (driver)
862 	{
863 		if (mame_machine_manager::instance()->lua()->call_plugin_check<const char *>("data_list", driver->name, true))
864 			menu::stack_push<menu_dats_view>(ui(), container(), driver);
865 	}
866 }
867 
868 
869 //-------------------------------------------------
870 //  draw common arrows
871 //-------------------------------------------------
872 
draw_common_arrow(float origx1,float origy1,float origx2,float origy2,int current,int dmin,int dmax,float title_size)873 void menu_select_launch::draw_common_arrow(float origx1, float origy1, float origx2, float origy2, int current, int dmin, int dmax, float title_size)
874 {
875 	auto line_height = ui().get_line_height();
876 	auto lr_arrow_width = 0.4f * line_height * machine().render().ui_aspect(&container());
877 	auto gutter_width = lr_arrow_width * 1.3f;
878 
879 	// set left-right arrows dimension
880 	float const ar_x0 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width - lr_arrow_width;
881 	float const ar_y0 = origy1 + 0.1f * line_height;
882 	float const ar_x1 = 0.5f * (origx2 + origx1) + 0.5f * title_size + gutter_width;
883 	float const ar_y1 = origy1 + 0.9f * line_height;
884 
885 	float const al_x0 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width;
886 	float const al_y0 = origy1 + 0.1f * line_height;
887 	float const al_x1 = 0.5f * (origx2 + origx1) - 0.5f * title_size - gutter_width + lr_arrow_width;
888 	float const al_y1 = origy1 + 0.9f * line_height;
889 
890 	rgb_t fgcolor_right, fgcolor_left;
891 	fgcolor_right = fgcolor_left = ui().colors().text_color();
892 
893 	// set hover
894 	if (mouse_in_rect(ar_x0, ar_y0, ar_x1, ar_y1) && current != dmax)
895 	{
896 		ui().draw_textured_box(container(), ar_x0 + 0.01f, ar_y0, ar_x1 - 0.01f, ar_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
897 				hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
898 		set_hover(HOVER_UI_RIGHT);
899 		fgcolor_right = ui().colors().mouseover_color();
900 	}
901 	else if (mouse_in_rect(al_x0, al_y0, al_x1, al_y1) && current != dmin)
902 	{
903 		ui().draw_textured_box(container(), al_x0 + 0.01f, al_y0, al_x1 - 0.01f, al_y1, ui().colors().mouseover_bg_color(), rgb_t(43, 43, 43),
904 				hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
905 		set_hover(HOVER_UI_LEFT);
906 		fgcolor_left = ui().colors().mouseover_color();
907 	}
908 
909 	// apply arrow
910 	if (dmax == dmin)
911 		return;
912 	else if (current == dmin)
913 		draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
914 	else if (current == dmax)
915 		draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
916 	else
917 	{
918 		draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor_right, ROT90);
919 		draw_arrow(al_x0, al_y0, al_x1, al_y1, fgcolor_left, ROT90 ^ ORIENTATION_FLIP_X);
920 	}
921 }
922 
923 
924 //-------------------------------------------------
925 //  draw info arrow
926 //-------------------------------------------------
927 
draw_info_arrow(int ub,float origx1,float origx2,float oy1,float line_height,float text_size,float ud_arrow_width)928 void menu_select_launch::draw_info_arrow(int ub, float origx1, float origx2, float oy1, float line_height, float text_size, float ud_arrow_width)
929 {
930 	rgb_t fgcolor = ui().colors().text_color();
931 	uint32_t orientation = (!ub) ? ROT0 : ROT0 ^ ORIENTATION_FLIP_Y;
932 
933 	if (mouse_in_rect(origx1, oy1, origx2, oy1 + (line_height * text_size)))
934 	{
935 		ui().draw_textured_box(container(), origx1 + 0.01f, oy1, origx2 - 0.01f, oy1 + (line_height * text_size), ui().colors().mouseover_bg_color(),
936 				rgb_t(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
937 		set_hover((!ub) ? HOVER_DAT_UP : HOVER_DAT_DOWN);
938 		fgcolor = ui().colors().mouseover_color();
939 	}
940 
941 	draw_arrow(0.5f * (origx1 + origx2) - 0.5f * (ud_arrow_width * text_size), oy1 + 0.25f * (line_height * text_size),
942 			0.5f * (origx1 + origx2) + 0.5f * (ud_arrow_width * text_size), oy1 + 0.75f * (line_height * text_size), fgcolor, orientation);
943 }
944 
draw_error_text()945 bool menu_select_launch::draw_error_text()
946 {
947 	if (m_ui_error)
948 		ui().draw_text_box(container(), m_error_text.c_str(), ui::text_layout::CENTER, 0.5f, 0.5f, UI_RED_COLOR);
949 
950 	return m_ui_error;
951 }
952 
953 
954 template <typename Filter>
draw_left_panel(typename Filter::type current,std::map<typename Filter::type,typename Filter::ptr> const & filters,float x1,float y1,float x2,float y2)955 float menu_select_launch::draw_left_panel(
956 		typename Filter::type current,
957 		std::map<typename Filter::type, typename Filter::ptr> const &filters,
958 		float x1, float y1, float x2, float y2)
959 {
960 	if ((ui_globals::panels_status != SHOW_PANELS) && (ui_globals::panels_status != HIDE_RIGHT_PANEL))
961 		return draw_collapsed_left_panel(x1, y1, x2, y2);
962 
963 	// calculate line height
964 	float const line_height(ui().get_line_height());
965 	float const text_size(ui().options().infos_size());
966 	float const sc(y2 - y1 - (2.0f * ui().box_tb_border()));
967 	float line_height_max(line_height * text_size);
968 	if ((Filter::COUNT * line_height_max) > sc)
969 	{
970 		float const lm(sc / Filter::COUNT);
971 		line_height_max = line_height * (lm / line_height);
972 	}
973 
974 	// calculate horizontal offset for unadorned names
975 	std::string tmp("_# ");
976 	convert_command_glyph(tmp);
977 	float const text_sign = ui().get_string_width(tmp.c_str(), text_size);
978 
979 	// get the maximum width of a filter name
980 	float left_width(0.0f);
981 	for (typename Filter::type x = Filter::FIRST; Filter::COUNT > x; ++x)
982 		left_width = std::max(ui().get_string_width(Filter::display_name(x), text_size) + text_sign, left_width);
983 
984 	// outline the box and inset by the border width
985 	float const origy1(y1);
986 	float const origy2(y2);
987 	float const aspect(machine().render().ui_aspect(&container()));
988 	float const lr_border(ui().box_lr_border() * aspect);
989 	x2 = x1 + left_width + 2.0f * lr_border;
990 	ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
991 	x1 += lr_border;
992 	x2 -= lr_border;
993 	y1 += ui().box_tb_border();
994 	y2 -= ui().box_tb_border();
995 
996 	// now draw the rows
997 	auto const active_filter(filters.find(current));
998 	for (typename Filter::type filter = Filter::FIRST; Filter::COUNT > filter; ++filter)
999 	{
1000 		std::string str;
1001 		if (filters.end() != active_filter)
1002 		{
1003 			str = active_filter->second->adorned_display_name(filter);
1004 		}
1005 		else
1006 		{
1007 			if (current == filter)
1008 			{
1009 				str = std::string("_> ");
1010 				convert_command_glyph(str);
1011 			}
1012 			str.append(Filter::display_name(filter));
1013 		}
1014 
1015 		// handle mouse hover in passing
1016 		rgb_t bgcolor = ui().colors().text_bg_color();
1017 		rgb_t fgcolor = ui().colors().text_color();
1018 		if (mouse_in_rect(x1, y1, x2, y1 + line_height_max))
1019 		{
1020 			bgcolor = ui().colors().mouseover_bg_color();
1021 			fgcolor = ui().colors().mouseover_color();
1022 			set_hover(HOVER_FILTER_FIRST + filter);
1023 			highlight(x1, y1, x2, y1 + line_height_max, bgcolor);
1024 		}
1025 
1026 		// draw primary highlight if keyboard focus is here
1027 		if ((m_filter_highlight == filter) && (get_focus() == focused_menu::LEFT))
1028 		{
1029 			fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
1030 			bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
1031 			ui().draw_textured_box(
1032 					container(),
1033 					x1, y1, x2, y1 + line_height_max,
1034 					bgcolor, rgb_t(255, 43, 43, 43),
1035 					hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
1036 		}
1037 
1038 		// finally draw the text itself and move to the next line
1039 		float const x1t = x1 + ((str == Filter::display_name(filter)) ? text_sign : 0.0f);
1040 		ui().draw_text_full(
1041 				container(), str.c_str(),
1042 				x1t, y1, x2 - x1,
1043 				ui::text_layout::LEFT, ui::text_layout::NEVER,
1044 				mame_ui_manager::NORMAL, fgcolor, bgcolor,
1045 				nullptr, nullptr, text_size);
1046 		y1 += line_height_max;
1047 	}
1048 
1049 	x1 = x2 + lr_border;
1050 	x2 = x1 + 2.0f * lr_border;
1051 	y1 = origy1;
1052 	y2 = origy2;
1053 	float const space = x2 - x1;
1054 	float const lr_arrow_width = 0.4f * space * aspect;
1055 
1056 	// set left-right arrows dimension
1057 	float const ar_x0 = 0.5f * (x2 + x1) - 0.5f * lr_arrow_width;
1058 	float const ar_y0 = 0.5f * (y2 + y1) + 0.1f * space;
1059 	float const ar_x1 = ar_x0 + lr_arrow_width;
1060 	float const ar_y1 = 0.5f * (y2 + y1) + 0.9f * space;
1061 
1062 	ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b));
1063 
1064 	rgb_t fgcolor = ui().colors().text_color();
1065 	if (mouse_in_rect(x1, y1, x2, y2))
1066 	{
1067 		fgcolor = ui().colors().mouseover_color();
1068 		set_hover(HOVER_LPANEL_ARROW);
1069 	}
1070 
1071 	draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
1072 	return x2 + lr_border;
1073 }
1074 
1075 
1076 //-------------------------------------------------
1077 //  icon helpers
1078 //-------------------------------------------------
1079 
check_for_icons(char const * listname)1080 void menu_select_launch::check_for_icons(char const *listname)
1081 {
1082 	// only ever set the flag, never clear it
1083 	if (m_has_icons)
1084 		return;
1085 
1086 	// iterate over configured icon paths
1087 	path_iterator paths(ui().options().icons_directory());
1088 	std::string current;
1089 	while (paths.next(current))
1090 	{
1091 		// if we're doing a software list, append it to the configured path
1092 		if (listname)
1093 		{
1094 			if (!current.empty() && !util::is_directory_separator(current.back()))
1095 				current.append(PATH_SEPARATOR);
1096 			current.append(listname);
1097 		}
1098 		osd_printf_verbose("Checking for icons in directory %s\n", current);
1099 
1100 		// open and walk the directory
1101 		osd::directory::ptr const dir(osd::directory::open(current));
1102 		if (dir)
1103 		{
1104 			// this could be improved in many ways - it's just a rough go/no-go
1105 			osd::directory::entry const *entry;
1106 			while ((entry = dir->read()) != nullptr)
1107 			{
1108 				current = entry->name;
1109 				std::string::size_type const found(current.rfind(".ico"));
1110 				if ((std::string::npos != found) && ((current.length() - 4) == found))
1111 				{
1112 					osd_printf_verbose("Entry %s is a candidate icon file\n", entry->name);
1113 					m_has_icons = true;
1114 					return;
1115 				}
1116 				else if (("icons" == current) || (current.find("icons.") == 0U))
1117 				{
1118 					osd_printf_verbose("Entry %s is a candidate icon collection\n", entry->name);
1119 					m_has_icons = true;
1120 					return;
1121 				}
1122 			}
1123 		}
1124 	}
1125 
1126 	// nothing promising
1127 	osd_printf_verbose(
1128 			"No candidate icons found for %s%s\n",
1129 			listname ? "software list " : "",
1130 			listname ? listname : "machines");
1131 }
1132 
make_icon_paths(char const * listname) const1133 std::string menu_select_launch::make_icon_paths(char const *listname) const
1134 {
1135 	// iterate over configured icon paths
1136 	path_iterator paths(ui().options().icons_directory());
1137 	std::string current, result;
1138 	while (paths.next(current))
1139 	{
1140 		// if we're doing a software list, append it to the configured path
1141 		if (listname)
1142 		{
1143 			if (!current.empty() && !util::is_directory_separator(current.back()))
1144 				current.append(PATH_SEPARATOR);
1145 			current.append(listname);
1146 		}
1147 
1148 		// append the configured path
1149 		if (!result.empty())
1150 			result.append(1, ';'); // FIXME: should be a macro
1151 		result.append(current);
1152 
1153 		// append with "icons" appended so it'll search icons.zip or icons.7z in the directory
1154 		if (!current.empty())
1155 		{
1156 			result.append(1, ';'); // FIXME: should be a macro
1157 			result.append(current);
1158 			if (!util::is_directory_separator(result.back()))
1159 				result.append(PATH_SEPARATOR);
1160 		}
1161 		result.append("icons");
1162 	}
1163 
1164 	// log the result for debugging
1165 	osd_printf_verbose(
1166 			"Icon path for %s%s set to %s\n",
1167 			listname ? "software list " : "",
1168 			listname ? listname : "machines",
1169 			result.c_str());
1170 	return result;
1171 }
1172 
scale_icon(bitmap_argb32 && src,texture_and_bitmap & dst) const1173 bool menu_select_launch::scale_icon(bitmap_argb32 &&src, texture_and_bitmap &dst) const
1174 {
1175 	assert(dst.texture);
1176 	if (src.valid())
1177 	{
1178 		// calculate available space for the icon in pixels
1179 		float const height(ui().get_line_height());
1180 		float const width(height * container().manager().ui_aspect(&container()));
1181 		render_target const &target(machine().render().ui_target());
1182 		uint32_t const dst_height(target.height());
1183 		uint32_t const dst_width(target.width());
1184 		bool const rotated((target.orientation() & ORIENTATION_SWAP_XY) != 0);
1185 		int const max_height(int((rotated ? dst_width : dst_height) * height));
1186 		int const max_width(int((rotated ? dst_height : dst_width) * width));
1187 
1188 		// reduce the source bitmap if it's too big
1189 		bitmap_argb32 tmp;
1190 		float const ratio((std::min)({ float(max_height) / src.height(), float(max_width) / src.width(), 1.0F }));
1191 		if (1.0F > ratio)
1192 		{
1193 			float const pix_height(src.height() * ratio);
1194 			float const pix_width(src.width() * ratio);
1195 			tmp.allocate(int32_t(pix_width), int32_t(pix_height));
1196 			render_resample_argb_bitmap_hq(tmp, src, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true);
1197 		}
1198 		else
1199 		{
1200 			tmp = std::move(src);
1201 		}
1202 
1203 		// copy into the destination
1204 		dst.bitmap.allocate(max_width, max_height);
1205 		for (int y = 0; tmp.height() > y; ++y)
1206 			for (int x = 0; tmp.width() > x; ++x)
1207 				dst.bitmap.pix(y, x) = tmp.pix(y, x);
1208 		dst.texture->set_bitmap(dst.bitmap, dst.bitmap.cliprect(), TEXFORMAT_ARGB32);
1209 		return true;
1210 	}
1211 	else
1212 	{
1213 		// couldn't load icon
1214 		dst.bitmap.reset();
1215 		return false;
1216 	}
1217 }
1218 
1219 
select_bios(T const & driver,bool inlist)1220 template <typename T> bool menu_select_launch::select_bios(T const &driver, bool inlist)
1221 {
1222 	s_bios biosname;
1223 	if (ui().options().skip_bios_menu() || !has_multiple_bios(driver, biosname))
1224 		return false;
1225 
1226 	menu::stack_push<bios_selection>(ui(), container(), std::move(biosname), driver, inlist);
1227 	return true;
1228 }
1229 
select_part(software_info const & info,ui_software_info const & ui_info)1230 bool menu_select_launch::select_part(software_info const &info, ui_software_info const &ui_info)
1231 {
1232 	return select_part(ui(), container(), info, ui_info);
1233 }
1234 
select_part(mame_ui_manager & mui,render_container & container,software_info const & info,ui_software_info const & ui_info)1235 bool menu_select_launch::select_part(mame_ui_manager &mui, render_container &container, software_info const &info, ui_software_info const &ui_info)
1236 {
1237 	if (mui.options().skip_parts_menu() || !info.has_multiple_parts(ui_info.interface.c_str()))
1238 		return false;
1239 
1240 	s_parts parts;
1241 	for (software_part const &part : info.parts())
1242 	{
1243 		if (part.matches_interface(ui_info.interface.c_str()))
1244 		{
1245 			std::string menu_part_name(part.name());
1246 			if (part.feature("part_id"))
1247 				menu_part_name.assign("(").append(part.feature("part_id")).append(")");
1248 			parts.emplace(part.name(), std::move(menu_part_name));
1249 		}
1250 	}
1251 	menu::stack_push<software_parts>(mui, container, std::move(parts), ui_info);
1252 	return true;
1253 }
1254 
1255 
1256 //-------------------------------------------------
1257 //  draw toolbar
1258 //-------------------------------------------------
1259 
draw_toolbar(float x1,float y1,float x2,float y2)1260 void menu_select_launch::draw_toolbar(float x1, float y1, float x2, float y2)
1261 {
1262 	// draw a box
1263 	ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
1264 
1265 	// take off the borders
1266 	float const aspect(machine().render().ui_aspect(&container()));
1267 	float const lr_border(ui().box_lr_border() * aspect);
1268 	x1 += lr_border;
1269 	x2 -= lr_border;
1270 	y1 += ui().box_tb_border();
1271 	y2 -= ui().box_tb_border();
1272 
1273 	texture_ptr_vector const &t_texture(m_is_swlist ? m_cache->sw_toolbar_texture() : m_cache->toolbar_texture());
1274 	bitmap_vector const &t_bitmap(m_is_swlist ? m_cache->sw_toolbar_bitmap() : m_cache->toolbar_bitmap());
1275 
1276 	auto const num_valid(std::count_if(std::begin(t_bitmap), std::end(t_bitmap), [](bitmap_argb32 const &e) { return e.valid(); }));
1277 
1278 	float const space_x = (y2 - y1) * aspect;
1279 	float const total = (float(num_valid) * space_x) + (float(num_valid - 1) * 0.001f);
1280 	x1 += (x2 - x1) * 0.5f - total * 0.5f;
1281 	x2 = x1 + space_x;
1282 
1283 	for (int z = 0; z < UI_TOOLBAR_BUTTONS; ++z)
1284 	{
1285 		if (t_bitmap[z].valid())
1286 		{
1287 			rgb_t color(0xEFEFEFEF);
1288 			if (mouse_in_rect(x1, y1, x2, y2))
1289 			{
1290 				set_hover(HOVER_B_FAV + z);
1291 				color = rgb_t::white();
1292 				float ypos = y2 + ui().get_line_height() + 2.0f * ui().box_tb_border();
1293 				ui().draw_text_box(container(), _(hover_msg[z]), ui::text_layout::CENTER, 0.5f, ypos, ui().colors().background_color());
1294 			}
1295 
1296 			container().add_quad(x1, y1, x2, y2, color, t_texture[z].get(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
1297 			x1 += space_x + ((z < UI_TOOLBAR_BUTTONS - 1) ? 0.001f : 0.0f);
1298 			x2 = x1 + space_x;
1299 		}
1300 	}
1301 }
1302 
1303 
1304 //-------------------------------------------------
1305 //  draw favorites star
1306 //-------------------------------------------------
1307 
draw_star(float x0,float y0)1308 void menu_select_launch::draw_star(float x0, float y0)
1309 {
1310 	float y1 = y0 + ui().get_line_height();
1311 	float x1 = x0 + ui().get_line_height() * container().manager().ui_aspect(&container());
1312 	container().add_quad(x0, y0, x1, y1, rgb_t::white(), m_cache->star_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_PACKABLE);
1313 }
1314 
1315 
set_pressed()1316 void menu_select_launch::set_pressed()
1317 {
1318 	(m_repeat == 0) ? m_repeat = osd_ticks() + osd_ticks_per_second() / 2 : m_repeat = osd_ticks() + osd_ticks_per_second() / 4;
1319 	m_pressed = true;
1320 }
1321 
1322 
1323 //-------------------------------------------------
1324 //  draw icons
1325 //-------------------------------------------------
1326 
draw_icon(int linenum,void * selectedref,float x0,float y0)1327 void menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, float y0)
1328 {
1329 	render_texture *const icon(get_icon_texture(linenum, selectedref));
1330 	if (icon)
1331 	{
1332 		float const ud_arrow_width = ui().get_line_height() * container().manager().ui_aspect(&container());
1333 		float const x1 = x0 + ud_arrow_width;
1334 		float const y1 = y0 + ui().get_line_height();
1335 		container().add_quad(x0, y0, x1, y1, rgb_t::white(), icon, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
1336 	};
1337 }
1338 
1339 
1340 //-------------------------------------------------
1341 //  get title and search path for right panel
1342 //-------------------------------------------------
1343 
get_title_search(std::string & snaptext,std::string & searchstr)1344 void menu_select_launch::get_title_search(std::string &snaptext, std::string &searchstr)
1345 {
1346 	// get arts title text
1347 	snaptext.assign(_(arts_info[m_image_view].first));
1348 
1349 	// get search path
1350 	std::string addpath;
1351 	if (m_image_view == SNAPSHOT_VIEW)
1352 	{
1353 		emu_options moptions;
1354 		searchstr = machine().options().value(arts_info[m_image_view].second);
1355 		addpath = moptions.value(arts_info[m_image_view].second);
1356 	}
1357 	else
1358 	{
1359 		ui_options moptions;
1360 		searchstr = ui().options().value(arts_info[m_image_view].second);
1361 		addpath = moptions.value(arts_info[m_image_view].second);
1362 	}
1363 
1364 	std::string tmp(searchstr);
1365 	path_iterator path(tmp);
1366 	path_iterator path_iter(addpath);
1367 	std::string c_path, curpath;
1368 
1369 	// iterate over path and add path for zipped formats
1370 	while (path.next(curpath))
1371 	{
1372 		path_iter.reset();
1373 		while (path_iter.next(c_path))
1374 			searchstr.append(";").append(curpath).append(PATH_SEPARATOR).append(c_path);
1375 	}
1376 }
1377 
1378 
1379 //-------------------------------------------------
1380 //  handle keys for main menu
1381 //-------------------------------------------------
1382 
handle_keys(uint32_t flags,int & iptkey)1383 void menu_select_launch::handle_keys(uint32_t flags, int &iptkey)
1384 {
1385 	bool const ignorepause = stack_has_special_main_menu();
1386 
1387 	// bail if no items
1388 	if (item_count() == 0)
1389 		return;
1390 
1391 	// if we hit select, return true or pop the stack, depending on the item
1392 	if (exclusive_input_pressed(iptkey, IPT_UI_SELECT, 0))
1393 	{
1394 		if (m_ui_error)
1395 		{
1396 			// dismiss error
1397 		}
1398 		else if (m_focus == focused_menu::LEFT)
1399 		{
1400 			m_prev_selected = nullptr;
1401 			filter_selected();
1402 		}
1403 		if (is_last_selected() && (m_focus == focused_menu::MAIN))
1404 		{
1405 			iptkey = IPT_UI_CANCEL;
1406 			stack_pop();
1407 		}
1408 		return;
1409 	}
1410 
1411 	if (exclusive_input_pressed(iptkey, IPT_UI_CANCEL, 0))
1412 	{
1413 		if (m_ui_error)
1414 		{
1415 			// dismiss error
1416 		}
1417 		else if (menu_has_search_active())
1418 		{
1419 			// escape pressed with non-empty search text clears it
1420 			m_search.clear();
1421 			reset(reset_options::SELECT_FIRST);
1422 		}
1423 		else
1424 		{
1425 			// otherwise pop the stack
1426 			stack_pop();
1427 		}
1428 		return;
1429 	}
1430 
1431 	// validate the current selection
1432 	validate_selection(1);
1433 
1434 	// swallow left/right keys if they are not appropriate
1435 	bool const ignoreleft = ((selected_item().flags & FLAG_LEFT_ARROW) == 0);
1436 	bool const ignoreright = ((selected_item().flags & FLAG_RIGHT_ARROW) == 0);
1437 	bool const leftclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_LEFT_PANEL);
1438 	bool const rightclose = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL);
1439 
1440 	// accept left/right keys as-is with repeat
1441 	if (!ignoreleft && exclusive_input_pressed(iptkey, IPT_UI_LEFT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
1442 	{
1443 		// Swap the right panel
1444 		if (m_focus == focused_menu::RIGHTTOP)
1445 			ui_globals::rpanel = RP_IMAGES;
1446 		return;
1447 	}
1448 
1449 	if (!ignoreright && exclusive_input_pressed(iptkey, IPT_UI_RIGHT, (flags & PROCESS_LR_REPEAT) ? 6 : 0))
1450 	{
1451 		// Swap the right panel
1452 		if (m_focus == focused_menu::RIGHTTOP)
1453 			ui_globals::rpanel = RP_INFOS;
1454 		return;
1455 	}
1456 
1457 	// up backs up by one item
1458 	if (exclusive_input_pressed(iptkey, IPT_UI_UP, 6))
1459 	{
1460 		if (!leftclose && m_focus == focused_menu::LEFT)
1461 		{
1462 			return;
1463 		}
1464 		else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1465 		{
1466 			m_topline_datsview--;
1467 			return;
1468 		}
1469 		else if (selected_index() == m_available_items + 1 || is_first_selected() || m_ui_error)
1470 		{
1471 			return;
1472 		}
1473 
1474 		set_selected_index(selected_index() - 1);
1475 
1476 		if (selected_index() == top_line && top_line != 0)
1477 			top_line--;
1478 	}
1479 
1480 	// down advances by one item
1481 	if (exclusive_input_pressed(iptkey, IPT_UI_DOWN, 6))
1482 	{
1483 		if (!leftclose && m_focus == focused_menu::LEFT)
1484 		{
1485 			return;
1486 		}
1487 		else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1488 		{
1489 			m_topline_datsview++;
1490 			return;
1491 		}
1492 		else if (is_last_selected() || selected_index() == m_available_items - 1 || m_ui_error)
1493 		{
1494 			return;
1495 		}
1496 
1497 		set_selected_index(selected_index() + 1);
1498 		if (selected_index() == top_line + m_visible_items + (top_line != 0))
1499 			top_line++;
1500 	}
1501 
1502 	// page up backs up by m_visible_items
1503 	if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_UP, 6))
1504 	{
1505 		// Infos
1506 		if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1507 		{
1508 			m_topline_datsview -= m_right_visible_lines - 1;
1509 			return;
1510 		}
1511 
1512 		if (selected_index() < m_available_items && !m_ui_error)
1513 		{
1514 			set_selected_index(std::max(selected_index() - m_visible_items, 0));
1515 
1516 			top_line -= m_visible_items - (top_line + m_visible_lines == m_available_items);
1517 		}
1518 	}
1519 
1520 	// page down advances by m_visible_items
1521 	if (exclusive_input_pressed(iptkey, IPT_UI_PAGE_DOWN, 6))
1522 	{
1523 		// Infos
1524 		if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1525 		{
1526 			m_topline_datsview += m_right_visible_lines - 1;
1527 			return;
1528 		}
1529 
1530 		if (selected_index() < m_available_items && !m_ui_error)
1531 		{
1532 			set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1));
1533 
1534 			top_line += m_visible_lines - 2;
1535 		}
1536 	}
1537 
1538 	// home goes to the start
1539 	if (exclusive_input_pressed(iptkey, IPT_UI_HOME, 0))
1540 	{
1541 		if (!leftclose && m_focus == focused_menu::LEFT)
1542 		{
1543 			return;
1544 		}
1545 		else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1546 		{
1547 			m_topline_datsview = 0;
1548 			return;
1549 		}
1550 
1551 		if (selected_index() < m_available_items && !m_ui_error)
1552 			select_first_item();
1553 	}
1554 
1555 	// end goes to the last
1556 	if (exclusive_input_pressed(iptkey, IPT_UI_END, 0))
1557 	{
1558 		if (!leftclose && m_focus == focused_menu::LEFT)
1559 		{
1560 			return;
1561 		}
1562 		else if (!rightclose && m_focus == focused_menu::RIGHTBOTTOM)
1563 		{
1564 			m_topline_datsview = m_total_lines;
1565 			return;
1566 		}
1567 
1568 		if (selected_index() < m_available_items && !m_ui_error)
1569 			set_selected_index(top_line = m_available_items - 1);
1570 	}
1571 
1572 	// focus next rotates throw targets forward
1573 	if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_NEXT, 12))
1574 	{
1575 		if (!m_ui_error)
1576 			rotate_focus(1);
1577 	}
1578 
1579 	// focus next rotates throw targets forward
1580 	if (exclusive_input_pressed(iptkey, IPT_UI_FOCUS_PREV, 12))
1581 	{
1582 		if (!m_ui_error)
1583 			rotate_focus(-1);
1584 	}
1585 
1586 	// pause enables/disables pause
1587 	if (!m_ui_error && !ignorepause && exclusive_input_pressed(iptkey, IPT_UI_PAUSE, 0))
1588 	{
1589 		if (machine().paused())
1590 			machine().resume();
1591 		else
1592 			machine().pause();
1593 	}
1594 
1595 	// handle a toggle cheats request
1596 	if (!m_ui_error && machine().ui_input().pressed_repeat(IPT_UI_TOGGLE_CHEAT, 0))
1597 		mame_machine_manager::instance()->cheat().set_enable(!mame_machine_manager::instance()->cheat().enabled());
1598 
1599 	// see if any other UI keys are pressed
1600 	if (iptkey == IPT_INVALID)
1601 	{
1602 		for (int code = IPT_UI_FIRST + 1; code < IPT_UI_LAST; code++)
1603 		{
1604 			if (m_ui_error)
1605 				continue;
1606 
1607 			switch (code)
1608 			{
1609 			case IPT_UI_FOCUS_NEXT:
1610 			case IPT_UI_FOCUS_PREV:
1611 				continue;
1612 			case IPT_UI_LEFT:
1613 				if (ignoreleft)
1614 					continue;
1615 				break;
1616 			case IPT_UI_RIGHT:
1617 				if (ignoreright)
1618 					continue;
1619 				break;
1620 			case IPT_UI_PAUSE:
1621 				if (ignorepause)
1622 					continue;
1623 				break;
1624 			}
1625 
1626 			if (exclusive_input_pressed(iptkey, code, 0))
1627 				break;
1628 		}
1629 	}
1630 }
1631 
1632 
1633 //-------------------------------------------------
1634 //  handle input events for main menu
1635 //-------------------------------------------------
1636 
handle_events(uint32_t flags,event & ev)1637 void menu_select_launch::handle_events(uint32_t flags, event &ev)
1638 {
1639 	if (m_pressed)
1640 	{
1641 		bool const pressed = mouse_pressed();
1642 		int32_t target_x, target_y;
1643 		bool button;
1644 		render_target *const mouse_target = machine().ui_input().find_mouse(&target_x, &target_y, &button);
1645 		if (mouse_target && button && (hover() == HOVER_ARROW_DOWN || hover() == HOVER_ARROW_UP))
1646 		{
1647 			if (pressed)
1648 				machine().ui_input().push_mouse_down_event(mouse_target, target_x, target_y);
1649 		}
1650 		else
1651 		{
1652 			reset_pressed();
1653 		}
1654 	}
1655 
1656 	// loop while we have interesting events
1657 	bool stop(false), search_changed(false);
1658 	ui_event local_menu_event;
1659 	while (!stop && machine().ui_input().pop_event(&local_menu_event))
1660 	{
1661 		switch (local_menu_event.event_type)
1662 		{
1663 		// if we are hovering over a valid item, select it with a single click
1664 		case ui_event::type::MOUSE_DOWN:
1665 			if (m_ui_error)
1666 			{
1667 				ev.iptkey = IPT_OTHER;
1668 				stop = true;
1669 			}
1670 			else
1671 			{
1672 				if (hover() >= 0 && hover() < item_count())
1673 				{
1674 					if (hover() >= m_available_items - 1 && selected_index() < m_available_items)
1675 						m_prev_selected = get_selection_ref();
1676 					set_selected_index(hover());
1677 					m_focus = focused_menu::MAIN;
1678 				}
1679 				else if (hover() == HOVER_ARROW_UP)
1680 				{
1681 					set_selected_index(std::max(selected_index() - m_visible_items, 0));
1682 					top_line -= m_visible_items - (top_line + m_visible_lines == m_available_items);
1683 					set_pressed();
1684 				}
1685 				else if (hover() == HOVER_ARROW_DOWN)
1686 				{
1687 					set_selected_index(std::min(selected_index() + m_visible_lines - 2 + (selected_index() == 0), m_available_items - 1));
1688 					top_line += m_visible_lines - 2;
1689 					set_pressed();
1690 				}
1691 				else if (hover() == HOVER_UI_RIGHT)
1692 					ev.iptkey = IPT_UI_RIGHT;
1693 				else if (hover() == HOVER_UI_LEFT)
1694 					ev.iptkey = IPT_UI_LEFT;
1695 				else if (hover() == HOVER_DAT_DOWN)
1696 					m_topline_datsview += m_right_visible_lines - 1;
1697 				else if (hover() == HOVER_DAT_UP)
1698 					m_topline_datsview -= m_right_visible_lines - 1;
1699 				else if (hover() == HOVER_LPANEL_ARROW)
1700 				{
1701 					if (get_focus() == focused_menu::LEFT)
1702 					{
1703 						set_focus(focused_menu::MAIN);
1704 						select_prev();
1705 					}
1706 
1707 					if (ui_globals::panels_status == HIDE_LEFT_PANEL)
1708 						ui_globals::panels_status = SHOW_PANELS;
1709 					else if (ui_globals::panels_status == HIDE_BOTH)
1710 						ui_globals::panels_status = HIDE_RIGHT_PANEL;
1711 					else if (ui_globals::panels_status == SHOW_PANELS)
1712 						ui_globals::panels_status = HIDE_LEFT_PANEL;
1713 					else if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
1714 						ui_globals::panels_status = HIDE_BOTH;
1715 				}
1716 				else if (hover() == HOVER_RPANEL_ARROW)
1717 				{
1718 					if ((get_focus() == focused_menu::RIGHTTOP) || (get_focus() == focused_menu::RIGHTBOTTOM))
1719 					{
1720 						set_focus(focused_menu::MAIN);
1721 						select_prev();
1722 					}
1723 
1724 					if (ui_globals::panels_status == HIDE_RIGHT_PANEL)
1725 						ui_globals::panels_status = SHOW_PANELS;
1726 					else if (ui_globals::panels_status == HIDE_BOTH)
1727 						ui_globals::panels_status = HIDE_LEFT_PANEL;
1728 					else if (ui_globals::panels_status == SHOW_PANELS)
1729 						ui_globals::panels_status = HIDE_RIGHT_PANEL;
1730 					else if (ui_globals::panels_status == HIDE_LEFT_PANEL)
1731 						ui_globals::panels_status = HIDE_BOTH;
1732 				}
1733 				else if (hover() == HOVER_B_FAV)
1734 				{
1735 					ev.iptkey = IPT_UI_FAVORITES;
1736 					stop = true;
1737 				}
1738 				else if (hover() == HOVER_B_EXPORT)
1739 				{
1740 					inkey_export();
1741 					stop = true;
1742 				}
1743 				else if (hover() == HOVER_B_DATS)
1744 				{
1745 					inkey_dats();
1746 					stop = true;
1747 				}
1748 				else if (hover() >= HOVER_RP_FIRST && hover() <= HOVER_RP_LAST)
1749 				{
1750 					ui_globals::rpanel = (HOVER_RP_FIRST - hover()) * (-1);
1751 					stop = true;
1752 				}
1753 				else if (hover() >= HOVER_FILTER_FIRST && hover() <= HOVER_FILTER_LAST)
1754 				{
1755 					m_prev_selected = nullptr;
1756 					m_filter_highlight = hover() - HOVER_FILTER_FIRST;
1757 					filter_selected();
1758 					stop = true;
1759 				}
1760 			}
1761 			break;
1762 
1763 		// if we are hovering over a valid item, fake a UI_SELECT with a double-click
1764 		case ui_event::type::MOUSE_DOUBLE_CLICK:
1765 			if (hover() >= 0 && hover() < item_count())
1766 			{
1767 				set_selected_index(hover());
1768 				ev.iptkey = IPT_UI_SELECT;
1769 			}
1770 
1771 			if (is_last_selected())
1772 			{
1773 				ev.iptkey = IPT_UI_CANCEL;
1774 				stack_pop();
1775 			}
1776 			stop = true;
1777 			break;
1778 
1779 		// caught scroll event
1780 		case ui_event::type::MOUSE_WHEEL:
1781 			if (hover() >= 0 && hover() < item_count() - skip_main_items - 1)
1782 			{
1783 				if (local_menu_event.zdelta > 0)
1784 				{
1785 					if (selected_index() >= m_available_items || is_first_selected() || m_ui_error)
1786 						break;
1787 					set_selected_index(selected_index() - local_menu_event.num_lines);
1788 					if (selected_index() < top_line + (top_line != 0))
1789 						top_line -= local_menu_event.num_lines;
1790 				}
1791 				else
1792 				{
1793 					if (selected_index() >= m_available_items - 1 || m_ui_error)
1794 						break;
1795 					set_selected_index(std::min(selected_index() + local_menu_event.num_lines, m_available_items - 1));
1796 					if (selected_index() >= top_line + m_visible_items + (top_line != 0))
1797 						top_line += local_menu_event.num_lines;
1798 				}
1799 			}
1800 			else if (hover() == HOVER_INFO_TEXT)
1801 			{
1802 				if (local_menu_event.zdelta > 0)
1803 					m_topline_datsview -= local_menu_event.num_lines;
1804 				else
1805 					m_topline_datsview += local_menu_event.num_lines;
1806 			}
1807 			break;
1808 
1809 		// translate CHAR events into specials
1810 		case ui_event::type::IME_CHAR:
1811 			if (exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_NEXT, 0) || exclusive_input_pressed(ev.iptkey, IPT_UI_FOCUS_PREV, 0))
1812 			{
1813 				stop = true;
1814 			}
1815 			else if (m_ui_error)
1816 			{
1817 				ev.iptkey = IPT_SPECIAL;
1818 				stop = true;
1819 			}
1820 			else if (accept_search())
1821 			{
1822 				if (input_character(m_search, local_menu_event.ch, uchar_is_printable))
1823 					search_changed = true;
1824 			}
1825 			break;
1826 
1827 		case ui_event::type::MOUSE_RDOWN:
1828 			if (hover() >= 0 && hover() < item_count() - skip_main_items - 1)
1829 			{
1830 				set_selected_index(hover());
1831 				m_prev_selected = get_selection_ref();
1832 				m_focus = focused_menu::MAIN;
1833 				ev.iptkey = IPT_CUSTOM;
1834 				ev.mouse.x0 = local_menu_event.mouse_x;
1835 				ev.mouse.y0 = local_menu_event.mouse_y;
1836 				stop = true;
1837 			}
1838 			break;
1839 
1840 		// ignore everything else
1841 		default:
1842 			break;
1843 		}
1844 
1845 		// need to update search before processing certain kinds of events, but others don't matter
1846 		if (search_changed)
1847 		{
1848 			switch (machine().ui_input().peek_event_type())
1849 			{
1850 			case ui_event::type::MOUSE_DOWN:
1851 			case ui_event::type::MOUSE_RDOWN:
1852 			case ui_event::type::MOUSE_DOUBLE_CLICK:
1853 			case ui_event::type::MOUSE_WHEEL:
1854 				stop = true;
1855 				break;
1856 			case ui_event::type::NONE:
1857 			case ui_event::type::MOUSE_MOVE:
1858 			case ui_event::type::MOUSE_LEAVE:
1859 			case ui_event::type::MOUSE_UP:
1860 			case ui_event::type::MOUSE_RUP:
1861 			case ui_event::type::IME_CHAR:
1862 				break;
1863 			}
1864 		}
1865 	}
1866 	if (search_changed)
1867 		reset(reset_options::SELECT_FIRST);
1868 }
1869 
1870 
1871 //-------------------------------------------------
1872 //  draw main menu
1873 //-------------------------------------------------
1874 
draw(uint32_t flags)1875 void menu_select_launch::draw(uint32_t flags)
1876 {
1877 	bool noinput = (flags & PROCESS_NOINPUT);
1878 	float const aspect = machine().render().ui_aspect(&container());
1879 	float const lr_border = ui().box_lr_border() * aspect;
1880 	float line_height = ui().get_line_height();
1881 	float const ud_arrow_width = line_height * aspect;
1882 	float const gutter_width = 0.52f * ud_arrow_width;
1883 	float const icon_offset = m_has_icons ? (1.5f * ud_arrow_width) : 0.0f;
1884 	float right_panel_size = (ui_globals::panels_status == HIDE_BOTH || ui_globals::panels_status == HIDE_RIGHT_PANEL) ? 2.0f * lr_border : 0.3f;
1885 	float visible_width = 1.0f - 4.0f * lr_border;
1886 	float primary_left = (1.0f - visible_width) * 0.5f;
1887 	float primary_width = visible_width;
1888 
1889 	draw_background();
1890 
1891 	clear_hover();
1892 	m_available_items = (m_is_swlist) ? item_count() - 2 : item_count() - 2 - skip_main_items;
1893 	float extra_height = (m_is_swlist) ? 2.0f * line_height : (2.0f + skip_main_items) * line_height;
1894 	float visible_extra_menu_height = get_customtop() + get_custombottom() + extra_height;
1895 
1896 	// locate mouse
1897 	if (noinput)
1898 		ignore_mouse();
1899 	else
1900 		map_mouse();
1901 
1902 	// account for extra space at the top and bottom
1903 	float visible_main_menu_height = 1.0f - 2.0f * ui().box_tb_border() - visible_extra_menu_height;
1904 	m_visible_lines = int(std::trunc(visible_main_menu_height / line_height));
1905 	visible_main_menu_height = float(m_visible_lines) * line_height;
1906 
1907 	if (!m_is_swlist)
1908 		ui_globals::visible_main_lines = m_visible_lines;
1909 	else
1910 		ui_globals::visible_sw_lines = m_visible_lines;
1911 
1912 	// compute top/left of inner menu area by centering
1913 	float visible_left = primary_left;
1914 	float visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
1915 
1916 	// if the menu is at the bottom of the extra, adjust
1917 	visible_top += get_customtop();
1918 
1919 	// compute left box size
1920 	float x1 = visible_left - lr_border;
1921 	float y1 = visible_top - ui().box_tb_border();
1922 	float x2 = x1 + 2.0f * lr_border;
1923 	float y2 = visible_top + visible_main_menu_height + ui().box_tb_border() + extra_height;
1924 
1925 	// add left box
1926 	visible_left = draw_left_panel(x1, y1, x2, y2);
1927 	visible_width -= right_panel_size + visible_left - 2.0f * lr_border;
1928 
1929 	// compute and add main box
1930 	x1 = visible_left - lr_border;
1931 	x2 = visible_left + visible_width + lr_border;
1932 	float line = visible_top + (float(m_visible_lines) * line_height);
1933 	ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
1934 
1935 	if (m_available_items < m_visible_lines)
1936 		m_visible_lines = m_available_items;
1937 	if (top_line < 0 || is_first_selected())
1938 		top_line = 0;
1939 	if (selected_index() < m_available_items && top_line + m_visible_lines >= m_available_items)
1940 		top_line = m_available_items - m_visible_lines;
1941 
1942 	// determine effective positions taking into account the hilighting arrows
1943 	float effective_width = visible_width - 2.0f * gutter_width;
1944 	float effective_left = visible_left + gutter_width;
1945 
1946 	if ((m_focus == focused_menu::MAIN) && (selected_index() < m_available_items))
1947 		m_prev_selected = nullptr;
1948 
1949 	int const n_loop = (std::min)(m_visible_lines, m_available_items);
1950 	for (int linenum = 0; linenum < n_loop; linenum++)
1951 	{
1952 		float line_y = visible_top + (float)linenum * line_height;
1953 		int itemnum = top_line + linenum;
1954 		const menu_item &pitem = item(itemnum);
1955 		const char *itemtext = pitem.text.c_str();
1956 		rgb_t fgcolor = ui().colors().text_color();
1957 		rgb_t bgcolor = ui().colors().text_bg_color();
1958 		rgb_t fgcolor3 = ui().colors().clone_color();
1959 		float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
1960 		float line_y0 = line_y;
1961 		float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
1962 		float line_y1 = line_y + line_height;
1963 
1964 		// set the hover if this is our item
1965 		if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
1966 			set_hover(itemnum);
1967 
1968 		if (is_selected(itemnum) && m_focus == focused_menu::MAIN)
1969 		{
1970 			// if we're selected, draw with a different background
1971 			fgcolor = rgb_t(0xff, 0xff, 0x00);
1972 			bgcolor = rgb_t(0xff, 0xff, 0xff);
1973 			fgcolor3 = rgb_t(0xcc, 0xcc, 0x00);
1974 			ui().draw_textured_box(
1975 					container(),
1976 					line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1,
1977 					bgcolor, rgb_t(43, 43, 43),
1978 					hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
1979 		}
1980 		else if (itemnum == hover())
1981 		{
1982 			// else if the mouse is over this item, draw with a different background
1983 			fgcolor = fgcolor3 = ui().options().mouseover_color();
1984 			bgcolor = ui().colors().mouseover_bg_color();
1985 			highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
1986 		}
1987 		else if (pitem.ref == m_prev_selected)
1988 		{
1989 			fgcolor = fgcolor3 = ui().options().mouseover_color();
1990 			bgcolor = ui().colors().mouseover_bg_color();
1991 			ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
1992 					hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
1993 		}
1994 
1995 		if (linenum == 0 && top_line != 0)
1996 		{
1997 			// if we're on the top line, display the up arrow
1998 			draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
1999 				0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0);
2000 
2001 			if (hover() == itemnum)
2002 				set_hover(HOVER_ARROW_UP);
2003 		}
2004 		else if (linenum == m_visible_lines - 1 && itemnum != m_available_items - 1)
2005 		{
2006 			// if we're on the bottom line, display the down arrow
2007 			draw_arrow(0.5f * (x1 + x2) - 0.5f * ud_arrow_width, line_y + 0.25f * line_height,
2008 				0.5f * (x1 + x2) + 0.5f * ud_arrow_width, line_y + 0.75f * line_height, fgcolor, ROT0 ^ ORIENTATION_FLIP_Y);
2009 
2010 			if (hover() == itemnum)
2011 				set_hover(HOVER_ARROW_DOWN);
2012 		}
2013 		else if (pitem.type == menu_item_type::SEPARATOR)
2014 		{
2015 			// if we're just a divider, draw a line
2016 			container().add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height,
2017 					UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2018 		}
2019 		else if (pitem.subtext.empty())
2020 		{
2021 			// draw the item centered
2022 			int const item_invert = pitem.flags & FLAG_INVERT;
2023 			if (m_has_icons)
2024 				draw_icon(linenum, item(itemnum).ref, effective_left, line_y);
2025 			ui().draw_text_full(
2026 					container(),
2027 					itemtext,
2028 					effective_left + icon_offset, line_y, effective_width - icon_offset,
2029 					ui::text_layout::LEFT, ui::text_layout::TRUNCATE,
2030 					mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
2031 					nullptr, nullptr);
2032 		}
2033 		else
2034 		{
2035 			int const item_invert = pitem.flags & FLAG_INVERT;
2036 			const char *subitem_text = pitem.subtext.c_str();
2037 			float item_width, subitem_width;
2038 
2039 			// compute right space for subitem
2040 			ui().draw_text_full(
2041 					container(),
2042 					subitem_text,
2043 					effective_left + icon_offset, line_y, ui().get_string_width(pitem.subtext.c_str()),
2044 					ui::text_layout::RIGHT, ui::text_layout::NEVER,
2045 					mame_ui_manager::NONE, item_invert ? fgcolor3 : fgcolor, bgcolor,
2046 					&subitem_width, nullptr);
2047 			subitem_width += gutter_width;
2048 
2049 			// draw the item left-justified
2050 			if (m_has_icons)
2051 				draw_icon(linenum, item(itemnum).ref, effective_left, line_y);
2052 			ui().draw_text_full(
2053 					container(),
2054 					itemtext,
2055 					effective_left + icon_offset, line_y, effective_width - icon_offset - subitem_width,
2056 					ui::text_layout::LEFT, ui::text_layout::TRUNCATE,
2057 					mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
2058 					&item_width, nullptr);
2059 
2060 			// draw the subitem right-justified
2061 			ui().draw_text_full(
2062 					container(),
2063 					subitem_text,
2064 					effective_left + icon_offset + item_width, line_y, effective_width - icon_offset - item_width,
2065 					ui::text_layout::RIGHT, ui::text_layout::NEVER,
2066 					mame_ui_manager::NORMAL, item_invert ? fgcolor3 : fgcolor, bgcolor,
2067 					nullptr, nullptr);
2068 		}
2069 	}
2070 
2071 	for (size_t count = m_available_items; count < item_count(); count++)
2072 	{
2073 		const menu_item &pitem = item(count);
2074 		const char *itemtext = pitem.text.c_str();
2075 		float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
2076 		float line_y0 = line;
2077 		float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
2078 		float line_y1 = line + line_height;
2079 		rgb_t fgcolor = ui().colors().text_color();
2080 		rgb_t bgcolor = ui().colors().text_bg_color();
2081 
2082 		if (mouse_in_rect(line_x0, line_y0, line_x1, line_y1) && is_selectable(pitem))
2083 			set_hover(count);
2084 
2085 		// if we're selected, draw with a different background
2086 		if (is_selected(count) && m_focus == focused_menu::MAIN)
2087 		{
2088 			fgcolor = rgb_t(0xff, 0xff, 0x00);
2089 			bgcolor = rgb_t(0xff, 0xff, 0xff);
2090 			ui().draw_textured_box(container(), line_x0 + 0.01f, line_y0, line_x1 - 0.01f, line_y1, bgcolor, rgb_t(43, 43, 43),
2091 					hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
2092 		}
2093 		// else if the mouse is over this item, draw with a different background
2094 		else if (count == hover())
2095 		{
2096 			fgcolor = ui().options().mouseover_color();
2097 			bgcolor = ui().colors().mouseover_bg_color();
2098 			highlight(line_x0, line_y0, line_x1, line_y1, bgcolor);
2099 		}
2100 
2101 		if (pitem.type == menu_item_type::SEPARATOR)
2102 		{
2103 			container().add_line(visible_left, line + 0.5f * line_height, visible_left + visible_width, line + 0.5f * line_height,
2104 					UI_LINE_WIDTH, ui().colors().text_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2105 		}
2106 		else
2107 		{
2108 			ui().draw_text_full(container(), itemtext, effective_left, line, effective_width, ui::text_layout::CENTER, ui::text_layout::TRUNCATE,
2109 					mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr);
2110 		}
2111 		line += line_height;
2112 	}
2113 
2114 	x1 = x2;
2115 	x2 += right_panel_size;
2116 
2117 	draw_right_panel(x1, y1, x2, y2);
2118 
2119 	x1 = primary_left - lr_border;
2120 	x2 = primary_left + primary_width + lr_border;
2121 
2122 	// if there is something special to add, do it by calling the virtual method
2123 	custom_render(get_selection_ref(), get_customtop(), get_custombottom(), x1, y1, x2, y2);
2124 
2125 	// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
2126 	m_visible_items = m_visible_lines - (top_line != 0) - (top_line + m_visible_lines != m_available_items);
2127 
2128 	// noinput
2129 	if (noinput)
2130 	{
2131 		int alpha = (1.0f - machine().options().pause_brightness()) * 255.0f;
2132 		if (alpha > 255)
2133 			alpha = 255;
2134 		if (alpha >= 0)
2135 			container().add_rect(0.0f, 0.0f, 1.0f, 1.0f, rgb_t(alpha, 0x00, 0x00, 0x00), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2136 	}
2137 }
2138 
2139 
2140 //-------------------------------------------------
2141 //  draw right panel
2142 //-------------------------------------------------
2143 
draw_right_panel(float origx1,float origy1,float origx2,float origy2)2144 void menu_select_launch::draw_right_panel(float origx1, float origy1, float origx2, float origy2)
2145 {
2146 	float const aspect(machine().render().ui_aspect(&container()));
2147 	bool const hide((ui_globals::panels_status == HIDE_RIGHT_PANEL) || (ui_globals::panels_status == HIDE_BOTH));
2148 	float const x2(hide ? origx2 : (origx1 + 2.0f * ui().box_lr_border() * aspect));
2149 	float const space(x2 - origx1);
2150 	float const lr_arrow_width(0.4f * space * aspect);
2151 
2152 	// set left-right arrows dimension
2153 	float const ar_x0(0.5f * (x2 + origx1) - 0.5f * lr_arrow_width);
2154 	float const ar_y0(0.5f * (origy2 + origy1) + 0.1f * space);
2155 	float const ar_x1(ar_x0 + lr_arrow_width);
2156 	float const ar_y1(0.5f * (origy2 + origy1) + 0.9f * space);
2157 
2158 	ui().draw_outlined_box(container(), origx1, origy1, origx2, origy2, rgb_t(0xEF, 0x12, 0x47, 0x7B));
2159 
2160 	rgb_t fgcolor(ui().colors().text_color());
2161 	if (mouse_in_rect(origx1, origy1, x2, origy2))
2162 	{
2163 		fgcolor = ui().options().mouseover_color();
2164 		set_hover(HOVER_RPANEL_ARROW);
2165 	}
2166 
2167 	if (hide)
2168 	{
2169 		draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90 ^ ORIENTATION_FLIP_X);
2170 		return;
2171 	}
2172 
2173 	draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
2174 	origy1 = draw_right_box_title(x2, origy1, origx2, origy2);
2175 
2176 	if (ui_globals::rpanel == RP_IMAGES)
2177 		arts_render(x2, origy1, origx2, origy2);
2178 	else
2179 		infos_render(x2, origy1, origx2, origy2);
2180 }
2181 
2182 
2183 //-------------------------------------------------
2184 //  draw right box title
2185 //-------------------------------------------------
2186 
draw_right_box_title(float x1,float y1,float x2,float y2)2187 float menu_select_launch::draw_right_box_title(float x1, float y1, float x2, float y2)
2188 {
2189 	auto line_height = ui().get_line_height();
2190 	float const midl = (x2 - x1) * 0.5f;
2191 
2192 	// add outlined box for options
2193 	ui().draw_outlined_box(container(), x1, y1, x2, y2, ui().colors().background_color());
2194 
2195 	// add separator line
2196 	container().add_line(x1 + midl, y1, x1 + midl, y1 + line_height, UI_LINE_WIDTH, ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2197 
2198 	std::string buffer[RP_LAST + 1];
2199 	buffer[RP_IMAGES] = _("Images");
2200 	buffer[RP_INFOS] = _("Infos");
2201 
2202 	// check size
2203 	float text_size = 1.0f;
2204 	for (auto & elem : buffer)
2205 	{
2206 		auto textlen = ui().get_string_width(elem.c_str()) + 0.01f;
2207 		float tmp_size = (textlen > midl) ? (midl / textlen) : 1.0f;
2208 		text_size = std::min(text_size, tmp_size);
2209 	}
2210 
2211 	for (int cells = RP_FIRST; cells <= RP_LAST; ++cells)
2212 	{
2213 		rgb_t bgcolor = ui().colors().text_bg_color();
2214 		rgb_t fgcolor = ui().colors().text_color();
2215 
2216 		if (mouse_in_rect(x1, y1, x1 + midl, y1 + line_height))
2217 		{
2218 			if (ui_globals::rpanel != cells)
2219 			{
2220 				bgcolor = ui().colors().mouseover_bg_color();
2221 				fgcolor = ui().options().mouseover_color();
2222 				set_hover(HOVER_RP_FIRST + cells);
2223 			}
2224 		}
2225 
2226 		if (ui_globals::rpanel != cells)
2227 		{
2228 			container().add_line(x1, y1 + line_height, x1 + midl, y1 + line_height, UI_LINE_WIDTH,
2229 					ui().colors().border_color(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2230 			if (fgcolor != ui().colors().mouseover_color())
2231 				fgcolor = ui().colors().clone_color();
2232 		}
2233 
2234 		if (m_focus == focused_menu::RIGHTTOP && ui_globals::rpanel == cells)
2235 		{
2236 			fgcolor = rgb_t(0xff, 0xff, 0x00);
2237 			bgcolor = rgb_t(0xff, 0xff, 0xff);
2238 			ui().draw_textured_box(container(), x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height,
2239 					bgcolor, rgb_t(43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
2240 		}
2241 		else if (bgcolor == ui().colors().mouseover_bg_color())
2242 		{
2243 			container().add_rect(x1 + UI_LINE_WIDTH, y1 + UI_LINE_WIDTH, x1 + midl - UI_LINE_WIDTH, y1 + line_height,
2244 					bgcolor, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
2245 		}
2246 
2247 		ui().draw_text_full(container(), buffer[cells].c_str(), x1 + UI_LINE_WIDTH, y1, midl - UI_LINE_WIDTH,
2248 				ui::text_layout::CENTER, ui::text_layout::NEVER, mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, text_size);
2249 		x1 += midl;
2250 	}
2251 
2252 	return (y1 + line_height + UI_LINE_WIDTH);
2253 }
2254 
2255 
2256 //-------------------------------------------------
2257 //  perform our special rendering
2258 //-------------------------------------------------
2259 
arts_render(float origx1,float origy1,float origx2,float origy2)2260 void menu_select_launch::arts_render(float origx1, float origy1, float origx2, float origy2)
2261 {
2262 	ui_software_info const *software;
2263 	game_driver const *driver;
2264 	get_selection(software, driver);
2265 
2266 	if (software && (!software->startempty || !driver))
2267 	{
2268 		m_cache->set_snapx_driver(nullptr);
2269 
2270 		if (m_default_image)
2271 			m_image_view = (software->startempty == 0) ? SNAPSHOT_VIEW : CABINETS_VIEW;
2272 
2273 		// arts title and searchpath
2274 		std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2);
2275 
2276 		// loads the image if necessary
2277 		if (!m_cache->snapx_software_is(software) || !snapx_valid() || m_switch_image)
2278 		{
2279 			emu_file snapfile(searchstr.c_str(), OPEN_FLAG_READ);
2280 			bitmap_argb32 tmp_bitmap;
2281 
2282 			if (software->startempty == 1)
2283 			{
2284 				// Load driver snapshot
2285 				load_driver_image(tmp_bitmap, snapfile, *software->driver);
2286 			}
2287 			else
2288 			{
2289 				// First attempt from name list
2290 				load_image(tmp_bitmap, snapfile, software->listname + PATH_SEPARATOR + software->shortname);
2291 
2292 				// Second attempt from driver name + part name
2293 				if (!tmp_bitmap.valid())
2294 					load_image(tmp_bitmap, snapfile, software->driver->name + software->part + PATH_SEPARATOR + software->shortname);
2295 			}
2296 
2297 			m_cache->set_snapx_software(software);
2298 			m_switch_image = false;
2299 			arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2);
2300 		}
2301 
2302 		// if the image is available, loaded and valid, display it
2303 		draw_snapx(origx1, origy1, origx2, origy2);
2304 	}
2305 	else if (driver)
2306 	{
2307 		m_cache->set_snapx_software(nullptr);
2308 
2309 		if (m_default_image)
2310 			m_image_view = ((driver->flags & machine_flags::MASK_TYPE) != machine_flags::TYPE_ARCADE) ? CABINETS_VIEW : SNAPSHOT_VIEW;
2311 
2312 		std::string const searchstr = arts_render_common(origx1, origy1, origx2, origy2);
2313 
2314 		// loads the image if necessary
2315 		if (!m_cache->snapx_driver_is(driver) || !snapx_valid() || m_switch_image)
2316 		{
2317 			emu_file snapfile(searchstr, OPEN_FLAG_READ);
2318 			bitmap_argb32 tmp_bitmap;
2319 			load_driver_image(tmp_bitmap, snapfile, *driver);
2320 
2321 			m_cache->set_snapx_driver(driver);
2322 			m_switch_image = false;
2323 			arts_render_images(std::move(tmp_bitmap), origx1, origy1, origx2, origy2);
2324 		}
2325 
2326 		// if the image is available, loaded and valid, display it
2327 		draw_snapx(origx1, origy1, origx2, origy2);
2328 	}
2329 }
2330 
2331 
2332 //-------------------------------------------------
2333 //  common function for images render
2334 //-------------------------------------------------
2335 
arts_render_common(float origx1,float origy1,float origx2,float origy2)2336 std::string menu_select_launch::arts_render_common(float origx1, float origy1, float origx2, float origy2)
2337 {
2338 	float const line_height = ui().get_line_height();
2339 	float const gutter_width = 0.4f * line_height * machine().render().ui_aspect(&container()) * 1.3f;
2340 
2341 	std::string snaptext, searchstr;
2342 	get_title_search(snaptext, searchstr);
2343 
2344 	// apply title to right panel
2345 	float title_size = 0.0f;
2346 	for (int x = FIRST_VIEW; x < LAST_VIEW; x++)
2347 	{
2348 		float text_length;
2349 		ui().draw_text_full(container(),
2350 				_(arts_info[x].first), origx1, origy1, origx2 - origx1,
2351 				ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NONE, rgb_t::white(), rgb_t::black(),
2352 				&text_length, nullptr);
2353 		title_size = (std::max)(text_length + 0.01f, title_size);
2354 	}
2355 
2356 	rgb_t const fgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0x00) : ui().colors().text_color();
2357 	rgb_t const bgcolor = (m_focus == focused_menu::RIGHTBOTTOM) ? rgb_t(0xff, 0xff, 0xff) : ui().colors().text_bg_color();
2358 	float const middle = origx2 - origx1;
2359 
2360 	// check size
2361 	float const sc = title_size + 2.0f * gutter_width;
2362 	float const tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f;
2363 	title_size *= tmp_size;
2364 
2365 	if (bgcolor != ui().colors().text_bg_color())
2366 	{
2367 		ui().draw_textured_box(
2368 				container(),
2369 				origx1 + ((middle - title_size) * 0.5f), origy1 + ui().box_tb_border(),
2370 				origx1 + ((middle + title_size) * 0.5f), origy1 + ui().box_tb_border() + line_height,
2371 				bgcolor, rgb_t(43, 43, 43),
2372 				hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
2373 	}
2374 
2375 	ui().draw_text_full(container(),
2376 			snaptext.c_str(), origx1, origy1 + ui().box_tb_border(), origx2 - origx1,
2377 			ui::text_layout::CENTER, ui::text_layout::TRUNCATE, mame_ui_manager::NORMAL, fgcolor, bgcolor,
2378 			nullptr, nullptr, tmp_size);
2379 
2380 	draw_common_arrow(origx1, origy1 + ui().box_tb_border(), origx2, origy2, m_image_view, FIRST_VIEW, LAST_VIEW, title_size);
2381 
2382 	return searchstr;
2383 }
2384 
2385 
2386 //-------------------------------------------------
2387 //  perform rendering of image
2388 //-------------------------------------------------
2389 
arts_render_images(bitmap_argb32 && tmp_bitmap,float origx1,float origy1,float origx2,float origy2)2390 void menu_select_launch::arts_render_images(bitmap_argb32 &&tmp_bitmap, float origx1, float origy1, float origx2, float origy2)
2391 {
2392 	bool no_available = false;
2393 	float line_height = ui().get_line_height();
2394 
2395 	// if it fails, use the default image
2396 	if (!tmp_bitmap.valid())
2397 	{
2398 		tmp_bitmap.allocate(256, 256);
2399 		const bitmap_argb32 &src(m_cache->no_avail_bitmap());
2400 		for (int x = 0; x < 256; x++)
2401 		{
2402 			for (int y = 0; y < 256; y++)
2403 				tmp_bitmap.pix(y, x) = src.pix(y, x);
2404 		}
2405 		no_available = true;
2406 	}
2407 
2408 	bitmap_argb32 &snapx_bitmap(m_cache->snapx_bitmap());
2409 	if (tmp_bitmap.valid())
2410 	{
2411 		float panel_width = origx2 - origx1 - 0.02f;
2412 		float panel_height = origy2 - origy1 - 0.02f - (3.0f * ui().box_tb_border()) - (2.0f * line_height);
2413 		int screen_width = machine().render().ui_target().width();
2414 		int screen_height = machine().render().ui_target().height();
2415 
2416 		if (machine().render().ui_target().orientation() & ORIENTATION_SWAP_XY)
2417 			std::swap(screen_height, screen_width);
2418 
2419 		int panel_width_pixel = panel_width * screen_width;
2420 		int panel_height_pixel = panel_height * screen_height;
2421 
2422 		// Calculate resize ratios for resizing
2423 		auto ratioW = (float)panel_width_pixel / tmp_bitmap.width();
2424 		auto ratioH = (float)panel_height_pixel / tmp_bitmap.height();
2425 		auto ratioI = (float)tmp_bitmap.height() / tmp_bitmap.width();
2426 		auto dest_xPixel = tmp_bitmap.width();
2427 		auto dest_yPixel = tmp_bitmap.height();
2428 
2429 		// force 4:3 ratio min
2430 		if (ui().options().forced_4x3_snapshot() && ratioI < 0.75f && m_image_view == SNAPSHOT_VIEW)
2431 		{
2432 			// smaller ratio will ensure that the image fits in the view
2433 			dest_yPixel = tmp_bitmap.width() * 0.75f;
2434 			ratioH = (float)panel_height_pixel / dest_yPixel;
2435 			float ratio = std::min(ratioW, ratioH);
2436 			dest_xPixel = tmp_bitmap.width() * ratio;
2437 			dest_yPixel *= ratio;
2438 		}
2439 		// resize the bitmap if necessary
2440 		else if (ratioW < 1 || ratioH < 1 || (ui().options().enlarge_snaps() && !no_available))
2441 		{
2442 			// smaller ratio will ensure that the image fits in the view
2443 			float ratio = std::min(ratioW, ratioH);
2444 			dest_xPixel = tmp_bitmap.width() * ratio;
2445 			dest_yPixel = tmp_bitmap.height() * ratio;
2446 		}
2447 
2448 		bitmap_argb32 dest_bitmap;
2449 
2450 		// resample if necessary
2451 		if (dest_xPixel != tmp_bitmap.width() || dest_yPixel != tmp_bitmap.height())
2452 		{
2453 			dest_bitmap.allocate(dest_xPixel, dest_yPixel);
2454 			render_color color = { 1.0f, 1.0f, 1.0f, 1.0f };
2455 			render_resample_argb_bitmap_hq(dest_bitmap, tmp_bitmap, color, true);
2456 		}
2457 		else
2458 			dest_bitmap = std::move(tmp_bitmap);
2459 
2460 		snapx_bitmap.allocate(panel_width_pixel, panel_height_pixel);
2461 		int x1 = (0.5f * panel_width_pixel) - (0.5f * dest_xPixel);
2462 		int y1 = (0.5f * panel_height_pixel) - (0.5f * dest_yPixel);
2463 
2464 		for (int x = 0; x < dest_xPixel; x++)
2465 			for (int y = 0; y < dest_yPixel; y++)
2466 				snapx_bitmap.pix(y + y1, x + x1) = dest_bitmap.pix(y, x);
2467 
2468 		// apply bitmap
2469 		m_cache->snapx_texture()->set_bitmap(snapx_bitmap, snapx_bitmap.cliprect(), TEXFORMAT_ARGB32);
2470 	}
2471 	else
2472 	{
2473 		snapx_bitmap.reset();
2474 	}
2475 }
2476 
2477 
2478 //-------------------------------------------------
2479 //  draw snapshot
2480 //-------------------------------------------------
2481 
draw_snapx(float origx1,float origy1,float origx2,float origy2)2482 void menu_select_launch::draw_snapx(float origx1, float origy1, float origx2, float origy2)
2483 {
2484 	// if the image is available, loaded and valid, display it
2485 	if (snapx_valid())
2486 	{
2487 		float const line_height = ui().get_line_height();
2488 		float const x1 = origx1 + 0.01f;
2489 		float const x2 = origx2 - 0.01f;
2490 		float const y1 = origy1 + (2.0f * ui().box_tb_border()) + line_height;
2491 		float const y2 = origy2 - ui().box_tb_border() - line_height;
2492 
2493 		// apply texture
2494 		container().add_quad(x1, y1, x2, y2, rgb_t::white(), m_cache->snapx_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2495 	}
2496 }
2497 
2498 
make_audit_fail_text(bool found,media_auditor const & auditor)2499 std::string menu_select_launch::make_audit_fail_text(bool found, media_auditor const &auditor)
2500 {
2501 	std::ostringstream str;
2502 	str << _("The selected machine is missing one or more required ROM or CHD images. Please select a different machine.\n\n");
2503 	if (found)
2504 	{
2505 		auditor.summarize(nullptr, &str);
2506 		str << "\n";
2507 	}
2508 	str << _("Press any key to continue.");
2509 	return str.str();
2510 }
2511 
2512 
2513 //-------------------------------------------------
2514 //  get bios count
2515 //-------------------------------------------------
2516 
has_multiple_bios(ui_software_info const & swinfo,s_bios & biosname)2517 bool menu_select_launch::has_multiple_bios(ui_software_info const &swinfo, s_bios &biosname)
2518 {
2519 	return has_multiple_bios(*swinfo.driver, biosname);
2520 }
2521 
has_multiple_bios(game_driver const & driver,s_bios & biosname)2522 bool menu_select_launch::has_multiple_bios(game_driver const &driver, s_bios &biosname)
2523 {
2524 	if (!driver.rom)
2525 		return false;
2526 
2527 	char const *default_name(nullptr);
2528 	for (tiny_rom_entry const *rom = driver.rom; !ROMENTRY_ISEND(rom); ++rom)
2529 	{
2530 		if (ROMENTRY_ISDEFAULT_BIOS(rom))
2531 			default_name = rom->name;
2532 	}
2533 
2534 	for (romload::system_bios const &bios : romload::entries(driver.rom).get_system_bioses())
2535 	{
2536 		std::string name(bios.get_description());
2537 		uint32_t const bios_flags(bios.get_value());
2538 
2539 		if (default_name && !std::strcmp(bios.get_name(), default_name))
2540 		{
2541 			name.append(_(" (default)"));
2542 			biosname.emplace(biosname.begin(), std::move(name), bios_flags - 1);
2543 		}
2544 		else
2545 		{
2546 			biosname.emplace_back(std::move(name), bios_flags - 1);
2547 		}
2548 	}
2549 	return biosname.size() > 1U;
2550 }
2551 
2552 
exit(running_machine & machine)2553 void menu_select_launch::exit(running_machine &machine)
2554 {
2555 	std::lock_guard<std::mutex> guard(s_cache_guard);
2556 	s_caches.erase(&machine);
2557 }
2558 
2559 
2560 //-------------------------------------------------
2561 //  draw collapsed left panel
2562 //-------------------------------------------------
2563 
draw_collapsed_left_panel(float x1,float y1,float x2,float y2)2564 float menu_select_launch::draw_collapsed_left_panel(float x1, float y1, float x2, float y2)
2565 {
2566 	float const aspect = machine().render().ui_aspect(&container());
2567 	float const space = x2 - x1;
2568 	float const lr_arrow_width = 0.4f * space * aspect;
2569 
2570 	// set left-right arrows dimension
2571 	float const ar_x0 = 0.5f * (x2 + x1) - (0.5f * lr_arrow_width);
2572 	float const ar_y0 = 0.5f * (y2 + y1) + (0.1f * space);
2573 	float const ar_x1 = ar_x0 + lr_arrow_width;
2574 	float const ar_y1 = 0.5f * (y2 + y1) + (0.9f * space);
2575 
2576 	ui().draw_outlined_box(container(), x1, y1, x2, y2, rgb_t(0xef, 0x12, 0x47, 0x7b)); // FIXME: magic numbers in colour?
2577 
2578 	rgb_t fgcolor = ui().colors().text_color();
2579 	if (mouse_in_rect(x1, y1, x2, y2))
2580 	{
2581 		fgcolor = ui().options().mouseover_color();
2582 		set_hover(HOVER_LPANEL_ARROW);
2583 	}
2584 
2585 	draw_arrow(ar_x0, ar_y0, ar_x1, ar_y1, fgcolor, ROT90);
2586 
2587 	return x2 + ui().box_lr_border() * aspect;
2588 }
2589 
2590 
2591 //-------------------------------------------------
2592 //  draw infos
2593 //-------------------------------------------------
2594 
infos_render(float origx1,float origy1,float origx2,float origy2)2595 void menu_select_launch::infos_render(float origx1, float origy1, float origx2, float origy2)
2596 {
2597 	float const line_height = ui().get_line_height();
2598 	float text_size = ui().options().infos_size();
2599 	std::vector<int> xstart;
2600 	std::vector<int> xend;
2601 	const char *first = "";
2602 	ui_software_info const *software;
2603 	game_driver const *driver;
2604 	int total;
2605 	get_selection(software, driver);
2606 
2607 	if (software && (!software->startempty || !driver))
2608 	{
2609 		m_info_driver = nullptr;
2610 		first = __("Usage");
2611 
2612 		if ((m_info_software != software) || (m_info_view != ui_globals::cur_sw_dats_view))
2613 		{
2614 			m_info_buffer.clear();
2615 			if (software == m_info_software)
2616 			{
2617 				m_info_view = ui_globals::cur_sw_dats_view;
2618 			}
2619 			else
2620 			{
2621 				m_info_view = 0;
2622 				m_info_software = software;
2623 				ui_globals::cur_sw_dats_view = 0;
2624 
2625 				m_items_list.clear();
2626 				mame_machine_manager::instance()->lua()->call_plugin("data_list", std::string(software->shortname).append(1, ',').append(software->listname).c_str(), m_items_list);
2627 				ui_globals::cur_sw_dats_total = m_items_list.size() + 1;
2628 			}
2629 
2630 			if (m_info_view == 0)
2631 			{
2632 				m_info_buffer = software->usage;
2633 			}
2634 			else
2635 			{
2636 				m_info_buffer.clear();
2637 				mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer);
2638 			}
2639 		}
2640 		total = ui_globals::cur_sw_dats_total;
2641 	}
2642 	else if (driver)
2643 	{
2644 		m_info_software = nullptr;
2645 		first = __("General Info");
2646 
2647 		if (driver != m_info_driver || ui_globals::curdats_view != m_info_view)
2648 		{
2649 			m_info_buffer.clear();
2650 			if (driver == m_info_driver)
2651 			{
2652 				m_info_view = ui_globals::curdats_view;
2653 			}
2654 			else
2655 			{
2656 				m_info_driver = driver;
2657 				m_info_view = 0;
2658 				ui_globals::curdats_view = 0;
2659 
2660 				m_items_list.clear();
2661 				mame_machine_manager::instance()->lua()->call_plugin("data_list", driver->name, m_items_list);
2662 				ui_globals::curdats_total = m_items_list.size() + 1;
2663 			}
2664 
2665 			if (m_info_view == 0)
2666 				general_info(driver, m_info_buffer);
2667 			else
2668 			{
2669 				m_info_buffer = "";
2670 				mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer);
2671 			}
2672 		}
2673 		total = ui_globals::curdats_total;
2674 	}
2675 	else
2676 	{
2677 		return;
2678 	}
2679 
2680 	origy1 += ui().box_tb_border();
2681 	float const aspect(machine().render().ui_aspect(&container()));
2682 	float const gutter_width = 0.4f * line_height * aspect * 1.3f;
2683 	float const ud_arrow_width = line_height * aspect;
2684 	float oy1 = origy1 + line_height;
2685 
2686 	char const *const snaptext(m_info_view ? m_items_list[m_info_view - 1].c_str() : _(first));
2687 
2688 	// get width of widest title
2689 	float title_size(0.0f);
2690 	for (std::size_t x = 0; total > x; ++x)
2691 	{
2692 		char const *const name(x ? m_items_list[x - 1].c_str() : _(first));
2693 		float txt_length(0.0f);
2694 		ui().draw_text_full(
2695 				container(), name,
2696 				origx1, origy1, origx2 - origx1,
2697 				ui::text_layout::CENTER, ui::text_layout::NEVER,
2698 				mame_ui_manager::NONE, ui().colors().text_color(), ui().colors().text_bg_color(),
2699 				&txt_length, nullptr);
2700 		txt_length += 0.01f;
2701 		title_size = (std::max)(txt_length, title_size);
2702 	}
2703 
2704 	rgb_t fgcolor = ui().colors().text_color();
2705 	rgb_t bgcolor = ui().colors().text_bg_color();
2706 	if (get_focus() == focused_menu::RIGHTBOTTOM)
2707 	{
2708 		fgcolor = rgb_t(0xff, 0xff, 0xff, 0x00);
2709 		bgcolor = rgb_t(0xff, 0xff, 0xff, 0xff);
2710 	}
2711 
2712 	float middle = origx2 - origx1;
2713 
2714 	// check size
2715 	float sc = title_size + 2.0f * gutter_width;
2716 	float tmp_size = (sc > middle) ? ((middle - 2.0f * gutter_width) / sc) : 1.0f;
2717 	title_size *= tmp_size;
2718 
2719 	if (bgcolor != ui().colors().text_bg_color())
2720 	{
2721 		ui().draw_textured_box(container(), origx1 + ((middle - title_size) * 0.5f), origy1, origx1 + ((middle + title_size) * 0.5f),
2722 				origy1 + line_height, bgcolor, rgb_t(255, 43, 43, 43), hilight_main_texture(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(1));
2723 	}
2724 
2725 	ui().draw_text_full(container(), snaptext, origx1, origy1, origx2 - origx1, ui::text_layout::CENTER,
2726 			ui::text_layout::NEVER, mame_ui_manager::NORMAL, fgcolor, bgcolor, nullptr, nullptr, tmp_size);
2727 
2728 	char justify = 'l'; // left justify
2729 	if ((m_info_buffer.length() >= 3) && (m_info_buffer[0] == '#'))
2730 	{
2731 		if (m_info_buffer[1] == 'j')
2732 			justify = m_info_buffer[2];
2733 	}
2734 
2735 	draw_common_arrow(origx1, origy1, origx2, origy2, m_info_view, 0, total - 1, title_size);
2736 	if (justify == 'f')
2737 	{
2738 		m_total_lines = ui().wrap_text(
2739 				container(), m_info_buffer.c_str(),
2740 				0.0f, 0.0f, 1.0f - (2.0f * gutter_width),
2741 				xstart, xend,
2742 				text_size);
2743 	}
2744 	else
2745 	{
2746 		m_total_lines = ui().wrap_text(
2747 				container(), m_info_buffer.c_str(),
2748 				origx1, origy1, origx2 - origx1 - (2.0f * gutter_width),
2749 				xstart, xend,
2750 				text_size);
2751 	}
2752 
2753 	int r_visible_lines = floor((origy2 - oy1) / (line_height * text_size));
2754 	if (m_total_lines < r_visible_lines)
2755 		r_visible_lines = m_total_lines;
2756 	if (m_topline_datsview < 0)
2757 		m_topline_datsview = 0;
2758 	if (m_topline_datsview + r_visible_lines >= m_total_lines)
2759 		m_topline_datsview = m_total_lines - r_visible_lines;
2760 
2761 	if (mouse_in_rect(origx1 + gutter_width, oy1, origx2 - gutter_width, origy2))
2762 		set_hover(HOVER_INFO_TEXT);
2763 
2764 	sc = origx2 - origx1 - (2.0f * gutter_width);
2765 	for (int r = 0; r < r_visible_lines; ++r)
2766 	{
2767 		int itemline = r + m_topline_datsview;
2768 		std::string const tempbuf(m_info_buffer.substr(xstart[itemline], xend[itemline] - xstart[itemline]));
2769 		if (tempbuf[0] == '#')
2770 			continue;
2771 
2772 		if (r == 0 && m_topline_datsview != 0) // up arrow
2773 		{
2774 			draw_info_arrow(0, origx1, origx2, oy1, line_height, text_size, ud_arrow_width);
2775 		}
2776 		else if (r == r_visible_lines - 1 && itemline != m_total_lines - 1) // bottom arrow
2777 		{
2778 			draw_info_arrow(1, origx1, origx2, oy1, line_height, text_size, ud_arrow_width);
2779 		}
2780 		else if (justify == '2') // two-column layout
2781 		{
2782 			// split at first tab
2783 			std::string::size_type const splitpos(tempbuf.find('\t'));
2784 			std::string const leftcol(tempbuf.substr(0, (std::string::npos == splitpos) ? 0U : splitpos));
2785 			std::string const rightcol(tempbuf.substr((std::string::npos == splitpos) ? 0U : (splitpos + 1U)));
2786 
2787 			// measure space needed, condense if necessary
2788 			float const leftlen(ui().get_string_width(leftcol.c_str(), text_size));
2789 			float const rightlen(ui().get_string_width(rightcol.c_str(), text_size));
2790 			float const textlen(leftlen + rightlen);
2791 			float const tmp_size3((textlen > sc) ? (text_size * (sc / textlen)) : text_size);
2792 
2793 			// draw in two parts
2794 			ui().draw_text_full(
2795 					container(), leftcol.c_str(),
2796 					origx1 + gutter_width, oy1, sc,
2797 					ui::text_layout::LEFT, ui::text_layout::TRUNCATE,
2798 					mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(),
2799 					nullptr, nullptr,
2800 					tmp_size3);
2801 			ui().draw_text_full(
2802 					container(), rightcol.c_str(),
2803 					origx1 + gutter_width, oy1, sc,
2804 					ui::text_layout::RIGHT, ui::text_layout::TRUNCATE,
2805 					mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(),
2806 					nullptr, nullptr,
2807 					tmp_size3);
2808 		}
2809 		else if (justify == 'f' || justify == 'p') // full or partial justify
2810 		{
2811 			// check size
2812 			float const textlen = ui().get_string_width(tempbuf.c_str(), text_size);
2813 			float tmp_size3 = (textlen > sc) ? text_size * (sc / textlen) : text_size;
2814 			ui().draw_text_full(
2815 					container(), tempbuf.c_str(),
2816 					origx1 + gutter_width, oy1, origx2 - origx1,
2817 					ui::text_layout::LEFT, ui::text_layout::TRUNCATE,
2818 					mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(),
2819 					nullptr, nullptr,
2820 					tmp_size3);
2821 		}
2822 		else
2823 		{
2824 			ui().draw_text_full(
2825 					container(), tempbuf.c_str(),
2826 					origx1 + gutter_width, oy1, origx2 - origx1,
2827 					ui::text_layout::LEFT, ui::text_layout::TRUNCATE,
2828 					mame_ui_manager::NORMAL, ui().colors().text_color(), ui().colors().text_bg_color(),
2829 					nullptr, nullptr,
2830 					text_size);
2831 		}
2832 
2833 		oy1 += (line_height * text_size);
2834 	}
2835 	// return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow
2836 	m_right_visible_lines = r_visible_lines - (m_topline_datsview != 0) - (m_topline_datsview + r_visible_lines != m_total_lines);
2837 }
2838 
2839 } // namespace ui
2840