summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/uimenu.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/uimenu.c')
-rw-r--r--trunk/src/emu/uimenu.c3989
1 files changed, 3989 insertions, 0 deletions
diff --git a/trunk/src/emu/uimenu.c b/trunk/src/emu/uimenu.c
new file mode 100644
index 00000000000..9c19131b62f
--- /dev/null
+++ b/trunk/src/emu/uimenu.c
@@ -0,0 +1,3989 @@
+/*********************************************************************
+
+ uimenu.c
+
+ Internal MAME menus for the user interface.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*********************************************************************/
+
+#include "emu.h"
+#include "emuopts.h"
+#include "ui.h"
+#include "rendutil.h"
+#include "cheat.h"
+#include "uiimage.h"
+#include "uiinput.h"
+#include "uimenu.h"
+#include "audit.h"
+#include "crsshair.h"
+#include <ctype.h>
+#include "imagedev/cassette.h"
+#include "imagedev/bitbngr.h"
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define UI_MENU_POOL_SIZE 65536
+#define UI_MENU_ALLOC_ITEMS 256
+
+#define VISIBLE_GAMES_IN_LIST 15
+
+#define MAX_PHYSICAL_DIPS 10
+#define MAX_INPUT_PORTS 32
+#define MAX_BITS_PER_PORT 32
+
+/* DIP switch rendering parameters */
+#define DIP_SWITCH_HEIGHT 0.05f
+#define DIP_SWITCH_SPACING 0.01
+#define SINGLE_TOGGLE_SWITCH_FIELD_WIDTH 0.025f
+#define SINGLE_TOGGLE_SWITCH_WIDTH 0.020f
+/* make the switch 80% of the width space and 1/2 of the switch height */
+#define PERCENTAGE_OF_HALF_FIELD_USED 0.80f
+#define SINGLE_TOGGLE_SWITCH_HEIGHT ((DIP_SWITCH_HEIGHT / 2) * PERCENTAGE_OF_HALF_FIELD_USED)
+
+
+enum
+{
+ INPUT_TYPE_DIGITAL = 0,
+ INPUT_TYPE_ANALOG = 1,
+ INPUT_TYPE_ANALOG_DEC = INPUT_TYPE_ANALOG + SEQ_TYPE_DECREMENT,
+ INPUT_TYPE_ANALOG_INC = INPUT_TYPE_ANALOG + SEQ_TYPE_INCREMENT,
+ INPUT_TYPE_TOTAL = INPUT_TYPE_ANALOG + SEQ_TYPE_TOTAL
+};
+
+enum
+{
+ ANALOG_ITEM_KEYSPEED = 0,
+ ANALOG_ITEM_CENTERSPEED,
+ ANALOG_ITEM_REVERSE,
+ ANALOG_ITEM_SENSITIVITY,
+ ANALOG_ITEM_COUNT
+};
+
+enum
+{
+ MEMCARD_ITEM_SELECT = 1,
+ MEMCARD_ITEM_LOAD,
+ MEMCARD_ITEM_EJECT,
+ MEMCARD_ITEM_CREATE
+};
+
+enum
+{
+ VIDEO_ITEM_ROTATE = 0x80000000,
+ VIDEO_ITEM_BACKDROPS,
+ VIDEO_ITEM_OVERLAYS,
+ VIDEO_ITEM_BEZELS,
+ VIDEO_ITEM_CPANELS,
+ VIDEO_ITEM_MARQUEES,
+ VIDEO_ITEM_ZOOM,
+ VIDEO_ITEM_VIEW
+};
+
+
+enum
+{
+ CROSSHAIR_ITEM_VIS = 0,
+ CROSSHAIR_ITEM_PIC,
+ CROSSHAIR_ITEM_AUTO_TIME
+};
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _ui_menu_pool ui_menu_pool;
+struct _ui_menu_pool
+{
+ ui_menu_pool * next; /* chain to next one */
+ UINT8 * top; /* top of the pool */
+ UINT8 * end; /* end of the pool */
+};
+
+
+typedef struct _ui_menu_item ui_menu_item;
+struct _ui_menu_item
+{
+ const char * text;
+ const char * subtext;
+ UINT32 flags;
+ void * ref;
+};
+
+
+class ui_menu
+{
+public:
+ ui_menu(running_machine &machine)
+ : m_machine(machine) { }
+
+ running_machine &machine() const { return m_machine; }
+
+ render_container * container; /* render_container we render to */
+ ui_menu_handler_func handler; /* handler callback */
+ void * parameter; /* parameter */
+ ui_menu_event menu_event; /* the UI menu_event that occurred */
+ ui_menu * parent; /* pointer to parent menu */
+ void * state; /* menu-specific state */
+ ui_menu_destroy_state_func destroy_state; /* destroy state callback */
+ int resetpos; /* reset position */
+ void * resetref; /* reset reference */
+ int selected; /* which item is selected */
+ int hover; /* which item is being hovered over */
+ int visitems; /* number of visible items */
+ int numitems; /* number of items in the menu */
+ int allocitems; /* allocated size of array */
+ ui_menu_item * item; /* pointer to array of items */
+ ui_menu_custom_func custom; /* callback for custom rendering */
+ float customtop; /* amount of extra height to add at the top */
+ float custombottom; /* amount of extra height to add at the bottom */
+ ui_menu_pool * pool; /* list of memory pools */
+
+private:
+ running_machine & m_machine; /* machine we are attached to */
+};
+
+
+/* internal input menu item data */
+typedef struct _input_item_data input_item_data;
+struct _input_item_data
+{
+ input_item_data * next; /* pointer to next item in the list */
+ const void * ref; /* reference to type description for global inputs or field for game inputs */
+ input_seq_type seqtype; /* sequence type */
+ input_seq seq; /* copy of the live sequence */
+ const input_seq * defseq; /* pointer to the default sequence */
+ const char * name; /* pointer to the base name of the item */
+ UINT16 sortorder; /* sorting information */
+ UINT8 type; /* type of port */
+};
+
+
+/* internal analog menu item data */
+typedef struct _analog_item_data analog_item_data;
+struct _analog_item_data
+{
+ input_field_config *field;
+ int type;
+ int min, max;
+ int cur;
+ int defvalue;
+};
+
+
+/* DIP switch descriptor */
+typedef struct _dip_descriptor dip_descriptor;
+struct _dip_descriptor
+{
+ dip_descriptor * next;
+ const char * name;
+ UINT32 mask;
+ UINT32 state;
+};
+
+
+/* extended settings menu state */
+typedef struct _settings_menu_state settings_menu_state;
+struct _settings_menu_state
+{
+ dip_descriptor * diplist;
+};
+
+
+/* extended input menu state */
+typedef struct _input_menu_state input_menu_state;
+struct _input_menu_state
+{
+ UINT16 last_sortorder;
+ const void * pollingref;
+ input_item_data * pollingitem;
+ UINT8 record_next;
+ input_seq starting_seq;
+};
+
+
+/* extended select game menu state */
+typedef struct _select_game_state select_game_state;
+struct _select_game_state
+{
+ UINT8 error;
+ UINT8 rerandomize;
+ char search[40];
+ int matchlist[VISIBLE_GAMES_IN_LIST];
+ const game_driver * driverlist[1];
+};
+
+
+/* internal crosshair menu item data */
+typedef struct _crosshair_item_data crosshair_item_data;
+struct _crosshair_item_data
+{
+ UINT8 type;
+ UINT8 player;
+ UINT8 min, max;
+ UINT8 cur;
+ UINT8 defvalue;
+ char last_name[CROSSHAIR_PIC_NAME_LENGTH + 1];
+ char next_name[CROSSHAIR_PIC_NAME_LENGTH + 1];
+};
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* menu states */
+static ui_menu *menu_stack;
+static ui_menu *menu_free;
+
+static bitmap_t *hilight_bitmap;
+static render_texture *hilight_texture;
+
+static render_texture *arrow_texture;
+
+static const char priortext[] = "Return to Prior Menu";
+static const char backtext[] = "Return to " CAPSTARTGAMENOUN;
+static const char exittext[] = "Exit";
+
+// temporary hack until this is C++-ified
+static driver_enumerator *drivlist;
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void ui_menu_exit(running_machine &machine);
+
+/* internal menu processing */
+static void ui_menu_draw(running_machine &machine, ui_menu *menu, int customonly);
+static void ui_menu_draw_text_box(ui_menu *menu);
+static void ui_menu_handle_events(ui_menu *menu);
+static void ui_menu_handle_keys(ui_menu *menu, UINT32 flags);
+static void ui_menu_validate_selection(ui_menu *menu, int scandir);
+static void ui_menu_clear_free_list(running_machine &machine);
+
+/* menu handlers */
+static void menu_main(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_main_populate(running_machine &machine, ui_menu *menu, void *state);
+static void menu_input_groups(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_input_groups_populate(running_machine &machine, ui_menu *menu, void *state);
+static void menu_input_general(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_input_general_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate, int group);
+static void menu_input_specific(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_input_specific_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate);
+static void menu_input_common(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static int CLIB_DECL menu_input_compare_items(const void *i1, const void *i2);
+static void menu_input_populate_and_sort(running_machine &machine, ui_menu *menu, input_item_data *itemlist, input_menu_state *menustate);
+static void menu_settings_dip_switches(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_settings_driver_config(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_settings_common(running_machine &machine, ui_menu *menu, void *state, UINT32 type);
+static void menu_settings_populate(running_machine &machine, ui_menu *menu, settings_menu_state *menustate, UINT32 type);
+static void menu_analog(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_analog_populate(running_machine &machine, ui_menu *menu);
+static void menu_bookkeeping(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_bookkeeping_populate(running_machine &machine, ui_menu *menu, attotime *curtime);
+static void menu_game_info(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_cheat(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_cheat_populate(running_machine &machine, ui_menu *menu);
+static void menu_memory_card(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_memory_card_populate(running_machine &machine, ui_menu *menu, int cardnum);
+static void menu_sliders(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_sliders_populate(running_machine &machine, ui_menu *menu, int menuless_mode);
+static void menu_sliders_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2);
+static void menu_video_targets(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_video_targets_populate(running_machine &machine, ui_menu *menu);
+static void menu_video_options(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_video_options_populate(running_machine &machine, ui_menu *menu, render_target *target);
+static void menu_crosshair(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_crosshair_populate(running_machine &machine, ui_menu *menu);
+static void menu_quit_game(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_select_game(running_machine &machine, ui_menu *menu, void *parameter, void *state);
+static void menu_select_game_populate(running_machine &machine, ui_menu *menu, select_game_state *menustate);
+static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate);
+static void menu_select_game_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
+
+/* menu helpers */
+static void menu_render_triangle(bitmap_t &dest, const bitmap_t &source, const rectangle &sbounds, void *param);
+static void menu_settings_custom_render_one(render_container *container, float x1, float y1, float x2, float y2, const dip_descriptor *dip, UINT32 selectedmask);
+static void menu_settings_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x, float y, float x2, float y2);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ get_field_default_seq - return a pointer
+ to the default sequence for the given field
+-------------------------------------------------*/
+
+INLINE const input_seq &get_field_default_seq(input_field_config *field, input_seq_type seqtype)
+{
+ if (field->seq[seqtype].is_default())
+ return input_type_seq(field->machine(), field->type, field->player, seqtype);
+ else
+ return field->seq[seqtype];
+}
+
+
+/*-------------------------------------------------
+ toggle_none_default - toggle between "NONE"
+ and the default item
+-------------------------------------------------*/
+
+INLINE void toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq)
+{
+ /* if we used to be "none", toggle to the default value */
+ if (original_seq.length() == 0)
+ selected_seq = selected_defseq;
+
+ /* otherwise, toggle to "none" */
+ else
+ selected_seq.reset();
+}
+
+
+/*-------------------------------------------------
+ item_is_selectable - return TRUE if the given
+ item is selectable
+-------------------------------------------------*/
+
+INLINE int item_is_selectable(const ui_menu_item *item)
+{
+ return ((item->flags & (MENU_FLAG_MULTILINE | MENU_FLAG_DISABLE)) == 0 && strcmp(item->text, MENU_SEPARATOR_ITEM) != 0);
+}
+
+
+/*-------------------------------------------------
+ exclusive_input_pressed - return TRUE if the
+ given key is pressed and we haven't already
+ reported a key
+-------------------------------------------------*/
+
+INLINE int exclusive_input_pressed(ui_menu *menu, int key, int repeat)
+{
+ if (menu->menu_event.iptkey == IPT_INVALID && ui_input_pressed_repeat(menu->machine(), key, repeat))
+ {
+ menu->menu_event.iptkey = key;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ CORE SYSTEM MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_menu_init - initialize the menu system
+-------------------------------------------------*/
+
+void ui_menu_init(running_machine &machine)
+{
+ int x;
+
+ /* initialize the menu stack */
+ ui_menu_stack_reset(machine);
+
+ /* create a texture for hilighting items */
+ hilight_bitmap = auto_bitmap_alloc(machine, 256, 1, BITMAP_FORMAT_ARGB32);
+ for (x = 0; x < 256; x++)
+ {
+ int alpha = 0xff;
+ if (x < 25) alpha = 0xff * x / 25;
+ if (x > 256 - 25) alpha = 0xff * (255 - x) / 25;
+ *BITMAP_ADDR32(hilight_bitmap, 0, x) = MAKE_ARGB(alpha,0xff,0xff,0xff);
+ }
+ hilight_texture = machine.render().texture_alloc();
+ hilight_texture->set_bitmap(hilight_bitmap, NULL, TEXFORMAT_ARGB32);
+
+ /* create a texture for arrow icons */
+ arrow_texture = machine.render().texture_alloc(menu_render_triangle);
+
+ /* add an exit callback to free memory */
+ machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(ui_menu_exit), &machine));
+}
+
+
+/*-------------------------------------------------
+ ui_menu_exit - clean up after ourselves
+-------------------------------------------------*/
+
+static void ui_menu_exit(running_machine &machine)
+{
+ /* free menus */
+ ui_menu_stack_reset(machine);
+ ui_menu_clear_free_list(machine);
+
+ /* free textures */
+ machine.render().texture_free(hilight_texture);
+ machine.render().texture_free(arrow_texture);
+
+ if (drivlist)
+ {
+ global_free(drivlist);
+ drivlist = NULL;
+ }
+}
+
+
+
+/***************************************************************************
+ CORE MENU MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_menu_alloc - allocate a new menu
+-------------------------------------------------*/
+
+ui_menu *ui_menu_alloc(running_machine &machine, render_container *container, ui_menu_handler_func handler, void *parameter)
+{
+ ui_menu *menu;
+
+ /* allocate and clear memory for the menu and the state */
+ menu = auto_alloc_clear(machine, ui_menu(machine));
+
+ /* initialize the state */
+ menu->container = container;
+ menu->handler = handler;
+ menu->parameter = parameter;
+
+ /* reset the menu (adds a default entry) */
+ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST);
+ return menu;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_free - free a menu
+-------------------------------------------------*/
+
+void ui_menu_free(ui_menu *menu)
+{
+ /* free the pools */
+ while (menu->pool != NULL)
+ {
+ ui_menu_pool *pool = menu->pool;
+ menu->pool = pool->next;
+ auto_free(menu->machine(), pool);
+ }
+
+ /* free the item array */
+ if (menu->item != NULL)
+ auto_free(menu->machine(), menu->item);
+
+ /* free the state */
+ if (menu->state != NULL)
+ {
+ if (menu->destroy_state != NULL)
+ (*menu->destroy_state)(menu, menu->state);
+ auto_free(menu->machine(), menu->state);
+ }
+
+ /* free the menu */
+ auto_free(menu->machine(), menu);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_reset - free all items in the menu,
+ and all memory allocated from the memory pool
+-------------------------------------------------*/
+
+void ui_menu_reset(ui_menu *menu, ui_menu_reset_options options)
+{
+ ui_menu_pool *pool;
+
+ /* based on the reset option, set the reset info */
+ menu->resetpos = 0;
+ menu->resetref = NULL;
+ if (options == UI_MENU_RESET_REMEMBER_POSITION)
+ menu->resetpos = menu->selected;
+ else if (options == UI_MENU_RESET_REMEMBER_REF)
+ menu->resetref = menu->item[menu->selected].ref;
+
+ /* reset all the pools and the numitems back to 0 */
+ for (pool = menu->pool; pool != NULL; pool = pool->next)
+ pool->top = (UINT8 *)(pool + 1);
+ menu->numitems = 0;
+ menu->visitems = 0;
+ menu->selected = 0;
+
+ /* add an item to return */
+ if (menu->parent == NULL)
+ ui_menu_item_append(menu, backtext, NULL, 0, NULL);
+ else if (menu->parent->handler == menu_quit_game)
+ ui_menu_item_append(menu, exittext, NULL, 0, NULL);
+ else
+ ui_menu_item_append(menu, priortext, NULL, 0, NULL);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_populated - returns TRUE if the menu
+ has any non-default items in it
+-------------------------------------------------*/
+
+int ui_menu_populated(ui_menu *menu)
+{
+ return (menu->numitems > 1);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_item_append - append a new item to the
+ end of the menu
+-------------------------------------------------*/
+
+void ui_menu_item_append(ui_menu *menu, const char *text, const char *subtext, UINT32 flags, void *ref)
+{
+ ui_menu_item *item;
+ int index;
+
+ /* only allow multiline as the first item */
+ if ((flags & MENU_FLAG_MULTILINE) != 0)
+ assert(menu->numitems == 1);
+
+ /* only allow a single multi-line item */
+ else if (menu->numitems >= 2)
+ assert((menu->item[0].flags & MENU_FLAG_MULTILINE) == 0);
+
+ /* realloc the item array if necessary */
+ if (menu->numitems >= menu->allocitems)
+ {
+ int olditems = menu->allocitems;
+ menu->allocitems += UI_MENU_ALLOC_ITEMS;
+ ui_menu_item *newitems = auto_alloc_array(menu->machine(), ui_menu_item, menu->allocitems);
+ for (int itemnum = 0; itemnum < olditems; itemnum++)
+ newitems[itemnum] = menu->item[itemnum];
+ auto_free(menu->machine(), menu->item);
+ menu->item = newitems;
+ }
+ index = menu->numitems++;
+
+ /* copy the previous last item to the next one */
+ if (index != 0)
+ {
+ index--;
+ menu->item[index + 1] = menu->item[index];
+ }
+
+ /* allocate a new item and populte it */
+ item = &menu->item[index];
+ item->text = (text != NULL) ? ui_menu_pool_strdup(menu, text) : NULL;
+ item->subtext = (subtext != NULL) ? ui_menu_pool_strdup(menu, subtext) : NULL;
+ item->flags = flags;
+ item->ref = ref;
+
+ /* update the selection if we need to */
+ if (menu->resetpos == index || (menu->resetref != NULL && menu->resetref == ref))
+ menu->selected = index;
+ if (menu->resetpos == menu->numitems - 1)
+ menu->selected = menu->numitems - 1;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_process - process a menu, drawing it
+ and returning any interesting events
+-------------------------------------------------*/
+
+const ui_menu_event *ui_menu_process(running_machine &machine, ui_menu *menu, UINT32 flags)
+{
+ /* reset the menu_event */
+ menu->menu_event.iptkey = IPT_INVALID;
+
+ /* first make sure our selection is valid */
+ ui_menu_validate_selection(menu, 1);
+
+ /* draw the menu */
+ if (menu->numitems > 1 && (menu->item[0].flags & MENU_FLAG_MULTILINE) != 0)
+ ui_menu_draw_text_box(menu);
+ else
+ ui_menu_draw(machine, menu, (flags & UI_MENU_PROCESS_CUSTOM_ONLY) != 0);
+
+ /* process input */
+ if (!(flags & UI_MENU_PROCESS_NOKEYS))
+ {
+ /* read events */
+ ui_menu_handle_events(menu);
+
+ /* handle the keys if we don't already have an menu_event */
+ if (menu->menu_event.iptkey == IPT_INVALID)
+ ui_menu_handle_keys(menu, flags);
+ }
+
+ /* update the selected item in the menu_event */
+ if (menu->menu_event.iptkey != IPT_INVALID && menu->selected >= 0 && menu->selected < menu->numitems)
+ {
+ menu->menu_event.itemref = menu->item[menu->selected].ref;
+ return &menu->menu_event;
+ }
+ return NULL;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_set_custom_render - configure the menu
+ for custom rendering
+-------------------------------------------------*/
+
+void ui_menu_set_custom_render(ui_menu *menu, ui_menu_custom_func custom, float top, float bottom)
+{
+ menu->custom = custom;
+ menu->customtop = top;
+ menu->custombottom = bottom;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_alloc_state - allocate permanent
+ memory to represent the menu's state
+-------------------------------------------------*/
+
+void *ui_menu_alloc_state(ui_menu *menu, size_t size, ui_menu_destroy_state_func destroy_state)
+{
+ if (menu->state != NULL)
+ {
+ if (menu->destroy_state != NULL)
+ (*menu->destroy_state)(menu, menu->state);
+ auto_free(menu->machine(), menu->state);
+ }
+ menu->state = auto_alloc_array_clear(menu->machine(), UINT8, size);
+ menu->destroy_state = destroy_state;
+
+ return menu->state;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_pool_alloc - allocate temporary memory
+ from the menu's memory pool
+-------------------------------------------------*/
+
+void *ui_menu_pool_alloc(ui_menu *menu, size_t size)
+{
+ ui_menu_pool *pool;
+
+ assert(size < UI_MENU_POOL_SIZE);
+
+ /* find a pool with enough room */
+ for (pool = menu->pool; pool != NULL; pool = pool->next)
+ if (pool->end - pool->top >= size)
+ {
+ void *result = pool->top;
+ pool->top += size;
+ return result;
+ }
+
+ /* allocate a new pool */
+ pool = (ui_menu_pool *)auto_alloc_array_clear(menu->machine(), UINT8, sizeof(*pool) + UI_MENU_POOL_SIZE);
+
+ /* wire it up */
+ pool->next = menu->pool;
+ menu->pool = pool;
+ pool->top = (UINT8 *)(pool + 1);
+ pool->end = pool->top + UI_MENU_POOL_SIZE;
+ return ui_menu_pool_alloc(menu, size);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_pool_strdup - make a temporary string
+ copy in the menu's memory pool
+-------------------------------------------------*/
+
+const char *ui_menu_pool_strdup(ui_menu *menu, const char *string)
+{
+ return strcpy((char *)ui_menu_pool_alloc(menu, strlen(string) + 1), string);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_get_selection - retrieves the index
+ of the currently selected menu item
+-------------------------------------------------*/
+
+void *ui_menu_get_selection(ui_menu *menu)
+{
+ return (menu->selected >= 0 && menu->selected < menu->numitems) ? menu->item[menu->selected].ref : NULL;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_set_selection - changes the index
+ of the currently selected menu item
+-------------------------------------------------*/
+
+void ui_menu_set_selection(ui_menu *menu, void *selected_itemref)
+{
+ int itemnum;
+
+ menu->selected = -1;
+ for (itemnum = 0; itemnum < menu->numitems; itemnum++)
+ if (menu->item[itemnum].ref == selected_itemref)
+ {
+ menu->selected = itemnum;
+ break;
+ }
+}
+
+
+
+/***************************************************************************
+ INTERNAL MENU PROCESSING
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_menu_draw - draw a menu
+-------------------------------------------------*/
+
+static void ui_menu_draw(running_machine &machine, ui_menu *menu, int customonly)
+{
+ float line_height = ui_get_line_height(machine);
+ float lr_arrow_width = 0.4f * line_height * machine.render().ui_aspect();
+ float ud_arrow_width = line_height * machine.render().ui_aspect();
+ float gutter_width = lr_arrow_width * 1.3f;
+ float x1, y1, x2, y2;
+
+ float effective_width, effective_left;
+ float visible_width, visible_main_menu_height;
+ float visible_extra_menu_height = 0;
+ float visible_top, visible_left;
+ int selected_subitem_too_big = FALSE;
+ int visible_lines;
+ int top_line;
+ int itemnum, linenum;
+ int mouse_hit, mouse_button;
+ render_target *mouse_target;
+ INT32 mouse_target_x, mouse_target_y;
+ float mouse_x = -1, mouse_y = -1;
+
+ /* compute the width and height of the full menu */
+ visible_width = 0;
+ visible_main_menu_height = 0;
+ for (itemnum = 0; itemnum < menu->numitems; itemnum++)
+ {
+ const ui_menu_item *item = &menu->item[itemnum];
+ float total_width;
+
+ /* compute width of left hand side */
+ total_width = gutter_width + ui_get_string_width(machine, item->text) + gutter_width;
+
+ /* add in width of right hand side */
+ if (item->subtext)
+ total_width += 2.0f * gutter_width + ui_get_string_width(machine, item->subtext);
+
+ /* track the maximum */
+ if (total_width > visible_width)
+ visible_width = total_width;
+
+ /* track the height as well */
+ visible_main_menu_height += line_height;
+ }
+
+ /* account for extra space at the top and bottom */
+ visible_extra_menu_height = menu->customtop + menu->custombottom;
+
+ /* add a little bit of slop for rounding */
+ visible_width += 0.01f;
+ visible_main_menu_height += 0.01f;
+
+ /* if we are too wide or too tall, clamp it down */
+ if (visible_width + 2.0f * UI_BOX_LR_BORDER > 1.0f)
+ visible_width = 1.0f - 2.0f * UI_BOX_LR_BORDER;
+
+ /* if the menu and extra menu won't fit, take away part of the regular menu, it will scroll */
+ if (visible_main_menu_height + visible_extra_menu_height + 2.0f * UI_BOX_TB_BORDER > 1.0f)
+ visible_main_menu_height = 1.0f - 2.0f * UI_BOX_TB_BORDER - visible_extra_menu_height;
+
+ visible_lines = floor(visible_main_menu_height / line_height);
+ visible_main_menu_height = (float)visible_lines * line_height;
+
+ /* compute top/left of inner menu area by centering */
+ visible_left = (1.0f - visible_width) * 0.5f;
+ visible_top = (1.0f - (visible_main_menu_height + visible_extra_menu_height)) * 0.5f;
+
+ /* if the menu is at the bottom of the extra, adjust */
+ visible_top += menu->customtop;
+
+ /* first add us a box */
+ x1 = visible_left - UI_BOX_LR_BORDER;
+ y1 = visible_top - UI_BOX_TB_BORDER;
+ x2 = visible_left + visible_width + UI_BOX_LR_BORDER;
+ y2 = visible_top + visible_main_menu_height + UI_BOX_TB_BORDER;
+ if (!customonly)
+ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
+
+ /* determine the first visible line based on the current selection */
+ top_line = menu->selected - visible_lines / 2;
+ if (top_line < 0)
+ top_line = 0;
+ if (top_line + visible_lines >= menu->numitems)
+ top_line = menu->numitems - visible_lines;
+
+ /* determine effective positions taking into account the hilighting arrows */
+ effective_width = visible_width - 2.0f * gutter_width;
+ effective_left = visible_left + gutter_width;
+
+ /* locate mouse */
+ mouse_hit = FALSE;
+ mouse_button = FALSE;
+ if (!customonly)
+ {
+ mouse_target = ui_input_find_mouse(machine, &mouse_target_x, &mouse_target_y, &mouse_button);
+ if (mouse_target != NULL)
+ if (mouse_target->map_point_container(mouse_target_x, mouse_target_y, *menu->container, mouse_x, mouse_y))
+ mouse_hit = TRUE;
+ }
+
+ /* loop over visible lines */
+ menu->hover = menu->numitems + 1;
+ if (!customonly)
+ for (linenum = 0; linenum < visible_lines; linenum++)
+ {
+ float line_y = visible_top + (float)linenum * line_height;
+ itemnum = top_line + linenum;
+ const ui_menu_item *item = &menu->item[itemnum];
+ const char *itemtext = item->text;
+ rgb_t fgcolor = UI_TEXT_COLOR;
+ rgb_t bgcolor = UI_TEXT_BG_COLOR;
+ rgb_t fgcolor2 = UI_SUBITEM_COLOR;
+ rgb_t fgcolor3 = UI_CLONE_COLOR;
+ float line_x0 = x1 + 0.5f * UI_LINE_WIDTH;
+ float line_y0 = line_y;
+ float line_x1 = x2 - 0.5f * UI_LINE_WIDTH;
+ float line_y1 = line_y + line_height;
+
+ /* set the hover if this is our item */
+ if (mouse_hit && line_x0 <= mouse_x && line_x1 > mouse_x && line_y0 <= mouse_y && line_y1 > mouse_y && item_is_selectable(item))
+ menu->hover = itemnum;
+
+ /* if we're selected, draw with a different background */
+ if (itemnum == menu->selected)
+ {
+ fgcolor = UI_SELECTED_COLOR;
+ bgcolor = UI_SELECTED_BG_COLOR;
+ fgcolor2 = UI_SELECTED_COLOR;
+ fgcolor3 = UI_SELECTED_COLOR;
+ }
+
+ /* else if the mouse is over this item, draw with a different background */
+ else if (itemnum == menu->hover)
+ {
+ fgcolor = UI_MOUSEOVER_COLOR;
+ bgcolor = UI_MOUSEOVER_BG_COLOR;
+ fgcolor2 = UI_MOUSEOVER_COLOR;
+ fgcolor3 = UI_MOUSEOVER_COLOR;
+ }
+
+ /* if we have some background hilighting to do, add a quad behind everything else */
+ if (bgcolor != UI_TEXT_BG_COLOR)
+ menu->container->add_quad(line_x0, line_y0, line_x1, line_y1, bgcolor, hilight_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
+
+ /* if we're on the top line, display the up arrow */
+ if (linenum == 0 && top_line != 0)
+ {
+ menu->container->add_quad(
+ 0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
+ line_y + 0.25f * line_height,
+ 0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
+ line_y + 0.75f * line_height,
+ fgcolor,
+ arrow_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT0));
+ if (menu->hover == itemnum)
+ menu->hover = -2;
+ }
+
+ /* if we're on the bottom line, display the down arrow */
+ else if (linenum == visible_lines - 1 && itemnum != menu->numitems - 1)
+ {
+ menu->container->add_quad(
+ 0.5f * (x1 + x2) - 0.5f * ud_arrow_width,
+ line_y + 0.25f * line_height,
+ 0.5f * (x1 + x2) + 0.5f * ud_arrow_width,
+ line_y + 0.75f * line_height,
+ fgcolor,
+ arrow_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT0 ^ ORIENTATION_FLIP_Y));
+ if (menu->hover == itemnum)
+ menu->hover = -1;
+ }
+
+ /* if we're just a divider, draw a line */
+ else if (strcmp(itemtext, MENU_SEPARATOR_ITEM) == 0)
+ menu->container->add_line(visible_left, line_y + 0.5f * line_height, visible_left + visible_width, line_y + 0.5f * line_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* if we don't have a subitem, just draw the string centered */
+ else if (item->subtext == NULL)
+ ui_draw_text_full(menu->container, itemtext, effective_left, line_y, effective_width,
+ JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, NULL, NULL);
+
+ /* otherwise, draw the item on the left and the subitem text on the right */
+ else
+ {
+ int subitem_invert = item->flags & MENU_FLAG_INVERT;
+ const char *subitem_text = item->subtext;
+ float item_width, subitem_width;
+
+ /* draw the left-side text */
+ ui_draw_text_full(menu->container, itemtext, effective_left, line_y, effective_width,
+ JUSTIFY_LEFT, WRAP_TRUNCATE, DRAW_NORMAL, fgcolor, bgcolor, &item_width, NULL);
+
+ /* give 2 spaces worth of padding */
+ item_width += 2.0f * gutter_width;
+
+ /* if the subitem doesn't fit here, display dots */
+ if (ui_get_string_width(machine, subitem_text) > effective_width - item_width)
+ {
+ subitem_text = "...";
+ if (itemnum == menu->selected)
+ selected_subitem_too_big = TRUE;
+ }
+
+ /* draw the subitem right-justified */
+ ui_draw_text_full(menu->container, subitem_text, effective_left + item_width, line_y, effective_width - item_width,
+ JUSTIFY_RIGHT, WRAP_TRUNCATE, DRAW_NORMAL, subitem_invert ? fgcolor3 : fgcolor2, bgcolor, &subitem_width, NULL);
+
+ /* apply arrows */
+ if (itemnum == menu->selected && (item->flags & MENU_FLAG_LEFT_ARROW))
+ {
+ menu->container->add_quad(
+ effective_left + effective_width - subitem_width - gutter_width,
+ line_y + 0.1f * line_height,
+ effective_left + effective_width - subitem_width - gutter_width + lr_arrow_width,
+ line_y + 0.9f * line_height,
+ fgcolor,
+ arrow_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90 ^ ORIENTATION_FLIP_X));
+ }
+ if (itemnum == menu->selected && (item->flags & MENU_FLAG_RIGHT_ARROW))
+ {
+ menu->container->add_quad(
+ effective_left + effective_width + gutter_width - lr_arrow_width,
+ line_y + 0.1f * line_height,
+ effective_left + effective_width + gutter_width,
+ line_y + 0.9f * line_height,
+ fgcolor,
+ arrow_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXORIENT(ROT90));
+ }
+ }
+ }
+
+ /* if the selected subitem is too big, display it in a separate offset box */
+ if (selected_subitem_too_big)
+ {
+ const ui_menu_item *item = &menu->item[menu->selected];
+ int subitem_invert = item->flags & MENU_FLAG_INVERT;
+ linenum = menu->selected - top_line;
+ float line_y = visible_top + (float)linenum * line_height;
+ float target_width, target_height;
+ float target_x, target_y;
+
+ /* compute the multi-line target width/height */
+ ui_draw_text_full(menu->container, item->subtext, 0, 0, visible_width * 0.75f,
+ JUSTIFY_RIGHT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height);
+
+ /* determine the target location */
+ target_x = visible_left + visible_width - target_width - UI_BOX_LR_BORDER;
+ target_y = line_y + line_height + UI_BOX_TB_BORDER;
+ if (target_y + target_height + UI_BOX_TB_BORDER > visible_main_menu_height)
+ target_y = line_y - target_height - UI_BOX_TB_BORDER;
+
+ /* add a box around that */
+ ui_draw_outlined_box(menu->container, target_x - UI_BOX_LR_BORDER,
+ target_y - UI_BOX_TB_BORDER,
+ target_x + target_width + UI_BOX_LR_BORDER,
+ target_y + target_height + UI_BOX_TB_BORDER, subitem_invert ? UI_SELECTED_BG_COLOR : UI_BACKGROUND_COLOR);
+ ui_draw_text_full(menu->container, item->subtext, target_x, target_y, target_width,
+ JUSTIFY_RIGHT, WRAP_WORD, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, NULL, NULL);
+ }
+
+ /* if there is something special to add, do it by calling the passed routine */
+ if (menu->custom != NULL)
+ {
+ void *selectedref = (menu->selected >= 0 && menu->selected < menu->numitems) ? menu->item[menu->selected].ref : NULL;
+ (*menu->custom)(menu->machine(), menu, menu->state, selectedref, menu->customtop, menu->custombottom, x1, y1, x2, y2);
+ }
+
+ /* return the number of visible lines, minus 1 for top arrow and 1 for bottom arrow */
+ menu->visitems = visible_lines - (top_line != 0) - (top_line + visible_lines != menu->numitems);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_draw_text_box - draw a multiline
+ word-wrapped text box with a menu item at the
+ bottom
+-------------------------------------------------*/
+
+static void ui_menu_draw_text_box(ui_menu *menu)
+{
+ const char *text = menu->item[0].text;
+ const char *backtext = menu->item[1].text;
+ float line_height = ui_get_line_height(menu->machine());
+ float lr_arrow_width = 0.4f * line_height * menu->machine().render().ui_aspect();
+ float gutter_width = lr_arrow_width;
+ float target_width, target_height, prior_width;
+ float target_x, target_y;
+
+ /* compute the multi-line target width/height */
+ ui_draw_text_full(menu->container, text, 0, 0, 1.0f - 2.0f * UI_BOX_LR_BORDER - 2.0f * gutter_width,
+ JUSTIFY_LEFT, WRAP_WORD, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &target_width, &target_height);
+ target_height += 2.0f * line_height;
+ if (target_height > 1.0f - 2.0f * UI_BOX_TB_BORDER)
+ target_height = floor((1.0f - 2.0f * UI_BOX_TB_BORDER) / line_height) * line_height;
+
+ /* maximum against "return to prior menu" text */
+ prior_width = ui_get_string_width(menu->machine(), backtext) + 2.0f * gutter_width;
+ target_width = MAX(target_width, prior_width);
+
+ /* determine the target location */
+ target_x = 0.5f - 0.5f * target_width;
+ target_y = 0.5f - 0.5f * target_height;
+
+ /* make sure we stay on-screen */
+ if (target_x < UI_BOX_LR_BORDER + gutter_width)
+ target_x = UI_BOX_LR_BORDER + gutter_width;
+ if (target_x + target_width + gutter_width + UI_BOX_LR_BORDER > 1.0f)
+ target_x = 1.0f - UI_BOX_LR_BORDER - gutter_width - target_width;
+ if (target_y < UI_BOX_TB_BORDER)
+ target_y = UI_BOX_TB_BORDER;
+ if (target_y + target_height + UI_BOX_TB_BORDER > 1.0f)
+ target_y = 1.0f - UI_BOX_TB_BORDER - target_height;
+
+ /* add a box around that */
+ ui_draw_outlined_box(menu->container, target_x - UI_BOX_LR_BORDER - gutter_width,
+ target_y - UI_BOX_TB_BORDER,
+ target_x + target_width + gutter_width + UI_BOX_LR_BORDER,
+ target_y + target_height + UI_BOX_TB_BORDER, (menu->item[0].flags & MENU_FLAG_REDTEXT) ? UI_RED_COLOR : UI_BACKGROUND_COLOR);
+ ui_draw_text_full(menu->container, text, target_x, target_y, target_width,
+ JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL);
+
+ /* draw the "return to prior menu" text with a hilight behind it */
+ menu->container->add_quad(
+ target_x + 0.5f * UI_LINE_WIDTH,
+ target_y + target_height - line_height,
+ target_x + target_width - 0.5f * UI_LINE_WIDTH,
+ target_y + target_height,
+ UI_SELECTED_BG_COLOR,
+ hilight_texture,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXWRAP(TRUE));
+ ui_draw_text_full(menu->container, backtext, target_x, target_y + target_height - line_height, target_width,
+ JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NORMAL, UI_SELECTED_COLOR, UI_SELECTED_BG_COLOR, NULL, NULL);
+
+ /* artificially set the hover to the last item so a double-click exits */
+ menu->hover = menu->numitems - 1;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_handle_events - generically handle
+ input events for a menu
+-------------------------------------------------*/
+
+static void ui_menu_handle_events(ui_menu *menu)
+{
+ int stop = FALSE;
+ ui_event menu_event;
+
+ /* loop while we have interesting events */
+ while (ui_input_pop_event(menu->machine(), &menu_event) && !stop)
+ {
+ switch (menu_event.event_type)
+ {
+ /* if we are hovering over a valid item, select it with a single click */
+ case UI_EVENT_MOUSE_DOWN:
+ if (menu->hover >= 0 && menu->hover < menu->numitems)
+ menu->selected = menu->hover;
+ else if (menu->hover == -2)
+ {
+ menu->selected -= menu->visitems - 1;
+ ui_menu_validate_selection(menu, 1);
+ }
+ else if (menu->hover == -1)
+ {
+ menu->selected += menu->visitems - 1;
+ ui_menu_validate_selection(menu, 1);
+ }
+ break;
+
+ /* if we are hovering over a valid item, fake a UI_SELECT with a double-click */
+ case UI_EVENT_MOUSE_DOUBLE_CLICK:
+ if (menu->hover >= 0 && menu->hover < menu->numitems)
+ {
+ menu->selected = menu->hover;
+ if (menu_event.event_type == UI_EVENT_MOUSE_DOUBLE_CLICK)
+ {
+ menu->menu_event.iptkey = IPT_UI_SELECT;
+ if (menu->selected == menu->numitems - 1)
+ {
+ menu->menu_event.iptkey = IPT_UI_CANCEL;
+ ui_menu_stack_pop(menu->machine());
+ }
+ }
+ stop = TRUE;
+ }
+ break;
+
+ /* translate CHAR events into specials */
+ case UI_EVENT_CHAR:
+ menu->menu_event.iptkey = IPT_SPECIAL;
+ menu->menu_event.unichar = menu_event.ch;
+ stop = TRUE;
+ break;
+
+ /* ignore everything else */
+ default:
+ break;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ ui_menu_handle_keys - generically handle
+ keys for a menu
+-------------------------------------------------*/
+
+static void ui_menu_handle_keys(ui_menu *menu, UINT32 flags)
+{
+ int ignorepause = ui_menu_is_force_game_select();
+ int ignoreright = FALSE;
+ int ignoreleft = FALSE;
+ int code;
+
+ /* bail if no items */
+ if (menu->numitems == 0)
+ return;
+
+ /* if we hit select, return TRUE or pop the stack, depending on the item */
+ if (exclusive_input_pressed(menu, IPT_UI_SELECT, 0))
+ {
+ if (menu->selected == menu->numitems - 1)
+ {
+ menu->menu_event.iptkey = IPT_UI_CANCEL;
+ ui_menu_stack_pop(menu->machine());
+ }
+ return;
+ }
+
+ /* hitting cancel also pops the stack */
+ if (exclusive_input_pressed(menu, IPT_UI_CANCEL, 0))
+ {
+ ui_menu_stack_pop(menu->machine());
+ return;
+ }
+
+ /* validate the current selection */
+ ui_menu_validate_selection(menu, 1);
+
+ /* swallow left/right keys if they are not appropriate */
+ ignoreleft = ((menu->item[menu->selected].flags & MENU_FLAG_LEFT_ARROW) == 0);
+ ignoreright = ((menu->item[menu->selected].flags & MENU_FLAG_RIGHT_ARROW) == 0);
+
+ /* accept left/right keys as-is with repeat */
+ if (!ignoreleft && exclusive_input_pressed(menu, IPT_UI_LEFT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
+ return;
+ if (!ignoreright && exclusive_input_pressed(menu, IPT_UI_RIGHT, (flags & UI_MENU_PROCESS_LR_REPEAT) ? 6 : 0))
+ return;
+
+ /* up backs up by one item */
+ if (exclusive_input_pressed(menu, IPT_UI_UP, 6))
+ {
+ menu->selected = (menu->selected + menu->numitems - 1) % menu->numitems;
+ ui_menu_validate_selection(menu, -1);
+ }
+
+ /* down advances by one item */
+ if (exclusive_input_pressed(menu, IPT_UI_DOWN, 6))
+ {
+ menu->selected = (menu->selected + 1) % menu->numitems;
+ ui_menu_validate_selection(menu, 1);
+ }
+
+ /* page up backs up by visitems */
+ if (exclusive_input_pressed(menu, IPT_UI_PAGE_UP, 6))
+ {
+ menu->selected -= menu->visitems - 1;
+ ui_menu_validate_selection(menu, 1);
+ }
+
+ /* page down advances by visitems */
+ if (exclusive_input_pressed(menu, IPT_UI_PAGE_DOWN, 6))
+ {
+ menu->selected += menu->visitems - 1;
+ ui_menu_validate_selection(menu, -1);
+ }
+
+ /* home goes to the start */
+ if (exclusive_input_pressed(menu, IPT_UI_HOME, 0))
+ {
+ menu->selected = 0;
+ ui_menu_validate_selection(menu, 1);
+ }
+
+ /* end goes to the last */
+ if (exclusive_input_pressed(menu, IPT_UI_END, 0))
+ {
+ menu->selected = menu->numitems - 1;
+ ui_menu_validate_selection(menu, -1);
+ }
+
+ /* pause enables/disables pause */
+ if (!ignorepause && exclusive_input_pressed(menu, IPT_UI_PAUSE, 0))
+ {
+ if (menu->machine().paused())
+ menu->machine().resume();
+ else
+ menu->machine().pause();
+ }
+
+ /* handle a toggle cheats request */
+ if (ui_input_pressed_repeat(menu->machine(), IPT_UI_TOGGLE_CHEAT, 0))
+ menu->machine().cheat().set_enable(!menu->machine().cheat().enabled());
+
+ /* see if any other UI keys are pressed */
+ if (menu->menu_event.iptkey == IPT_INVALID)
+ for (code = __ipt_ui_start; code <= __ipt_ui_end; code++)
+ {
+ if (code == IPT_UI_CONFIGURE || (code == IPT_UI_LEFT && ignoreleft) || (code == IPT_UI_RIGHT && ignoreright) || (code == IPT_UI_PAUSE && ignorepause))
+ continue;
+ if (exclusive_input_pressed(menu, code, 0))
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ ui_menu_validate_selection - validate the
+ current selection and ensure it is on a
+ correct item
+-------------------------------------------------*/
+
+static void ui_menu_validate_selection(ui_menu *menu, int scandir)
+{
+ /* clamp to be in range */
+ if (menu->selected < 0)
+ menu->selected = 0;
+ else if (menu->selected >= menu->numitems)
+ menu->selected = menu->numitems - 1;
+
+ /* skip past unselectable items */
+ while (!item_is_selectable(&menu->item[menu->selected]))
+ menu->selected = (menu->selected + menu->numitems + scandir) % menu->numitems;
+}
+
+
+
+/*-------------------------------------------------
+ ui_menu_clear_free_list - clear out anything
+ accumulated in the free list
+-------------------------------------------------*/
+
+static void ui_menu_clear_free_list(running_machine &machine)
+{
+ while (menu_free != NULL)
+ {
+ ui_menu *menu = menu_free;
+ menu_free = menu->parent;
+ ui_menu_free(menu);
+ }
+}
+
+
+
+/***************************************************************************
+ MENU STACK MANAGEMENT
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_menu_stack_reset - reset the menu stack
+-------------------------------------------------*/
+
+void ui_menu_stack_reset(running_machine &machine)
+{
+ while (menu_stack != NULL)
+ ui_menu_stack_pop(machine);
+}
+
+
+/*-------------------------------------------------
+ ui_menu_stack_push - push a new menu onto the
+ stack
+-------------------------------------------------*/
+
+void ui_menu_stack_push(ui_menu *menu)
+{
+ menu->parent = menu_stack;
+ menu_stack = menu;
+ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST);
+ ui_input_reset(menu->machine());
+}
+
+
+/*-------------------------------------------------
+ ui_menu_stack_pop - pop a menu from the stack
+-------------------------------------------------*/
+
+void ui_menu_stack_pop(running_machine &machine)
+{
+ if (menu_stack != NULL)
+ {
+ ui_menu *menu = menu_stack;
+ menu_stack = menu->parent;
+ menu->parent = menu_free;
+ menu_free = menu;
+ ui_input_reset(machine);
+ }
+}
+
+
+
+/***************************************************************************
+ UI SYSTEM INTERACTION
+***************************************************************************/
+
+/*-------------------------------------------------
+ ui_menu_ui_handler - displays the current menu
+ and calls the menu handler
+-------------------------------------------------*/
+
+UINT32 ui_menu_ui_handler(running_machine &machine, render_container *container, UINT32 state)
+{
+ /* if we have no menus stacked up, start with the main menu */
+ if (menu_stack == NULL)
+ ui_menu_stack_push(ui_menu_alloc(machine, container, menu_main, NULL));
+
+ /* update the menu state */
+ if (menu_stack != NULL)
+ {
+ ui_menu *menu = menu_stack;
+ (*menu->handler)(machine, menu, menu->parameter, menu->state);
+ }
+
+ /* clear up anything pending to be released */
+ ui_menu_clear_free_list(machine);
+
+ /* if the menus are to be hidden, return a cancel here */
+ if ((ui_input_pressed(machine, IPT_UI_CONFIGURE) && !ui_menu_is_force_game_select()) || menu_stack == NULL)
+ return UI_HANDLER_CANCEL;
+
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ ui_slider_ui_handler - pushes the slider
+ menu on the stack and hands off to the
+ standard menu handler
+-------------------------------------------------*/
+
+UINT32 ui_slider_ui_handler(running_machine &machine, render_container *container, UINT32 state)
+{
+ UINT32 result;
+
+ /* if this is the first call, push the sliders menu */
+ if (state)
+ ui_menu_stack_push(ui_menu_alloc(machine, container, menu_sliders, (void *)1));
+
+ /* handle standard menus */
+ result = ui_menu_ui_handler(machine, container, state);
+
+ /* if we are cancelled, pop the sliders menu */
+ if (result == UI_HANDLER_CANCEL)
+ ui_menu_stack_pop(machine);
+
+ return (menu_stack != NULL && menu_stack->handler == menu_sliders && menu_stack->parameter != NULL) ? 0 : UI_HANDLER_CANCEL;
+}
+
+
+/*-------------------------------------------------
+ ui_menu_force_game_select - force the game
+ select menu to be visible and inescapable
+-------------------------------------------------*/
+
+void ui_menu_force_game_select(running_machine &machine, render_container *container)
+{
+ char *gamename = (char *)machine.options().system_name();
+
+ /* reset the menu stack */
+ ui_menu_stack_reset(machine);
+
+ /* add the quit entry followed by the game select entry */
+ ui_menu_stack_push(ui_menu_alloc(machine, container, menu_quit_game, NULL));
+ ui_menu_stack_push(ui_menu_alloc(machine, container, menu_select_game, gamename));
+
+ /* force the menus on */
+ ui_show_menu();
+
+ /* make sure MAME is paused */
+ machine.pause();
+}
+
+
+/*-------------------------------------------------
+ ui_menu_is_force_game_select - return true
+ if we are currently in "force game select"
+ mode
+-------------------------------------------------*/
+
+int ui_menu_is_force_game_select(void)
+{
+ ui_menu *menu;
+
+ for (menu = menu_stack; menu != NULL; menu = menu->parent)
+ if (menu->handler == menu_quit_game && menu->parent == NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+
+/***************************************************************************
+ MENU HANDLERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ menu_main - handle the main menu
+-------------------------------------------------*/
+
+static void menu_main(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_main_populate(machine, menu, state);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+ if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT)
+ ui_menu_stack_push(ui_menu_alloc(machine, menu->container, (ui_menu_handler_func)menu_event->itemref, NULL));
+}
+
+
+/*-------------------------------------------------
+ ui_menu_keyboard_mode - menu that
+-------------------------------------------------*/
+
+static void ui_menu_keyboard_mode(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+ int natural = ui_get_use_natural_keyboard(machine);
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ ui_menu_item_append(menu, "Keyboard Mode:", natural ? "Natural" : "Emulated", natural ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, NULL);
+ }
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+
+ if (menu_event != NULL)
+ {
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) {
+ ui_set_use_natural_keyboard(machine, natural ^ TRUE);
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ ui_slot_get_current_index - returns
+-------------------------------------------------*/
+int ui_slot_get_current_index(running_machine &machine, device_slot_interface *slot)
+{
+ const char *current = machine.options().value(slot->device().tag());
+ const slot_interface* intf = slot->get_slot_interfaces();
+ int val = -1;
+ for (int i = 0; intf[i].name != NULL; i++) {
+ if (strcmp(current, intf[i].name) == 0) val = i;
+ }
+ return val;
+}
+
+/*-------------------------------------------------
+ ui_slot_get_length - returns
+-------------------------------------------------*/
+int ui_slot_get_length(running_machine &machine, device_slot_interface *slot)
+{
+ const slot_interface* intf = slot->get_slot_interfaces();
+ int val = 0;
+ for (int i = 0; intf[i].name != NULL; i++) val++;
+ return val;
+}
+
+/*-------------------------------------------------
+ ui_slot_get_next - returns
+-------------------------------------------------*/
+const char *ui_slot_get_next(running_machine &machine, device_slot_interface *slot)
+{
+ int idx = ui_slot_get_current_index(machine, slot) + 1;
+ if (idx==ui_slot_get_length(machine,slot)) return "";
+ return slot->get_slot_interfaces()[idx].name;
+}
+
+/*-------------------------------------------------
+ ui_slot_get_prev - returns
+-------------------------------------------------*/
+const char *ui_slot_get_prev(running_machine &machine, device_slot_interface *slot)
+{
+ int idx = ui_slot_get_current_index(machine, slot) - 1;
+ if (idx==-1) return "";
+ if (idx==-2) idx = ui_slot_get_length(machine,slot) -1;
+ if (idx==-1) return "";
+ return slot->get_slot_interfaces()[idx].name;
+}
+
+/*-------------------------------------------------
+ ui_get_slot_device - returns
+-------------------------------------------------*/
+const char *ui_get_slot_device(running_machine &machine, device_slot_interface *slot)
+{
+ return machine.options().value(slot->device().tag());
+}
+
+
+/*-------------------------------------------------
+ ui_set_use_natural_keyboard - specifies
+ whether the natural keyboard is active
+-------------------------------------------------*/
+
+void ui_set_slot_device(running_machine &machine, device_slot_interface *slot, const char *val)
+{
+ astring error;
+ machine.options().set_value(slot->device().tag(), val, OPTION_PRIORITY_CMDLINE, error);
+ assert(!error);
+}
+
+/*-------------------------------------------------
+ menu_slot_devices_populate - populates the main
+ slot device menu
+-------------------------------------------------*/
+
+static void menu_slot_devices_populate(running_machine &machine, ui_menu *menu, void *state)
+{
+ device_slot_interface *slot = NULL;
+
+ /* cycle through all devices for this system */
+ for (bool gotone = machine.devicelist().first(slot); gotone; gotone = slot->next(slot))
+ {
+ /* record the menu item */
+ const char *title = ui_get_slot_device(machine,slot);
+ ui_menu_item_append(menu, slot->device().tag(), strcmp(title,"")==0 ? "------" : title, MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, (void *)slot);
+ }
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL);
+ ui_menu_item_append(menu, "Reset", NULL, 0, NULL);
+}
+
+/*-------------------------------------------------
+ ui_menu_slot_devices - menu that
+-------------------------------------------------*/
+
+static void ui_menu_slot_devices(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ menu_slot_devices_populate(machine, menu, state);
+ }
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT) {
+ device_slot_interface *slot = (device_slot_interface *)menu_event->itemref;
+ const char *val = (menu_event->iptkey == IPT_UI_LEFT) ? ui_slot_get_prev(machine,slot) : ui_slot_get_next(machine,slot);
+ ui_set_slot_device(machine,slot,val);
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ }
+ } else if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT) {
+ machine.schedule_hard_reset();
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_main_populate - populate the main menu
+-------------------------------------------------*/
+
+static void menu_main_populate(running_machine &machine, ui_menu *menu, void *state)
+{
+ input_field_config *field;
+ input_port_config *port;
+ int has_configs = FALSE;
+ int has_analog = FALSE;
+ int has_dips = FALSE;
+
+ /* scan the input port array to see what options we need to enable */
+ for (port = machine.m_portlist.first(); port != NULL; port = port->next())
+ for (field = port->fieldlist().first(); field != NULL; field = field->next())
+ {
+ if (field->type == IPT_DIPSWITCH)
+ has_dips = TRUE;
+ if (field->type == IPT_CONFIG)
+ has_configs = TRUE;
+ if (input_type_is_analog(field->type))
+ has_analog = TRUE;
+ }
+
+ /* add input menu items */
+ ui_menu_item_append(menu, "Input (general)", NULL, 0, (void *)menu_input_groups);
+ ui_menu_item_append(menu, "Input (this " CAPSTARTGAMENOUN ")", NULL, 0, (void *)menu_input_specific);
+
+ /* add optional input-related menus */
+ if (has_dips)
+ ui_menu_item_append(menu, "Dip Switches", NULL, 0, (void *)menu_settings_dip_switches);
+ if (has_configs)
+ ui_menu_item_append(menu, "Driver Configuration", NULL, 0, (void *)menu_settings_driver_config);
+ if (has_analog)
+ ui_menu_item_append(menu, "Analog Controls", NULL, 0, (void *)menu_analog);
+
+ /* add bookkeeping menu */
+ ui_menu_item_append(menu, "Bookkeeping Info", NULL, 0, (void *)menu_bookkeeping);
+
+ /* add game info menu */
+ ui_menu_item_append(menu, CAPSTARTGAMENOUN " Information", NULL, 0, (void *)menu_game_info);
+
+ device_image_interface *image = NULL;
+ if (machine.devicelist().first(image))
+ {
+ /* add image info menu */
+ ui_menu_item_append(menu, "Image Information", NULL, 0, (void*)ui_image_menu_image_info);
+
+ /* add file manager menu */
+ ui_menu_item_append(menu, "File Manager", NULL, 0, (void*)ui_image_menu_file_manager);
+
+ /* add tape control menu */
+ if (machine.devicelist().first(CASSETTE))
+ ui_menu_item_append(menu, "Tape Control", NULL, 0, (void*)ui_mess_menu_tape_control);
+
+ /* add bitbanger control menu */
+ if (machine.devicelist().first(BITBANGER))
+ ui_menu_item_append(menu, "Bitbanger Control", NULL, 0, (void*)ui_mess_menu_bitbanger_control);
+ }
+
+ device_slot_interface *slot = NULL;
+ if (machine.devicelist().first(slot))
+ {
+ /* add image info menu */
+ ui_menu_item_append(menu, "Slot Devices", NULL, 0, (void*)ui_menu_slot_devices);
+ }
+
+ /* add keyboard mode menu */
+ if (input_machine_has_keyboard(machine) && inputx_can_post(machine))
+ ui_menu_item_append(menu, "Keyboard Mode", NULL, 0, (void *)ui_menu_keyboard_mode);
+
+ /* add sliders menu */
+ ui_menu_item_append(menu, "Slider Controls", NULL, 0, (void *)menu_sliders);
+
+ /* add video options menu */
+ ui_menu_item_append(menu, "Video Options", NULL, 0, (machine.render().target_by_index(1) != NULL) ? (void *)menu_video_targets : (void *)menu_video_options);
+
+ /* add crosshair options menu */
+ if (crosshair_get_usage(machine))
+ ui_menu_item_append(menu, "Crosshair Options", NULL, 0, (void *)menu_crosshair);
+
+ /* add cheat menu */
+ if (machine.options().cheat() && machine.cheat().first() != NULL)
+ ui_menu_item_append(menu, "Cheat", NULL, 0, (void *)menu_cheat);
+
+ /* add memory card menu */
+ if (machine.config().m_memcard_handler != NULL)
+ ui_menu_item_append(menu, "Memory Card", NULL, 0, (void *)menu_memory_card);
+
+ /* add reset and exit menus */
+ ui_menu_item_append(menu, "Select New " CAPSTARTGAMENOUN, NULL, 0, (void *)menu_select_game);
+}
+
+
+/*-------------------------------------------------
+ menu_input_groups - handle the input groups
+ menu
+-------------------------------------------------*/
+
+static void menu_input_groups(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_input_groups_populate(machine, menu, state);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+ if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT)
+ ui_menu_stack_push(ui_menu_alloc(machine, menu->container, menu_input_general, menu_event->itemref));
+}
+
+
+/*-------------------------------------------------
+ menu_input_groups_populate - populate the
+ input groups menu
+-------------------------------------------------*/
+
+static void menu_input_groups_populate(running_machine &machine, ui_menu *menu, void *state)
+{
+ int player;
+
+ /* build up the menu */
+ ui_menu_item_append(menu, "User Interface", NULL, 0, (void *)(IPG_UI + 1));
+ for (player = 0; player < MAX_PLAYERS; player++)
+ {
+ char buffer[40];
+ sprintf(buffer, "Player %d Controls", player + 1);
+ ui_menu_item_append(menu, buffer, NULL, 0, (void *)(FPTR)(IPG_PLAYER1 + player + 1));
+ }
+ ui_menu_item_append(menu, "Other Controls", NULL, 0, (void *)(FPTR)(IPG_OTHER + 1));
+}
+
+
+
+/*-------------------------------------------------
+ menu_input_general - handle the general
+ input menu
+-------------------------------------------------*/
+
+static void menu_input_general(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ menu_input_common(machine, menu, parameter, state);
+}
+
+
+/*-------------------------------------------------
+ menu_input_general_populate - populate the
+ general input menu
+-------------------------------------------------*/
+
+static void menu_input_general_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate, int group)
+{
+ input_item_data *itemlist = NULL;
+ int suborder[SEQ_TYPE_TOTAL];
+ astring tempstring;
+ int sortorder = 1;
+
+ /* create a mini lookup table for sort order based on sequence type */
+ suborder[SEQ_TYPE_STANDARD] = 0;
+ suborder[SEQ_TYPE_DECREMENT] = 1;
+ suborder[SEQ_TYPE_INCREMENT] = 2;
+
+ /* iterate over the input ports and add menu items */
+ for (input_type_entry *entry = input_type_list(machine).first(); entry != NULL; entry = entry->next())
+
+ /* add if we match the group and we have a valid name */
+ if (entry->group == group && entry->name != NULL && entry->name[0] != 0)
+ {
+ input_seq_type seqtype;
+
+ /* loop over all sequence types */
+ sortorder++;
+ for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++)
+ {
+ /* build an entry for the standard sequence */
+ input_item_data *item = (input_item_data *)ui_menu_pool_alloc(menu, sizeof(*item));
+ memset(item, 0, sizeof(*item));
+ item->ref = entry;
+ item->seqtype = seqtype;
+ item->seq = input_type_seq(machine, entry->type, entry->player, seqtype);
+ item->defseq = &entry->defseq[seqtype];
+ item->sortorder = sortorder * 4 + suborder[seqtype];
+ item->type = input_type_is_analog(entry->type) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
+ item->name = entry->name;
+ item->next = itemlist;
+ itemlist = item;
+
+ /* stop after one, unless we're analog */
+ if (item->type == INPUT_TYPE_DIGITAL)
+ break;
+ }
+ }
+
+ /* sort and populate the menu in a standard fashion */
+ menu_input_populate_and_sort(machine, menu, itemlist, menustate);
+}
+
+
+/*-------------------------------------------------
+ menu_input_specific - handle the game-specific
+ input menu
+-------------------------------------------------*/
+
+static void menu_input_specific(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ menu_input_common(machine, menu, NULL, state);
+}
+
+
+/*-------------------------------------------------
+ menu_input_specific_populate - populate
+ the input menu with game-specific items
+-------------------------------------------------*/
+
+static void menu_input_specific_populate(running_machine &machine, ui_menu *menu, input_menu_state *menustate)
+{
+ input_item_data *itemlist = NULL;
+ input_field_config *field;
+ input_port_config *port;
+ int suborder[SEQ_TYPE_TOTAL];
+ astring tempstring;
+
+ /* create a mini lookup table for sort order based on sequence type */
+ suborder[SEQ_TYPE_STANDARD] = 0;
+ suborder[SEQ_TYPE_DECREMENT] = 1;
+ suborder[SEQ_TYPE_INCREMENT] = 2;
+
+ /* iterate over the input ports and add menu items */
+ for (port = machine.m_portlist.first(); port != NULL; port = port->next())
+ for (field = port->fieldlist().first(); field != NULL; field = field->next())
+ {
+ const char *name = input_field_name(field);
+
+ /* add if we match the group and we have a valid name */
+ if (name != NULL && input_condition_true(machine, &field->condition, port->owner()) &&
+ ((field->type == IPT_OTHER && field->name != NULL) || input_type_group(machine, field->type, field->player) != IPG_INVALID))
+ {
+ input_seq_type seqtype;
+ UINT16 sortorder;
+
+ /* determine the sorting order */
+ if (field->type >= IPT_START1 && field->type <= __ipt_analog_end)
+ sortorder = (field->type << 2) | (field->player << 12);
+ else
+ sortorder = field->type | 0xf000;
+
+ /* loop over all sequence types */
+ for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; seqtype++)
+ {
+ /* build an entry for the standard sequence */
+ input_item_data *item = (input_item_data *)ui_menu_pool_alloc(menu, sizeof(*item));
+ memset(item, 0, sizeof(*item));
+ item->ref = field;
+ item->seqtype = seqtype;
+ item->seq = input_field_seq(field, seqtype);
+ item->defseq = &get_field_default_seq(field, seqtype);
+ item->sortorder = sortorder + suborder[seqtype];
+ item->type = input_type_is_analog(field->type) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL;
+ item->name = name;
+ item->next = itemlist;
+ itemlist = item;
+
+ /* stop after one, unless we're analog */
+ if (item->type == INPUT_TYPE_DIGITAL)
+ break;
+ }
+ }
+ }
+
+ /* sort and populate the menu in a standard fashion */
+ menu_input_populate_and_sort(machine, menu, itemlist, menustate);
+}
+
+
+/*-------------------------------------------------
+ menu_input - display a menu for inputs
+-------------------------------------------------*/
+
+static void menu_input_common(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ input_item_data *seqchangeditem = NULL;
+ input_menu_state *menustate;
+ const ui_menu_event *menu_event;
+ int invalidate = FALSE;
+
+ /* if no state, allocate now */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL);
+ menustate = (input_menu_state *)state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ if (parameter != NULL)
+ menu_input_general_populate(machine, menu, menustate, (FPTR)parameter - 1);
+ else
+ menu_input_specific_populate(machine, menu, menustate);
+ }
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, (menustate->pollingitem != NULL) ? UI_MENU_PROCESS_NOKEYS : 0);
+
+ /* if we are polling, handle as a special case */
+ if (menustate->pollingitem != NULL)
+ {
+ input_item_data *item = menustate->pollingitem;
+ input_seq newseq;
+
+ /* if UI_CANCEL is pressed, abort */
+ if (ui_input_pressed(machine, IPT_UI_CANCEL))
+ {
+ menustate->pollingitem = NULL;
+ menustate->record_next = FALSE;
+ toggle_none_default(item->seq, menustate->starting_seq, *item->defseq);
+ seqchangeditem = item;
+ }
+
+ /* poll again; if finished, update the sequence */
+ if (machine.input().seq_poll())
+ {
+ menustate->pollingitem = NULL;
+ menustate->record_next = TRUE;
+ item->seq = machine.input().seq_poll_final();
+ seqchangeditem = item;
+ }
+ }
+
+ /* otherwise, handle the events */
+ else if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ input_item_data *item = (input_item_data *)menu_event->itemref;
+ switch (menu_event->iptkey)
+ {
+ /* an item was selected: begin polling */
+ case IPT_UI_SELECT:
+ menustate->pollingitem = item;
+ menustate->last_sortorder = item->sortorder;
+ menustate->starting_seq = item->seq;
+ machine.input().seq_poll_start((item->type == INPUT_TYPE_ANALOG) ? ITEM_CLASS_ABSOLUTE : ITEM_CLASS_SWITCH, menustate->record_next ? &item->seq : NULL);
+ invalidate = TRUE;
+ break;
+
+ /* if the clear key was pressed, reset the selected item */
+ case IPT_UI_CLEAR:
+ toggle_none_default(item->seq, item->seq, *item->defseq);
+ menustate->record_next = FALSE;
+ seqchangeditem = item;
+ break;
+ }
+
+ /* if the selection changed, reset the "record next" flag */
+ if (item->sortorder != menustate->last_sortorder)
+ menustate->record_next = FALSE;
+ menustate->last_sortorder = item->sortorder;
+ }
+
+ /* if the sequence changed, update it */
+ if (seqchangeditem != NULL)
+ {
+ /* update a general input */
+ if (parameter != NULL)
+ {
+ const input_type_entry *entry = (const input_type_entry *)seqchangeditem->ref;
+ input_type_set_seq(machine, entry->type, entry->player, seqchangeditem->seqtype, &seqchangeditem->seq);
+ }
+
+ /* update a game-specific input */
+ else
+ {
+ input_field_user_settings settings;
+
+ input_field_get_user_settings((input_field_config *)seqchangeditem->ref, &settings);
+ settings.seq[seqchangeditem->seqtype] = seqchangeditem->seq;
+ input_field_set_user_settings((input_field_config *)seqchangeditem->ref, &settings);
+ }
+
+ /* invalidate the menu to force an update */
+ invalidate = TRUE;
+ }
+
+ /* if the menu is invalidated, clear it now */
+ if (invalidate)
+ {
+ menustate->pollingref = NULL;
+ if (menustate->pollingitem != NULL)
+ menustate->pollingref = menustate->pollingitem->ref;
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_input_compare_items - compare two
+ items for quicksort
+-------------------------------------------------*/
+
+static int menu_input_compare_items(const void *i1, const void *i2)
+{
+ const input_item_data * const *data1 = (const input_item_data * const *)i1;
+ const input_item_data * const *data2 = (const input_item_data * const *)i2;
+ if ((*data1)->sortorder < (*data2)->sortorder)
+ return -1;
+ if ((*data1)->sortorder > (*data2)->sortorder)
+ return 1;
+ return 0;
+}
+
+
+/*-------------------------------------------------
+ menu_input_populate_and_sort - take a list
+ of input_item_data objects and build up the
+ menu from them
+-------------------------------------------------*/
+
+static void menu_input_populate_and_sort(running_machine &machine, ui_menu *menu, input_item_data *itemlist, input_menu_state *menustate)
+{
+ const char *nameformat[INPUT_TYPE_TOTAL] = { 0 };
+ input_item_data **itemarray, *item;
+ int numitems = 0, curitem;
+ astring subtext;
+ astring text;
+
+ /* create a mini lookup table for name format based on type */
+ nameformat[INPUT_TYPE_DIGITAL] = "%s";
+ nameformat[INPUT_TYPE_ANALOG] = "%s Analog";
+ nameformat[INPUT_TYPE_ANALOG_INC] = "%s Analog Inc";
+ nameformat[INPUT_TYPE_ANALOG_DEC] = "%s Analog Dec";
+
+ /* first count the number of items */
+ for (item = itemlist; item != NULL; item = item->next)
+ numitems++;
+
+ /* now allocate an array of items and fill it up */
+ itemarray = (input_item_data **)ui_menu_pool_alloc(menu, sizeof(*itemarray) * numitems);
+ for (item = itemlist, curitem = 0; item != NULL; item = item->next)
+ itemarray[curitem++] = item;
+
+ /* sort it */
+ qsort(itemarray, numitems, sizeof(*itemarray), menu_input_compare_items);
+
+ /* build the menu */
+ for (curitem = 0; curitem < numitems; curitem++)
+ {
+ UINT32 flags = 0;
+
+ /* generate the name of the item itself, based off the base name and the type */
+ item = itemarray[curitem];
+ assert(nameformat[item->type] != NULL);
+ text.printf(nameformat[item->type], item->name);
+
+ /* if we're polling this item, use some spaces with left/right arrows */
+ if (menustate->pollingref == item->ref)
+ {
+ subtext.cpy(" ");
+ flags |= MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW;
+ }
+
+ /* otherwise, generate the sequence name and invert it if different from the default */
+ else
+ {
+ machine.input().seq_name(subtext, item->seq);
+ flags |= (item->seq != *item->defseq) ? MENU_FLAG_INVERT : 0;
+ }
+
+ /* add the item */
+ ui_menu_item_append(menu, text, subtext, flags, item);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_settings_dip_switches - handle the DIP
+ switches menu
+-------------------------------------------------*/
+
+static void menu_settings_dip_switches(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ menu_settings_common(machine, menu, state, IPT_DIPSWITCH);
+}
+
+
+/*-------------------------------------------------
+ menu_settings_driver_config - handle the
+ driver config menu
+-------------------------------------------------*/
+
+static void menu_settings_driver_config(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ menu_settings_common(machine, menu, state, IPT_CONFIG);
+}
+
+
+/*-------------------------------------------------
+ menu_settings_common - handle one of the
+ switches menus
+-------------------------------------------------*/
+
+static void menu_settings_common(running_machine &machine, ui_menu *menu, void *state, UINT32 type)
+{
+ settings_menu_state *menustate;
+ const ui_menu_event *menu_event;
+
+ /* if no state, allocate now */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*menustate), NULL);
+ menustate = (settings_menu_state *)state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_settings_populate(machine, menu, menustate, type);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+
+ /* handle events */
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ input_field_config *field = (input_field_config *)menu_event->itemref;
+ input_field_user_settings settings;
+ int changed = FALSE;
+
+ switch (menu_event->iptkey)
+ {
+ /* if selected, reset to default value */
+ case IPT_UI_SELECT:
+ input_field_get_user_settings(field, &settings);
+ settings.value = field->defvalue;
+ input_field_set_user_settings(field, &settings);
+ changed = TRUE;
+ break;
+
+ /* left goes to previous setting */
+ case IPT_UI_LEFT:
+ input_field_select_previous_setting(field);
+ changed = TRUE;
+ break;
+
+ /* right goes to next setting */
+ case IPT_UI_RIGHT:
+ input_field_select_next_setting(field);
+ changed = TRUE;
+ break;
+ }
+
+ /* if anything changed, rebuild the menu, trying to stay on the same field */
+ if (changed)
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_settings_populate - populate one of the
+ switches menus
+-------------------------------------------------*/
+
+static void menu_settings_populate(running_machine &machine, ui_menu *menu, settings_menu_state *menustate, UINT32 type)
+{
+ input_field_config *field;
+ input_port_config *port;
+ dip_descriptor **diplist_tailptr;
+ int dipcount = 0;
+
+ /* reset the dip switch tracking */
+ menustate->diplist = NULL;
+ diplist_tailptr = &menustate->diplist;
+
+ /* loop over input ports and set up the current values */
+ for (port = machine.m_portlist.first(); port != NULL; port = port->next())
+ for (field = port->fieldlist().first(); field != NULL; field = field->next())
+ if (field->type == type && input_condition_true(machine, &field->condition, port->owner()))
+ {
+ UINT32 flags = 0;
+
+ /* set the left/right flags appropriately */
+ if (input_field_has_previous_setting(field))
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (input_field_has_next_setting(field))
+ flags |= MENU_FLAG_RIGHT_ARROW;
+
+ /* add the menu item */
+ ui_menu_item_append(menu, input_field_name(field), input_field_setting_name(field), flags, (void *)field);
+
+ /* for DIP switches, build up the model */
+ if (type == IPT_DIPSWITCH && field->diploclist().count() != 0)
+ {
+ const input_field_diplocation *diploc;
+ input_field_user_settings settings;
+ UINT32 accummask = field->mask;
+
+ /* get current settings */
+ input_field_get_user_settings(field, &settings);
+
+ /* iterate over each bit in the field */
+ for (diploc = field->diploclist().first(); diploc != NULL; diploc = diploc->next())
+ {
+ UINT32 mask = accummask & ~(accummask - 1);
+ dip_descriptor *dip;
+
+ /* find the matching switch name */
+ for (dip = menustate->diplist; dip != NULL; dip = dip->next)
+ if (strcmp(dip->name, diploc->swname) == 0)
+ break;
+
+ /* allocate new if none */
+ if (dip == NULL)
+ {
+ dip = (dip_descriptor *)ui_menu_pool_alloc(menu, sizeof(*dip));
+ dip->next = NULL;
+ dip->name = diploc->swname;
+ dip->mask = dip->state = 0;
+ *diplist_tailptr = dip;
+ diplist_tailptr = &dip->next;
+ dipcount++;
+ }
+
+ /* apply the bits */
+ dip->mask |= 1 << (diploc->swnum - 1);
+ if (((settings.value & mask) != 0 && !diploc->invert) || ((settings.value & mask) == 0 && diploc->invert))
+ dip->state |= 1 << (diploc->swnum - 1);
+
+ /* clear the relevant bit in the accumulated mask */
+ accummask &= ~mask;
+ }
+ }
+ }
+
+ /* configure the extra menu */
+ if (type == IPT_DIPSWITCH && menustate->diplist != NULL)
+ ui_menu_set_custom_render(menu, menu_settings_custom_render, 0.0f, dipcount * (DIP_SWITCH_HEIGHT + DIP_SWITCH_SPACING) + DIP_SWITCH_SPACING);
+}
+
+
+/*-------------------------------------------------
+ menu_settings_custom_render - perform our special
+ rendering
+-------------------------------------------------*/
+
+static void menu_settings_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+ input_field_config *field = (input_field_config *)selectedref;
+ settings_menu_state *menustate = (settings_menu_state *)state;
+ dip_descriptor *dip;
+
+ /* add borders */
+ y1 = y2 + UI_BOX_TB_BORDER;
+ y2 = y1 + bottom;
+
+ /* draw extra menu area */
+ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
+ y1 += (float)DIP_SWITCH_SPACING;
+
+ /* iterate over DIP switches */
+ for (dip = menustate->diplist; dip != NULL; dip = dip->next)
+ {
+ const input_field_diplocation *diploc;
+ UINT32 selectedmask = 0;
+
+ /* determine the mask of selected bits */
+ if (field != NULL)
+ for (diploc = field->diploclist().first(); diploc != NULL; diploc = diploc->next())
+ if (strcmp(dip->name, diploc->swname) == 0)
+ selectedmask |= 1 << (diploc->swnum - 1);
+
+ /* draw one switch */
+ menu_settings_custom_render_one(menu->container, x1, y1, x2, y1 + DIP_SWITCH_HEIGHT, dip, selectedmask);
+ y1 += (float)(DIP_SWITCH_SPACING + DIP_SWITCH_HEIGHT);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_settings_custom_render_one - draw a single
+ DIP switch
+-------------------------------------------------*/
+
+static void menu_settings_custom_render_one(render_container *container, float x1, float y1, float x2, float y2, const dip_descriptor *dip, UINT32 selectedmask)
+{
+ float switch_field_width = SINGLE_TOGGLE_SWITCH_FIELD_WIDTH * container->manager().ui_aspect();
+ float switch_width = SINGLE_TOGGLE_SWITCH_WIDTH * container->manager().ui_aspect();
+ int numtoggles, toggle;
+ float switch_toggle_gap;
+ float y1_off, y1_on;
+
+ /* determine the number of toggles in the DIP */
+ numtoggles = 32 - count_leading_zeros(dip->mask);
+
+ /* center based on the number of switches */
+ x1 += (x2 - x1 - numtoggles * switch_field_width) / 2;
+
+ /* draw the dip switch name */
+ ui_draw_text_full( container,
+ dip->name,
+ 0,
+ y1 + (DIP_SWITCH_HEIGHT - UI_TARGET_FONT_HEIGHT) / 2,
+ x1 - ui_get_string_width(container->manager().machine(), " "),
+ JUSTIFY_RIGHT,
+ WRAP_NEVER,
+ DRAW_NORMAL,
+ UI_TEXT_COLOR,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA),
+ NULL ,
+ NULL);
+
+ /* compute top and bottom for on and off positions */
+ switch_toggle_gap = ((DIP_SWITCH_HEIGHT/2) - SINGLE_TOGGLE_SWITCH_HEIGHT)/2;
+ y1_off = y1 + UI_LINE_WIDTH + switch_toggle_gap;
+ y1_on = y1 + DIP_SWITCH_HEIGHT/2 + switch_toggle_gap;
+
+ /* iterate over toggles */
+ for (toggle = 0; toggle < numtoggles; toggle++)
+ {
+ float innerx1;
+
+ /* first outline the switch */
+ ui_draw_outlined_box(container, x1, y1, x1 + switch_field_width, y2, UI_BACKGROUND_COLOR);
+
+ /* compute x1/x2 for the inner filled in switch */
+ innerx1 = x1 + (switch_field_width - switch_width) / 2;
+
+ /* see if the switch is actually used */
+ if (dip->mask & (1 << toggle))
+ {
+ float innery1 = (dip->state & (1 << toggle)) ? y1_on : y1_off;
+ container->add_rect(innerx1, innery1, innerx1 + switch_width, innery1 + SINGLE_TOGGLE_SWITCH_HEIGHT,
+ (selectedmask & (1 << toggle)) ? UI_DIPSW_COLOR : UI_TEXT_COLOR,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+ else
+ {
+ container->add_rect(innerx1, y1_off, innerx1 + switch_width, y1_on + SINGLE_TOGGLE_SWITCH_HEIGHT,
+ UI_UNAVAILABLE_COLOR,
+ PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ }
+
+ /* advance to the next switch */
+ x1 += switch_field_width;
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_analog - handle the analog settings menu
+-------------------------------------------------*/
+
+static void menu_analog(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_analog_populate(machine, menu);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+
+ /* handle events */
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ analog_item_data *data = (analog_item_data *)menu_event->itemref;
+ int newval = data->cur;
+
+ switch (menu_event->iptkey)
+ {
+ /* if selected, reset to default value */
+ case IPT_UI_SELECT:
+ newval = data->defvalue;
+ break;
+
+ /* left decrements */
+ case IPT_UI_LEFT:
+ newval -= machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
+
+ /* right increments */
+ case IPT_UI_RIGHT:
+ newval += machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
+ }
+
+ /* clamp to range */
+ if (newval < data->min)
+ newval = data->min;
+ if (newval > data->max)
+ newval = data->max;
+
+ /* if things changed, update */
+ if (newval != data->cur)
+ {
+ input_field_user_settings settings;
+
+ /* get the settings and set the new value */
+ input_field_get_user_settings(data->field, &settings);
+ switch (data->type)
+ {
+ case ANALOG_ITEM_KEYSPEED: settings.delta = newval; break;
+ case ANALOG_ITEM_CENTERSPEED: settings.centerdelta = newval; break;
+ case ANALOG_ITEM_REVERSE: settings.reverse = newval; break;
+ case ANALOG_ITEM_SENSITIVITY: settings.sensitivity = newval; break;
+ }
+ input_field_set_user_settings(data->field, &settings);
+
+ /* rebuild the menu */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_analog_populate - populate the analog
+ settings menu
+-------------------------------------------------*/
+
+static void menu_analog_populate(running_machine &machine, ui_menu *menu)
+{
+ input_field_config *field;
+ input_port_config *port;
+ astring subtext;
+ astring text;
+
+ /* loop over input ports and add the items */
+ for (port = machine.m_portlist.first(); port != NULL; port = port->next())
+ for (field = port->fieldlist().first(); field != NULL; field = field->next())
+ if (input_type_is_analog(field->type) && input_condition_true(machine, &field->condition, port->owner()))
+ {
+ input_field_user_settings settings;
+ int use_autocenter = FALSE;
+ int type;
+
+ /* based on the type, determine if we enable autocenter */
+ switch (field->type)
+ {
+ case IPT_POSITIONAL:
+ case IPT_POSITIONAL_V:
+ if (field->flags & ANALOG_FLAG_WRAPS)
+ break;
+
+ case IPT_PEDAL:
+ case IPT_PEDAL2:
+ case IPT_PEDAL3:
+ case IPT_PADDLE:
+ case IPT_PADDLE_V:
+ case IPT_AD_STICK_X:
+ case IPT_AD_STICK_Y:
+ case IPT_AD_STICK_Z:
+ use_autocenter = TRUE;
+ break;
+ }
+
+ /* get the user settings */
+ input_field_get_user_settings(field, &settings);
+
+ /* iterate over types */
+ for (type = 0; type < ANALOG_ITEM_COUNT; type++)
+ if (type != ANALOG_ITEM_CENTERSPEED || use_autocenter)
+ {
+ analog_item_data *data;
+ UINT32 flags = 0;
+
+ /* allocate a data item for tracking what this menu item refers to */
+ data = (analog_item_data *)ui_menu_pool_alloc(menu, sizeof(*data));
+ data->field = field;
+ data->type = type;
+
+ /* determine the properties of this item */
+ switch (type)
+ {
+ default:
+ case ANALOG_ITEM_KEYSPEED:
+ text.printf("%s Digital Speed", input_field_name(field));
+ subtext.printf("%d", settings.delta);
+ data->min = 0;
+ data->max = 255;
+ data->cur = settings.delta;
+ data->defvalue = field->delta;
+ break;
+
+ case ANALOG_ITEM_CENTERSPEED:
+ text.printf("%s Autocenter Speed", input_field_name(field));
+ subtext.printf("%d", settings.centerdelta);
+ data->min = 0;
+ data->max = 255;
+ data->cur = settings.centerdelta;
+ data->defvalue = field->centerdelta;
+ break;
+
+ case ANALOG_ITEM_REVERSE:
+ text.printf("%s Reverse", input_field_name(field));
+ subtext.cpy(settings.reverse ? "On" : "Off");
+ data->min = 0;
+ data->max = 1;
+ data->cur = settings.reverse;
+ data->defvalue = ((field->flags & ANALOG_FLAG_REVERSE) != 0);
+ break;
+
+ case ANALOG_ITEM_SENSITIVITY:
+ text.printf("%s Sensitivity", input_field_name(field));
+ subtext.printf("%d", settings.sensitivity);
+ data->min = 1;
+ data->max = 255;
+ data->cur = settings.sensitivity;
+ data->defvalue = field->sensitivity;
+ break;
+ }
+
+ /* put on arrows */
+ if (data->cur > data->min)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (data->cur < data->max)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+
+ /* append a menu item */
+ ui_menu_item_append(menu, text, subtext, flags, data);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_bookkeeping - handle the bookkeeping
+ information menu
+-------------------------------------------------*/
+
+static void menu_bookkeeping(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ attotime *prevtime;
+ attotime curtime;
+
+ /* if no state, allocate some */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*prevtime), NULL);
+ prevtime = (attotime *)state;
+
+ /* if the time has rolled over another second, regenerate */
+ curtime = machine.time();
+ if (prevtime->seconds != curtime.seconds)
+ {
+ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST);
+ *prevtime = curtime;
+ menu_bookkeeping_populate(machine, menu, prevtime);
+ }
+
+ /* process the menu */
+ ui_menu_process(machine, menu, 0);
+}
+
+
+/*-------------------------------------------------
+ menu_bookkeeping - handle the bookkeeping
+ information menu
+-------------------------------------------------*/
+
+static void menu_bookkeeping_populate(running_machine &machine, ui_menu *menu, attotime *curtime)
+{
+ int tickets = get_dispensed_tickets(machine);
+ astring tempstring;
+ int ctrnum;
+
+ /* show total time first */
+ if (curtime->seconds >= 60 * 60)
+ tempstring.catprintf("Uptime: %d:%02d:%02d\n\n", curtime->seconds / (60*60), (curtime->seconds / 60) % 60, curtime->seconds % 60);
+ else
+ tempstring.catprintf("Uptime: %d:%02d\n\n", (curtime->seconds / 60) % 60, curtime->seconds % 60);
+
+ /* show tickets at the top */
+ if (tickets > 0)
+ tempstring.catprintf("Tickets dispensed: %d\n\n", tickets);
+
+ /* loop over coin counters */
+ for (ctrnum = 0; ctrnum < COIN_COUNTERS; ctrnum++)
+ {
+ int count = coin_counter_get_count(machine, ctrnum);
+
+ /* display the coin counter number */
+ tempstring.catprintf("Coin %c: ", ctrnum + 'A');
+
+ /* display how many coins */
+ if (count == 0)
+ tempstring.cat("NA");
+ else
+ tempstring.catprintf("%d", count);
+
+ /* display whether or not we are locked out */
+ if (coin_lockout_get_state(machine, ctrnum))
+ tempstring.cat(" (locked)");
+ tempstring.cat("\n");
+ }
+
+ /* append the single item */
+ ui_menu_item_append(menu, tempstring, NULL, MENU_FLAG_MULTILINE, NULL);
+}
+
+
+/*-------------------------------------------------
+ menu_game_info - handle the game information
+ menu
+-------------------------------------------------*/
+
+static void menu_game_info(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ {
+ astring tempstring;
+ ui_menu_item_append(menu, game_info_astring(machine, tempstring), NULL, MENU_FLAG_MULTILINE, NULL);
+ }
+
+ /* process the menu */
+ ui_menu_process(machine, menu, 0);
+}
+
+
+/*-------------------------------------------------
+ menu_cheat - handle the cheat menu
+-------------------------------------------------*/
+
+static void menu_cheat(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_cheat_populate(machine, menu);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+
+ /* handle events */
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ bool changed = false;
+
+ /* clear cheat comment on any movement or keypress */
+ popmessage(NULL);
+
+ /* handle reset all + reset all cheats for reload all option */
+ if ((FPTR)menu_event->itemref < 3 && menu_event->iptkey == IPT_UI_SELECT)
+ {
+ for (cheat_entry *curcheat = machine.cheat().first(); curcheat != NULL; curcheat = curcheat->next())
+ if (curcheat->select_default_state())
+ changed = true;
+ }
+
+
+ /* handle individual cheats */
+ else if ((FPTR)menu_event->itemref > 2)
+ {
+ cheat_entry *curcheat = reinterpret_cast<cheat_entry *>(menu_event->itemref);
+ const char *string;
+ switch (menu_event->iptkey)
+ {
+ /* if selected, activate a oneshot */
+ case IPT_UI_SELECT:
+ changed = curcheat->activate();
+ break;
+
+ /* if cleared, reset to default value */
+ case IPT_UI_CLEAR:
+ changed = curcheat->select_default_state();
+ break;
+
+ /* left decrements */
+ case IPT_UI_LEFT:
+ changed = curcheat->select_previous_state();
+ break;
+
+ /* right increments */
+ case IPT_UI_RIGHT:
+ changed = curcheat->select_next_state();
+ break;
+
+ /* bring up display comment if one exists */
+ case IPT_UI_DISPLAY_COMMENT:
+ case IPT_UI_UP:
+ case IPT_UI_DOWN:
+ string = curcheat->comment();
+ if (string != NULL && string[0] != 0)
+ popmessage("Cheat Comment:\n%s", string);
+ break;
+ }
+ }
+
+ /* handle reload all */
+ if ((FPTR)menu_event->itemref == 2 && menu_event->iptkey == IPT_UI_SELECT)
+ {
+ /* re-init cheat engine and thus reload cheats/cheats have already been turned off by here */
+ machine.cheat().reload();
+
+ /* display the reloaded cheats */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ popmessage("All cheats reloaded");
+ }
+
+ /* if things changed, update */
+ if (changed)
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_cheat_populate - populate the cheat menu
+-------------------------------------------------*/
+
+static void menu_cheat_populate(running_machine &machine, ui_menu *menu)
+{
+ /* iterate over cheats */
+ astring text;
+ astring subtext;
+ for (cheat_entry *curcheat = machine.cheat().first(); curcheat != NULL; curcheat = curcheat->next())
+ {
+ UINT32 flags;
+ curcheat->menu_text(text, subtext, flags);
+ ui_menu_item_append(menu, text, subtext, flags, curcheat);
+ }
+
+ /* add a separator */
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL);
+
+ /* add a reset all option */
+ ui_menu_item_append(menu, "Reset All", NULL, 0, (void *)1);
+
+ /* add a reload all cheats option */
+ ui_menu_item_append(menu, "Reload All", NULL, 0, (void *)2);
+}
+
+
+/*-------------------------------------------------
+ menu_memory_card - handle the memory card
+ menu
+-------------------------------------------------*/
+
+static void menu_memory_card(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+ int *cardnum;
+
+ /* if no state, allocate some */
+ if (state == NULL)
+ state = ui_menu_alloc_state(menu, sizeof(*cardnum), NULL);
+ cardnum = (int *)state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_memory_card_populate(machine, menu, *cardnum);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+
+ /* if something was selected, act on it */
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ FPTR item = (FPTR)menu_event->itemref;
+
+ /* select executes actions on some of the items */
+ if (menu_event->iptkey == IPT_UI_SELECT)
+ {
+ switch (item)
+ {
+ /* handle card loading; if we succeed, clear the menus */
+ case MEMCARD_ITEM_LOAD:
+ if (memcard_insert(menu->machine(), *cardnum) == 0)
+ {
+ popmessage("Memory card loaded");
+ ui_menu_stack_reset(menu->machine());
+ }
+ else
+ popmessage("Error loading memory card");
+ break;
+
+ /* handle card ejecting */
+ case MEMCARD_ITEM_EJECT:
+ memcard_eject(menu->machine());
+ popmessage("Memory card ejected");
+ break;
+
+ /* handle card creating */
+ case MEMCARD_ITEM_CREATE:
+ if (memcard_create(menu->machine(), *cardnum, FALSE) == 0)
+ popmessage("Memory card created");
+ else
+ popmessage("Error creating memory card\n(Card may already exist)");
+ break;
+ }
+ }
+
+ /* the select item has extra keys */
+ else if (item == MEMCARD_ITEM_SELECT)
+ {
+ switch (menu_event->iptkey)
+ {
+ /* left decrements the card number */
+ case IPT_UI_LEFT:
+ *cardnum -= 1;
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ break;
+
+ /* right decrements the card number */
+ case IPT_UI_RIGHT:
+ *cardnum += 1;
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ break;
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_memory_card_populate - populate the
+ memory card menu
+-------------------------------------------------*/
+
+static void menu_memory_card_populate(running_machine &machine, ui_menu *menu, int cardnum)
+{
+ char tempstring[20];
+ UINT32 flags = 0;
+
+ /* add the card select menu */
+ sprintf(tempstring, "%d", cardnum);
+ if (cardnum > 0)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (cardnum < 1000)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ ui_menu_item_append(menu, "Card Number:", tempstring, flags, (void *)MEMCARD_ITEM_SELECT);
+
+ /* add the remaining items */
+ ui_menu_item_append(menu, "Load Selected Card", NULL, 0, (void *)MEMCARD_ITEM_LOAD);
+ if (memcard_present(machine) != -1)
+ ui_menu_item_append(menu, "Eject Current Card", NULL, 0, (void *)MEMCARD_ITEM_EJECT);
+ ui_menu_item_append(menu, "Create New Card", NULL, 0, (void *)MEMCARD_ITEM_CREATE);
+}
+
+
+/*-------------------------------------------------
+ menu_sliders - handle the sliders menu
+-------------------------------------------------*/
+
+static void menu_sliders(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ int menuless_mode = (parameter != NULL);
+ const ui_menu_event *menu_event;
+ UINT8 *hidden = (UINT8 *)state;
+
+ /* if no state, allocate some */
+ if (hidden == NULL)
+ hidden = (UINT8 *)ui_menu_alloc_state(menu, sizeof(*hidden), NULL);
+ if (menuless_mode)
+ *hidden = TRUE;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_sliders_populate(machine, menu, menuless_mode);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT | (*hidden ? UI_MENU_PROCESS_CUSTOM_ONLY : 0));
+ if (menu_event != NULL)
+ {
+ /* handle keys if there is a valid item selected */
+ if (menu_event->itemref != NULL)
+ {
+ const slider_state *slider = (const slider_state *)menu_event->itemref;
+ INT32 curvalue = (*slider->update)(machine, slider->arg, NULL, SLIDER_NOCHANGE);
+ INT32 increment = 0;
+
+ switch (menu_event->iptkey)
+ {
+ /* toggle visibility */
+ case IPT_UI_ON_SCREEN_DISPLAY:
+ if (menuless_mode)
+ ui_menu_stack_pop(machine);
+ else
+ *hidden = !*hidden;
+ break;
+
+ /* decrease value */
+ case IPT_UI_LEFT:
+ if (machine.input().code_pressed(KEYCODE_LALT) || machine.input().code_pressed(KEYCODE_RALT))
+ increment = -1;
+ else if (machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT))
+ increment = (slider->incval > 10) ? -(slider->incval / 10) : -1;
+ else if (machine.input().code_pressed(KEYCODE_LCONTROL) || machine.input().code_pressed(KEYCODE_RCONTROL))
+ increment = -slider->incval * 10;
+ else
+ increment = -slider->incval;
+ break;
+
+ /* increase value */
+ case IPT_UI_RIGHT:
+ if (machine.input().code_pressed(KEYCODE_LALT) || machine.input().code_pressed(KEYCODE_RALT))
+ increment = 1;
+ else if (machine.input().code_pressed(KEYCODE_LSHIFT) || machine.input().code_pressed(KEYCODE_RSHIFT))
+ increment = (slider->incval > 10) ? (slider->incval / 10) : 1;
+ else if (machine.input().code_pressed(KEYCODE_LCONTROL) || machine.input().code_pressed(KEYCODE_RCONTROL))
+ increment = slider->incval * 10;
+ else
+ increment = slider->incval;
+ break;
+
+ /* restore default */
+ case IPT_UI_SELECT:
+ increment = slider->defval - curvalue;
+ break;
+ }
+
+ /* handle any changes */
+ if (increment != 0)
+ {
+ INT32 newvalue = curvalue + increment;
+
+ /* clamp within bounds */
+ if (newvalue < slider->minval)
+ newvalue = slider->minval;
+ if (newvalue > slider->maxval)
+ newvalue = slider->maxval;
+
+ /* update the slider and recompute the menu */
+ (*slider->update)(machine, slider->arg, NULL, newvalue);
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ }
+ }
+
+ /* if we are selecting an invalid item and we are hidden, skip to the next one */
+ else if (*hidden)
+ {
+ /* if we got here via up or page up, select the previous item */
+ if (menu_event->iptkey == IPT_UI_UP || menu_event->iptkey == IPT_UI_PAGE_UP)
+ {
+ menu->selected = (menu->selected + menu->numitems - 1) % menu->numitems;
+ ui_menu_validate_selection(menu, -1);
+ }
+
+ /* otherwise select the next item */
+ else if (menu_event->iptkey == IPT_UI_DOWN || menu_event->iptkey == IPT_UI_PAGE_DOWN)
+ {
+ menu->selected = (menu->selected + 1) % menu->numitems;
+ ui_menu_validate_selection(menu, 1);
+ }
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_sliders_populate - populate the sliders
+ menu
+-------------------------------------------------*/
+
+static void menu_sliders_populate(running_machine &machine, ui_menu *menu, int menuless_mode)
+{
+ const slider_state *curslider;
+ astring tempstring;
+
+ /* add all sliders */
+ for (curslider = ui_get_slider_list(); curslider != NULL; curslider = curslider->next)
+ {
+ INT32 curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE);
+ UINT32 flags = 0;
+ if (curval > curslider->minval)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (curval < curslider->maxval)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ ui_menu_item_append(menu, curslider->description, tempstring, flags, (void *)curslider);
+
+ if (menuless_mode)
+ break;
+ }
+
+ /* add all sliders */
+ for (curslider = (slider_state*)machine.osd().get_slider_list(); curslider != NULL; curslider = curslider->next)
+ {
+ INT32 curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE);
+ UINT32 flags = 0;
+ if (curval > curslider->minval)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (curval < curslider->maxval)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ ui_menu_item_append(menu, curslider->description, tempstring, flags, (void *)curslider);
+ }
+
+ ui_menu_set_custom_render(menu, menu_sliders_custom_render, 0.0f, 2.0f * ui_get_line_height(machine) + 2.0f * UI_BOX_TB_BORDER);
+}
+
+
+/*-------------------------------------------------
+ menu_sliders_custom_render - perform our special
+ rendering
+-------------------------------------------------*/
+
+static void menu_sliders_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float x1, float y1, float x2, float y2)
+{
+ const slider_state *curslider = (const slider_state *)selectedref;
+ if (curslider != NULL)
+ {
+ float bar_left, bar_area_top, bar_width, bar_area_height, bar_top, bar_bottom, default_x, current_x;
+ float line_height = ui_get_line_height(machine);
+ float percentage, default_percentage;
+ astring tempstring;
+ float text_height;
+ INT32 curval;
+
+ /* determine the current value and text */
+ curval = (*curslider->update)(machine, curslider->arg, &tempstring, SLIDER_NOCHANGE);
+
+ /* compute the current and default percentages */
+ percentage = (float)(curval - curslider->minval) / (float)(curslider->maxval - curslider->minval);
+ default_percentage = (float)(curslider->defval - curslider->minval) / (float)(curslider->maxval - curslider->minval);
+
+ /* assemble the the text */
+ tempstring.ins(0, " ").ins(0, curslider->description);
+
+ /* move us to the bottom of the screen, and expand to full width */
+ y2 = 1.0f - UI_BOX_TB_BORDER;
+ y1 = y2 - bottom;
+ x1 = UI_BOX_LR_BORDER;
+ x2 = 1.0f - UI_BOX_LR_BORDER;
+
+ /* draw extra menu area */
+ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
+ y1 += UI_BOX_TB_BORDER;
+
+ /* determine the text height */
+ ui_draw_text_full(menu->container, tempstring, 0, 0, x2 - x1 - 2.0f * UI_BOX_LR_BORDER,
+ JUSTIFY_CENTER, WRAP_TRUNCATE, DRAW_NONE, ARGB_WHITE, ARGB_BLACK, NULL, &text_height);
+
+ /* draw the thermometer */
+ bar_left = x1 + UI_BOX_LR_BORDER;
+ bar_area_top = y1;
+ bar_width = x2 - x1 - 2.0f * UI_BOX_LR_BORDER;
+ bar_area_height = line_height;
+
+ /* compute positions */
+ bar_top = bar_area_top + 0.125f * bar_area_height;
+ bar_bottom = bar_area_top + 0.875f * bar_area_height;
+ default_x = bar_left + bar_width * default_percentage;
+ current_x = bar_left + bar_width * percentage;
+
+ /* fill in the percentage */
+ menu->container->add_rect(bar_left, bar_top, current_x, bar_bottom, UI_SLIDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* draw the top and bottom lines */
+ menu->container->add_line(bar_left, bar_top, bar_left + bar_width, bar_top, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ menu->container->add_line(bar_left, bar_bottom, bar_left + bar_width, bar_bottom, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* draw default marker */
+ menu->container->add_line(default_x, bar_area_top, default_x, bar_top, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ menu->container->add_line(default_x, bar_bottom, default_x, bar_area_top + bar_area_height, UI_LINE_WIDTH, UI_BORDER_COLOR, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+
+ /* draw the actual text */
+ ui_draw_text_full(menu->container, tempstring, x1 + UI_BOX_LR_BORDER, y1 + line_height, x2 - x1 - 2.0f * UI_BOX_LR_BORDER,
+ JUSTIFY_CENTER, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, &text_height);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_video_targets - handle the video targets
+ menu
+-------------------------------------------------*/
+
+static void menu_video_targets(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_video_targets_populate(machine, menu);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+ if (menu_event != NULL && menu_event->iptkey == IPT_UI_SELECT)
+ ui_menu_stack_push(ui_menu_alloc(machine, menu->container, menu_video_options, menu_event->itemref));
+}
+
+
+/*-------------------------------------------------
+ menu_video_targets_populate - populate the
+ video targets menu
+-------------------------------------------------*/
+
+static void menu_video_targets_populate(running_machine &machine, ui_menu *menu)
+{
+ int targetnum;
+
+ /* find the targets */
+ for (targetnum = 0; ; targetnum++)
+ {
+ render_target *target = machine.render().target_by_index(targetnum);
+ char buffer[40];
+
+ /* stop when we run out */
+ if (target == NULL)
+ break;
+
+ /* add a menu item */
+ sprintf(buffer, "Screen #%d", targetnum);
+ ui_menu_item_append(menu, buffer, NULL, 0, target);
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_video_options - handle the video options
+ menu
+-------------------------------------------------*/
+
+static void menu_video_options(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ render_target *target = (parameter != NULL) ? (render_target *)parameter : machine.render().first_target();
+ const ui_menu_event *menu_event;
+ int changed = FALSE;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_video_options_populate(machine, menu, target);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ switch ((FPTR)menu_event->itemref)
+ {
+ /* rotate adds rotation depending on the direction */
+ case VIDEO_ITEM_ROTATE:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ int delta = (menu_event->iptkey == IPT_UI_LEFT) ? ROT270 : ROT90;
+ target->set_orientation(orientation_add(delta, target->orientation()));
+ if (target->is_ui_target())
+ {
+ render_container::user_settings settings;
+ menu->container->get_user_settings(settings);
+ settings.m_orientation = orientation_add(delta ^ ROT180, settings.m_orientation);
+ menu->container->set_user_settings(settings);
+ }
+ changed = TRUE;
+ }
+ break;
+
+ /* layer config bitmasks handle left/right keys the same (toggle) */
+ case VIDEO_ITEM_BACKDROPS:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_backdrops_enabled(!target->backdrops_enabled());
+ changed = TRUE;
+ }
+ break;
+
+ case VIDEO_ITEM_OVERLAYS:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_overlays_enabled(!target->overlays_enabled());
+ changed = TRUE;
+ }
+ break;
+
+ case VIDEO_ITEM_BEZELS:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_bezels_enabled(!target->bezels_enabled());
+ changed = TRUE;
+ }
+ break;
+
+ case VIDEO_ITEM_CPANELS:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_cpanels_enabled(!target->cpanels_enabled());
+ changed = TRUE;
+ }
+ break;
+
+ case VIDEO_ITEM_MARQUEES:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_marquees_enabled(!target->marquees_enabled());
+ changed = TRUE;
+ }
+ break;
+
+ case VIDEO_ITEM_ZOOM:
+ if (menu_event->iptkey == IPT_UI_LEFT || menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ target->set_zoom_to_screen(!target->zoom_to_screen());
+ changed = TRUE;
+ }
+ break;
+
+ /* anything else is a view item */
+ default:
+ if (menu_event->iptkey == IPT_UI_SELECT && (int)(FPTR)menu_event->itemref >= VIDEO_ITEM_VIEW)
+ {
+ target->set_view((FPTR)menu_event->itemref - VIDEO_ITEM_VIEW);
+ changed = TRUE;
+ }
+ break;
+ }
+ }
+
+ /* if something changed, rebuild the menu */
+ if (changed)
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+}
+
+
+/*-------------------------------------------------
+ menu_video_options_populate - populate the
+ video options menu
+-------------------------------------------------*/
+
+static void menu_video_options_populate(running_machine &machine, ui_menu *menu, render_target *target)
+{
+ const char *subtext = "";
+ astring tempstring;
+ int viewnum;
+ int enabled;
+
+ /* add items for each view */
+ for (viewnum = 0; ; viewnum++)
+ {
+ const char *name = target->view_name(viewnum);
+ if (name == NULL)
+ break;
+
+ /* create a string for the item, replacing underscores with spaces */
+ tempstring.cpy(name).replace(0, "_", " ");
+ ui_menu_item_append(menu, tempstring, NULL, 0, (void *)(FPTR)(VIDEO_ITEM_VIEW + viewnum));
+ }
+
+ /* add a separator */
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL);
+
+ /* add a rotate item */
+ switch (target->orientation())
+ {
+ case ROT0: subtext = "None"; break;
+ case ROT90: subtext = "CW 90" UTF8_DEGREES; break;
+ case ROT180: subtext = "180" UTF8_DEGREES; break;
+ case ROT270: subtext = "CCW 90" UTF8_DEGREES; break;
+ }
+ ui_menu_item_append(menu, "Rotate", subtext, MENU_FLAG_LEFT_ARROW | MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_ROTATE);
+
+ /* backdrop item */
+ enabled = target->backdrops_enabled();
+ ui_menu_item_append(menu, "Backdrops", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_BACKDROPS);
+
+ /* overlay item */
+ enabled = target->overlays_enabled();
+ ui_menu_item_append(menu, "Overlays", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_OVERLAYS);
+
+ /* bezel item */
+ enabled = target->bezels_enabled();
+ ui_menu_item_append(menu, "Bezels", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_BEZELS);
+
+ /* cpanel item */
+ enabled = target->cpanels_enabled();
+ ui_menu_item_append(menu, "CPanels", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_CPANELS);
+
+ /* marquee item */
+ enabled = target->marquees_enabled();
+ ui_menu_item_append(menu, "Marquees", enabled ? "Enabled" : "Disabled", enabled ? MENU_FLAG_LEFT_ARROW : MENU_FLAG_RIGHT_ARROW, (void *)VIDEO_ITEM_MARQUEES);
+
+ /* cropping */
+ enabled = target->zoom_to_screen();
+ ui_menu_item_append(menu, "View", enabled ? "Cropped" : "Full", enabled ? MENU_FLAG_RIGHT_ARROW : MENU_FLAG_LEFT_ARROW, (void *)VIDEO_ITEM_ZOOM);
+}
+
+
+/*-------------------------------------------------
+ menu_crosshair - handle the crosshair settings
+ menu
+-------------------------------------------------*/
+
+static void menu_crosshair(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ const ui_menu_event *menu_event;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_crosshair_populate(machine, menu);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, UI_MENU_PROCESS_LR_REPEAT);
+
+ /* handle events */
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ crosshair_user_settings settings;
+ crosshair_item_data *data = (crosshair_item_data *)menu_event->itemref;
+ int changed = FALSE;
+ //int set_def = FALSE;
+ int newval = data->cur;
+
+ /* retreive the user settings */
+ crosshair_get_user_settings(machine, data->player, &settings);
+
+ switch (menu_event->iptkey)
+ {
+ /* if selected, reset to default value */
+ case IPT_UI_SELECT:
+ newval = data->defvalue;
+ //set_def = TRUE;
+ break;
+
+ /* left decrements */
+ case IPT_UI_LEFT:
+ newval -= machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
+
+ /* right increments */
+ case IPT_UI_RIGHT:
+ newval += machine.input().code_pressed(KEYCODE_LSHIFT) ? 10 : 1;
+ break;
+ }
+
+ /* clamp to range */
+ if (newval < data->min)
+ newval = data->min;
+ if (newval > data->max)
+ newval = data->max;
+
+ /* if things changed, update */
+ if (newval != data->cur)
+ {
+ switch (data->type)
+ {
+ /* visibility state */
+ case CROSSHAIR_ITEM_VIS:
+ settings.mode = newval;
+ changed = TRUE;
+ break;
+
+ /* auto time */
+ case CROSSHAIR_ITEM_AUTO_TIME:
+ settings.auto_time = newval;
+ changed = TRUE;
+ break;
+ }
+ }
+
+ /* crosshair graphic name */
+ if (data->type == CROSSHAIR_ITEM_PIC)
+ {
+ if (menu_event->iptkey == IPT_UI_SELECT)
+ {
+ /* clear the name string to reset to default crosshair */
+ settings.name[0] = 0;
+ changed = TRUE;
+ }
+ else if (menu_event->iptkey == IPT_UI_LEFT)
+ {
+ strcpy(settings.name, data->last_name);
+ changed = TRUE;
+ }
+ else if (menu_event->iptkey == IPT_UI_RIGHT)
+ {
+ strcpy(settings.name, data->next_name);
+ changed = TRUE;
+ }
+ }
+
+ if (changed)
+ {
+ /* save the user settings */
+ crosshair_set_user_settings(machine, data->player, &settings);
+
+ /* rebuild the menu */
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_POSITION);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ menu_crosshair_populate - populate the
+ crosshair settings menu
+-------------------------------------------------*/
+
+static void menu_crosshair_populate(running_machine &machine, ui_menu *menu)
+{
+ crosshair_user_settings settings;
+ crosshair_item_data *data;
+ char temp_text[16];
+ int player;
+ UINT8 use_auto = FALSE;
+ UINT32 flags = 0;
+
+ /* loop over player and add the manual items */
+ for (player = 0; player < MAX_PLAYERS; player++)
+ {
+ /* get the user settings */
+ crosshair_get_user_settings(machine, player, &settings);
+
+ /* add menu items for usable crosshairs */
+ if (settings.used)
+ {
+ /* Make sure to keep these matched to the CROSSHAIR_VISIBILITY_xxx types */
+ static const char *const vis_text[] = { "Off", "On", "Auto" };
+
+ /* track if we need the auto time menu */
+ if (settings.mode == CROSSHAIR_VISIBILITY_AUTO) use_auto = TRUE;
+
+ /* CROSSHAIR_ITEM_VIS - allocate a data item and fill it */
+ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data));
+ data->type = CROSSHAIR_ITEM_VIS;
+ data->player = player;
+ data->min = CROSSHAIR_VISIBILITY_OFF;
+ data->max = CROSSHAIR_VISIBILITY_AUTO;
+ data->defvalue = CROSSHAIR_VISIBILITY_DEFAULT;
+ data->cur = settings.mode;
+
+ /* put on arrows */
+ if (data->cur > data->min)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (data->cur < data->max)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+
+ /* add CROSSHAIR_ITEM_VIS menu */
+ sprintf(temp_text, "P%d Visibility", player + 1);
+ ui_menu_item_append(menu, temp_text, vis_text[settings.mode], flags, data);
+
+ /* CROSSHAIR_ITEM_PIC - allocate a data item and fill it */
+ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data));
+ data->type = CROSSHAIR_ITEM_PIC;
+ data->player = player;
+ data->last_name[0] = 0;
+ /* other data item not used by this menu */
+
+ /* search for crosshair graphics */
+
+ /* open a path to the crosshairs */
+ file_enumerator path(machine.options().crosshair_path());
+ const osd_directory_entry *dir;
+ /* reset search flags */
+ int using_default = FALSE;
+ int finished = FALSE;
+ int found = FALSE;
+
+ /* if we are using the default, then we just need to find the first in the list */
+ if (strlen(settings.name) == 0)
+ using_default = TRUE;
+
+ /* look for the current name, then remember the name before */
+ /* and find the next name */
+ while (((dir = path.next()) != NULL) && !finished)
+ {
+ int length = strlen(dir->name);
+
+ /* look for files ending in .png with a name not larger then 9 chars*/
+ if ((length > 4) && (length <= CROSSHAIR_PIC_NAME_LENGTH + 4) &&
+ dir->name[length - 4] == '.' &&
+ tolower((UINT8)dir->name[length - 3]) == 'p' &&
+ tolower((UINT8)dir->name[length - 2]) == 'n' &&
+ tolower((UINT8)dir->name[length - 1]) == 'g')
+
+ {
+ /* remove .png from length */
+ length -= 4;
+
+ if (found || using_default)
+ {
+ /* get the next name */
+ strncpy(data->next_name, dir->name, length);
+ data->next_name[length] = 0;
+ finished = TRUE;
+ }
+ else if (!strncmp(dir->name, settings.name, length))
+ {
+ /* we found the current name */
+ /* so loop once more to find the next name */
+ found = TRUE;
+ }
+ else
+ /* remember last name */
+ /* we will do it here in case files get added to the directory */
+ {
+ strncpy(data->last_name, dir->name, length);
+ data->last_name[length] = 0;
+ }
+ }
+ }
+ /* if name not found then next item is DEFAULT */
+ if (!found && !using_default)
+ {
+ data->next_name[0] = 0;
+ finished = TRUE;
+ }
+ /* setup the selection flags */
+ flags = 0;
+ if (finished)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+ if (found)
+ flags |= MENU_FLAG_LEFT_ARROW;
+
+ /* add CROSSHAIR_ITEM_PIC menu */
+ sprintf(temp_text, "P%d Crosshair", player + 1);
+ ui_menu_item_append(menu, temp_text, using_default ? "DEFAULT" : settings.name, flags, data);
+ }
+ }
+ if (use_auto)
+ {
+ /* any player can be used to get the autotime */
+ crosshair_get_user_settings(machine, 0, &settings);
+
+ /* CROSSHAIR_ITEM_AUTO_TIME - allocate a data item and fill it */
+ data = (crosshair_item_data *)ui_menu_pool_alloc(menu, sizeof(*data));
+ data->type = CROSSHAIR_ITEM_AUTO_TIME;
+ data->min = CROSSHAIR_VISIBILITY_AUTOTIME_MIN;
+ data->max = CROSSHAIR_VISIBILITY_AUTOTIME_MAX;
+ data->defvalue = CROSSHAIR_VISIBILITY_AUTOTIME_DEFAULT;
+ data->cur = settings.auto_time;
+
+ /* put on arrows in visible menu */
+ if (data->cur > data->min)
+ flags |= MENU_FLAG_LEFT_ARROW;
+ if (data->cur < data->max)
+ flags |= MENU_FLAG_RIGHT_ARROW;
+
+ /* add CROSSHAIR_ITEM_AUTO_TIME menu */
+ sprintf(temp_text, "%d", settings.auto_time);
+ ui_menu_item_append(menu, "Visible Delay", temp_text, flags, data);
+ }
+// else
+// /* leave a blank filler line when not in auto time so size does not rescale */
+// ui_menu_item_append(menu, "", "", NULL, NULL);
+}
+
+
+/*-------------------------------------------------
+ menu_quit_game - handle the "menu" for
+ quitting the game
+-------------------------------------------------*/
+
+static void menu_quit_game(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ /* request a reset */
+ machine.schedule_exit();
+
+ /* reset the menu stack */
+ ui_menu_stack_reset(machine);
+}
+
+
+/*-------------------------------------------------
+ menu_select_game - handle the game select
+ menu
+-------------------------------------------------*/
+
+static void menu_select_game(running_machine &machine, ui_menu *menu, void *parameter, void *state)
+{
+ select_game_state *menustate;
+ const ui_menu_event *menu_event;
+
+ /* if no state, allocate some */
+ if (state == NULL)
+ {
+ state = ui_menu_alloc_state(menu, sizeof(*menustate) + sizeof(menustate->driverlist) * driver_list::total(), NULL);
+ if (parameter != NULL)
+ strcpy(((select_game_state *)state)->search, (const char *)parameter);
+ ((select_game_state *)state)->matchlist[0] = -1;
+ }
+ menustate = (select_game_state *)state;
+
+ /* if the menu isn't built, populate now */
+ if (!ui_menu_populated(menu))
+ menu_select_game_populate(machine, menu, menustate);
+
+ /* ignore pause keys by swallowing them before we process the menu */
+ ui_input_pressed(machine, IPT_UI_PAUSE);
+
+ /* process the menu */
+ menu_event = ui_menu_process(machine, menu, 0);
+ if (menu_event != NULL && menu_event->itemref != NULL)
+ {
+ /* reset the error on any future menu_event */
+ if (menustate->error)
+ menustate->error = FALSE;
+
+ /* handle selections */
+ else if (menu_event->iptkey == IPT_UI_SELECT)
+ {
+ const game_driver *driver = (const game_driver *)menu_event->itemref;
+
+ /* special case for configure inputs */
+ if ((FPTR)driver == 1)
+ ui_menu_stack_push(ui_menu_alloc(menu->machine(), menu->container, menu_input_groups, NULL));
+
+ /* anything else is a driver */
+ else
+ {
+ // audit the game first to see if we're going to work
+ driver_enumerator enumerator(machine.options(), *driver);
+ enumerator.next();
+ media_auditor auditor(enumerator);
+ media_auditor::summary summary = auditor.audit_media(AUDIT_VALIDATE_FAST);
+
+ // if everything looks good, schedule the new driver
+ if (summary == media_auditor::CORRECT || summary == media_auditor::BEST_AVAILABLE)
+ {
+ machine.schedule_new_driver(*driver);
+ ui_menu_stack_reset(machine);
+ }
+
+ // otherwise, display an error
+ else
+ {
+ ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF);
+ menustate->error = TRUE;
+ }
+ }
+ }
+
+ /* escape pressed with non-empty text clears the text */
+ else if (menu_event->iptkey == IPT_UI_CANCEL && menustate->search[0] != 0)
+ {
+ /* since we have already been popped, we must recreate ourself from scratch */
+ ui_menu_stack_push(ui_menu_alloc(menu->machine(), menu->container, menu_select_game, NULL));
+ }
+
+ /* typed characters append to the buffer */
+ else if (menu_event->iptkey == IPT_SPECIAL)
+ {
+ int buflen = strlen(menustate->search);
+
+ /* if it's a backspace and we can handle it, do so */
+ if ((menu_event->unichar == 8 || menu_event->unichar == 0x7f) && buflen > 0)
+ {
+ *(char *)utf8_previous_char(&menustate->search[buflen]) = 0;
+ menustate->rerandomize = TRUE;
+ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST);
+ }
+
+ /* if it's any other key and we're not maxed out, update */
+ else if (menu_event->unichar >= ' ' && menu_event->unichar < 0x7f)
+ {
+ buflen += utf8_from_uchar(&menustate->search[buflen], ARRAY_LENGTH(menustate->search) - buflen, menu_event->unichar);
+ menustate->search[buflen] = 0;
+ ui_menu_reset(menu, UI_MENU_RESET_SELECT_FIRST);
+ }
+ }
+ }
+
+ /* if we're in an error state, overlay an error message */
+ if (menustate->error)
+ ui_draw_text_box(menu->container,
+ "The selected game is missing one or more required ROM or CHD images. "
+ "Please select a different game.\n\nPress any key to continue.",
+ JUSTIFY_CENTER, 0.5f, 0.5f, UI_RED_COLOR);
+}
+
+
+/*-------------------------------------------------
+ menu_select_game_populate - populate the game
+ select menu
+-------------------------------------------------*/
+
+static void menu_select_game_populate(running_machine &machine, ui_menu *menu, select_game_state *menustate)
+{
+ int matchcount;
+ int curitem;
+
+ /* update our driver list if necessary */
+ if (menustate->driverlist[0] == NULL)
+ menu_select_game_build_driver_list(menu, menustate);
+ for (curitem = matchcount = 0; menustate->driverlist[curitem] != NULL && matchcount < VISIBLE_GAMES_IN_LIST; curitem++)
+ if (!(menustate->driverlist[curitem]->flags & GAME_NO_STANDALONE))
+ matchcount++;
+
+ /* if nothing there, add a single multiline item and return */
+ if (matchcount == 0)
+ {
+ ui_menu_item_append(menu, "No "GAMESNOUN" found. Please check the rompath specified in the "CONFIGNAME".ini file.\n\n"
+ "If this is your first time using "APPNAME", please see the config.txt file in "
+ "the docs directory for information on configuring "APPNAME".", NULL, MENU_FLAG_MULTILINE | MENU_FLAG_REDTEXT, NULL);
+ return;
+ }
+
+ /* otherwise, rebuild the match list */
+ assert(drivlist != NULL);
+ if (menustate->search[0] != 0 || menustate->matchlist[0] == -1 || menustate->rerandomize)
+ drivlist->find_approximate_matches(menustate->search, matchcount, menustate->matchlist);
+ menustate->rerandomize = FALSE;
+
+ /* iterate over entries */
+ for (curitem = 0; curitem < matchcount; curitem++)
+ {
+ int curmatch = menustate->matchlist[curitem];
+ if (curmatch != -1)
+ {
+ int cloneof = drivlist->non_bios_clone(curmatch);
+ ui_menu_item_append(menu, drivlist->driver(curmatch).name, drivlist->driver(curmatch).description, (cloneof == -1) ? 0 : MENU_FLAG_INVERT, (void *)&drivlist->driver(curmatch));
+ }
+ }
+
+ /* if we're forced into this, allow general input configuration as well */
+ if (ui_menu_is_force_game_select())
+ {
+ ui_menu_item_append(menu, MENU_SEPARATOR_ITEM, NULL, 0, NULL);
+ ui_menu_item_append(menu, "Configure General Inputs", NULL, 0, (void *)1);
+ }
+
+ /* configure the custom rendering */
+ ui_menu_set_custom_render(menu, menu_select_game_custom_render, ui_get_line_height(machine) + 3.0f * UI_BOX_TB_BORDER, 4.0f * ui_get_line_height(machine) + 3.0f * UI_BOX_TB_BORDER);
+}
+
+
+/*-------------------------------------------------
+ menu_select_game_build_driver_list - build a
+ list of available drivers
+-------------------------------------------------*/
+
+static void menu_select_game_build_driver_list(ui_menu *menu, select_game_state *menustate)
+{
+ // start with an empty list
+ // hack alert: use new directly here to avoid reporting this one-time static memory as unfreed
+ if (drivlist == NULL)
+ drivlist = global_alloc(driver_enumerator(menu->machine().options()));
+ drivlist->exclude_all();
+
+ /* open a path to the ROMs and find them in the array */
+ file_enumerator path(menu->machine().options().media_path());
+ const osd_directory_entry *dir;
+
+ /* iterate while we get new objects */
+ while ((dir = path.next()) != NULL)
+ {
+ char drivername[50];
+ char *dst = drivername;
+ const char *src;
+
+ /* build a name for it */
+ for (src = dir->name; *src != 0 && *src != '.' && dst < &drivername[ARRAY_LENGTH(drivername) - 1]; src++)
+ *dst++ = tolower((UINT8)*src);
+ *dst = 0;
+
+ int drivnum = drivlist->find(drivername);
+ if (drivnum != -1)
+ drivlist->include(drivnum);
+ }
+
+ /* now build the final list */
+ drivlist->reset();
+ int listnum = 0;
+ while (drivlist->next())
+ menustate->driverlist[listnum++] = &drivlist->driver();
+
+ /* NULL-terminate */
+ menustate->driverlist[listnum] = NULL;
+}
+
+
+/*-------------------------------------------------
+ menu_select_game_custom_render - perform our
+ special rendering
+-------------------------------------------------*/
+
+static void menu_select_game_custom_render(running_machine &machine, ui_menu *menu, void *state, void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
+{
+ select_game_state *menustate = (select_game_state *)state;
+ const game_driver *driver;
+ float width, maxwidth;
+ float x1, y1, x2, y2;
+ char tempbuf[4][256];
+ rgb_t color;
+ int line;
+
+ /* display the current typeahead */
+ if (menustate->search[0] != 0)
+ sprintf(&tempbuf[0][0], "Type name or select: %s_", menustate->search);
+ else
+ sprintf(&tempbuf[0][0], "Type name or select: (random)");
+
+ /* get the size of the text */
+ ui_draw_text_full(menu->container, &tempbuf[0][0], 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE,
+ DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, NULL);
+ width += 2 * UI_BOX_LR_BORDER;
+ maxwidth = MAX(width, origx2 - origx1);
+
+ /* compute our bounds */
+ x1 = 0.5f - 0.5f * maxwidth;
+ x2 = x1 + maxwidth;
+ y1 = origy1 - top;
+ y2 = origy1 - UI_BOX_TB_BORDER;
+
+ /* draw a box */
+ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, UI_BACKGROUND_COLOR);
+
+ /* take off the borders */
+ x1 += UI_BOX_LR_BORDER;
+ x2 -= UI_BOX_LR_BORDER;
+ y1 += UI_BOX_TB_BORDER;
+ y2 -= UI_BOX_TB_BORDER;
+
+ /* draw the text within it */
+ ui_draw_text_full(menu->container, &tempbuf[0][0], x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE,
+ DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL);
+
+ /* determine the text to render below */
+ driver = ((FPTR)selectedref > 1) ? (const game_driver *)selectedref : NULL;
+ if ((FPTR)driver > 1)
+ {
+ const char *gfxstat, *soundstat;
+
+ /* first line is game name */
+ sprintf(&tempbuf[0][0], "%-.100s", driver->description);
+
+ /* next line is year, manufacturer */
+ sprintf(&tempbuf[1][0], "%s, %-.100s", driver->year, driver->manufacturer);
+
+ /* next line is overall driver status */
+ if (driver->flags & GAME_NOT_WORKING)
+ strcpy(&tempbuf[2][0], "Overall: NOT WORKING");
+ else if (driver->flags & GAME_UNEMULATED_PROTECTION)
+ strcpy(&tempbuf[2][0], "Overall: Unemulated Protection");
+ else
+ strcpy(&tempbuf[2][0], "Overall: Working");
+
+ /* next line is graphics, sound status */
+ if (driver->flags & (GAME_IMPERFECT_GRAPHICS | GAME_WRONG_COLORS | GAME_IMPERFECT_COLORS))
+ gfxstat = "Imperfect";
+ else
+ gfxstat = "OK";
+
+ if (driver->flags & GAME_NO_SOUND)
+ soundstat = "Unimplemented";
+ else if (driver->flags & GAME_IMPERFECT_SOUND)
+ soundstat = "Imperfect";
+ else
+ soundstat = "OK";
+
+ sprintf(&tempbuf[3][0], "Gfx: %s, Sound: %s", gfxstat, soundstat);
+ }
+ else
+ {
+ const char *s = COPYRIGHT;
+ line = 0;
+ int col = 0;
+
+ /* first line is version string */
+ sprintf(&tempbuf[line++][0], "%s %s", APPLONGNAME, build_version);
+
+ /* output message */
+ while (line < ARRAY_LENGTH(tempbuf))
+ {
+ if (*s == 0 || *s == '\n')
+ {
+ tempbuf[line++][col] = 0;
+ col = 0;
+ }
+ else
+ tempbuf[line][col++] = *s;
+
+ if (*s != 0)
+ s++;
+ }
+ }
+
+ /* get the size of the text */
+ maxwidth = origx2 - origx1;
+ for (line = 0; line < 4; line++)
+ {
+ ui_draw_text_full(menu->container, &tempbuf[line][0], 0.0f, 0.0f, 1.0f, JUSTIFY_CENTER, WRAP_TRUNCATE,
+ DRAW_NONE, ARGB_WHITE, ARGB_BLACK, &width, NULL);
+ width += 2 * UI_BOX_LR_BORDER;
+ maxwidth = MAX(maxwidth, width);
+ }
+
+ /* compute our bounds */
+ x1 = 0.5f - 0.5f * maxwidth;
+ x2 = x1 + maxwidth;
+ y1 = origy2 + UI_BOX_TB_BORDER;
+ y2 = origy2 + bottom;
+
+ /* draw a box */
+ color = UI_BACKGROUND_COLOR;
+ if (driver != NULL)
+ color = UI_GREEN_COLOR;
+ if (driver != NULL && (driver->flags & (GAME_IMPERFECT_GRAPHICS | GAME_WRONG_COLORS | GAME_IMPERFECT_COLORS | GAME_NO_SOUND | GAME_IMPERFECT_SOUND)) != 0)
+ color = UI_YELLOW_COLOR;
+ if (driver != NULL && (driver->flags & (GAME_NOT_WORKING | GAME_UNEMULATED_PROTECTION)) != 0)
+ color = UI_RED_COLOR;
+ ui_draw_outlined_box(menu->container, x1, y1, x2, y2, color);
+
+ /* take off the borders */
+ x1 += UI_BOX_LR_BORDER;
+ x2 -= UI_BOX_LR_BORDER;
+ y1 += UI_BOX_TB_BORDER;
+ y2 -= UI_BOX_TB_BORDER;
+
+ /* draw all lines */
+ for (line = 0; line < 4; line++)
+ {
+ ui_draw_text_full(menu->container, &tempbuf[line][0], x1, y1, x2 - x1, JUSTIFY_CENTER, WRAP_TRUNCATE,
+ DRAW_NORMAL, UI_TEXT_COLOR, UI_TEXT_BG_COLOR, NULL, NULL);
+ y1 += ui_get_line_height(machine);
+ }
+}
+
+
+
+/***************************************************************************
+ MENU HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ menu_render_triangle - render a triangle that
+ is used for up/down arrows and left/right
+ indicators
+-------------------------------------------------*/
+
+static void menu_render_triangle(bitmap_t &dest, const bitmap_t &source, const rectangle &sbounds, void *param)
+{
+ int halfwidth = dest.width / 2;
+ int height = dest.height;
+ int x, y;
+
+ /* start with all-transparent */
+ bitmap_fill(&dest, NULL, MAKE_ARGB(0x00,0x00,0x00,0x00));
+
+ /* render from the tip to the bottom */
+ for (y = 0; y < height; y++)
+ {
+ int linewidth = (y * (halfwidth - 1) + (height / 2)) * 255 * 2 / height;
+ UINT32 *target = BITMAP_ADDR32(&dest, y, halfwidth);
+
+ /* don't antialias if height < 12 */
+ if (dest.height < 12)
+ {
+ int pixels = (linewidth + 254) / 255;
+ if (pixels % 2 == 0) pixels++;
+ linewidth = pixels * 255;
+ }
+
+ /* loop while we still have data to generate */
+ for (x = 0; linewidth > 0; x++)
+ {
+ int dalpha;
+
+ /* first column we only consume one pixel */
+ if (x == 0)
+ {
+ dalpha = MIN(0xff, linewidth);
+ target[x] = MAKE_ARGB(dalpha,0xff,0xff,0xff);
+ }
+
+ /* remaining columns consume two pixels, one on each side */
+ else
+ {
+ dalpha = MIN(0x1fe, linewidth);
+ target[x] = target[-x] = MAKE_ARGB(dalpha/2,0xff,0xff,0xff);
+ }
+
+ /* account for the weight we consumed */
+ linewidth -= dalpha;
+ }
+ }
+}