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