diff options
Diffstat (limited to 'trunk/src/emu/uiswlist.c')
-rw-r--r-- | trunk/src/emu/uiswlist.c | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/trunk/src/emu/uiswlist.c b/trunk/src/emu/uiswlist.c new file mode 100644 index 00000000000..9fffb999ded --- /dev/null +++ b/trunk/src/emu/uiswlist.c @@ -0,0 +1,491 @@ +/********************************************************************* + + uiswlist.c + + Internal MAME user interface for software list. + + Copyright Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + + *********************************************************************/ + +#include "emu.h" +#include "ui.h" +#include "uimenu.h" +#include "softlist.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* time (in seconds) to display errors */ +#define ERROR_MESSAGE_TIME 5 + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* state of a software entry */ +typedef struct _software_entry_state software_entry_state; +struct _software_entry_state +{ + software_entry_state *next; + + const char *short_name; + const char *long_name; + const char *interface; + char *list_name; + device_image_interface* image; +}; + +/* state of the software menu */ +typedef struct _software_menu_state software_menu_state; +struct _software_menu_state +{ + char *list_name; /* currently selected list */ + device_image_interface* image; + software_entry_state *entrylist; + char filename_buffer[1024]; + int ordered_by_shortname; + int reorder; +}; + +/* state of a software part */ +typedef struct _software_part_state software_part_state; +struct _software_part_state +{ + const char *part_name; + const char *interface; +}; + + +static void ui_mess_menu_populate_software_parts(running_machine &machine, ui_menu *menu, const char *swlist, const char *swinfo, const char *interface) +{ + software_list *list = software_list_open(machine.options(), swlist, FALSE, NULL); + + if (list) + { + software_info *info = software_list_find(list, swinfo, NULL); + + if (info) + { + for (software_part *swpart = software_find_part(info, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) + { + if (strcmp(interface, swpart->interface_) == 0) + { + software_part_state *entry = (software_part_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); + // check if the available parts have specific part_id to be displayed (e.g. "Map Disc", "Bonus Disc", etc.) + // if not, we simply display "part_name"; if yes we display "part_name (part_id)" + astring menu_part_name(swpart->name); + if (software_part_get_feature(swpart, "part_id") != NULL) + { + menu_part_name.cat(" ("); + menu_part_name.cat(software_part_get_feature(swpart, "part_id")); + menu_part_name.cat(")"); + } + entry->part_name = ui_menu_pool_strdup(menu, swpart->name); // part_name is later used to build up the filename to load, so we use swpart->name! + entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); + ui_menu_item_append(menu, info->shortname, menu_part_name.cstr(), 0, entry); + } + } + } + + software_list_close(list); + } +} + +void ui_mess_menu_software_parts(running_machine &machine, ui_menu *menu, void *parameter, void *state) +{ + const ui_menu_event *event; + software_entry_state *sw_state = (software_entry_state *)state; + const char *swlist = sw_state->list_name; + const char *swinfo = sw_state->short_name; + const char *interface = sw_state->interface; + + // generate list of available parts + if (!ui_menu_populated(menu)) + { + if (sw_state->list_name) + { + ui_mess_menu_populate_software_parts(machine, menu, swlist, swinfo, interface); + } + } + + /* process the menu */ + event = ui_menu_process(machine, menu, 0); + + if (event != NULL && event->iptkey == IPT_UI_SELECT && event->itemref != NULL) + { + software_part_state *entry = (software_part_state *) event->itemref; + + // build the name for the part to be loaded + astring temp_name(sw_state->short_name); + temp_name.cat(":"); + temp_name.cat(entry->part_name); + //printf("%s\n", temp_name.cstr()); + + sw_state->image->load(temp_name.cstr()); + } +} + +static int compare_software_entries(const software_entry_state *e1, const software_entry_state *e2, int shortname) +{ + int result; + const char *e1_basename; + const char *e2_basename; + + if (shortname) + { + e1_basename = (e1->short_name != NULL) ? e1->short_name : ""; + e2_basename = (e2->short_name != NULL) ? e2->short_name : ""; + } + else + { + e1_basename = (e1->long_name != NULL) ? e1->long_name : ""; + e2_basename = (e2->long_name != NULL) ? e2->long_name : ""; + } + + result = mame_stricmp(e1_basename, e2_basename); + if (result == 0) + { + result = strcmp(e1_basename, e2_basename); + if (result == 0) + { + if (e1 < e2) + result = -1; + else if (e1 > e2) + result = 1; + } + } + + return result; +} + +/* populate a specific list */ + +static software_entry_state *append_software_entry(ui_menu *menu, software_menu_state *menustate, + software_info *swinfo, char *list_name, device_image_interface* image) +{ + software_entry_state *entry = NULL; + software_entry_state **entryptr; + const char *interface = image->image_interface(); + + // check if at least one of the parts has the correct interface and add a menu entry only in this case + for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) + { + if (strcmp(interface, swpart->interface_) == 0) + { + // allocate a new entry + entry = (software_entry_state *) ui_menu_pool_alloc(menu, sizeof(*entry)); + memset(entry, 0, sizeof(*entry)); + + entry->short_name = ui_menu_pool_strdup(menu, swinfo->shortname); + entry->long_name = ui_menu_pool_strdup(menu, swinfo->longname); + entry->list_name = list_name; + entry->image = image; + entry->interface = ui_menu_pool_strdup(menu, swpart->interface_); + break; + } + } + + // find the end of the list + entryptr = &menustate->entrylist; + while ((*entryptr != NULL) && (compare_software_entries(entry, *entryptr, menustate->ordered_by_shortname) >= 0)) + entryptr = &(*entryptr)->next; + + // insert the entry + entry->next = *entryptr; + *entryptr = entry; + + return entry; +} + + +static void ui_mess_menu_populate_software_entries(running_machine &machine, ui_menu *menu, software_menu_state *menustate) +{ + software_list *list = software_list_open(machine.options(), menustate->list_name, FALSE, NULL); + + // build up the list of entries for the menu + if (list) + { + for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) + append_software_entry(menu, menustate, swinfo, menustate->list_name, menustate->image); + + software_list_close(list); + } + + // add an entry to change ordering + ui_menu_item_append(menu, "Switch Item Ordering", NULL, 0, (void *)1); + + // append all of the menu entries + for (software_entry_state *entry = menustate->entrylist; entry != NULL; entry = entry->next) + ui_menu_item_append(menu, entry->short_name, entry->long_name, 0, entry); +} + +bool swinfo_has_multiple_parts(software_info *swinfo, const char *interface) +{ + int count = 0; + + for (software_part *swpart = software_find_part(swinfo, NULL, NULL); swpart != NULL; swpart = software_part_next(swpart)) + { + if (strcmp(interface, swpart->interface_) == 0) + count++; + } + return (count > 1) ? TRUE : FALSE; +} + +void ui_mess_menu_software_list(running_machine &machine, ui_menu *menu, void *parameter, void *state) +{ + const ui_menu_event *event; + software_menu_state *sw_state = (software_menu_state *)state; + const software_entry_state *entry; + const software_entry_state *selected_entry = NULL; + int bestmatch = 0; + + if (!ui_menu_populated(menu) || sw_state->reorder) + { + sw_state->reorder = 0; + + if (sw_state->list_name) + { + ui_mess_menu_populate_software_entries(machine, menu, sw_state); + } + } + + /* process the menu */ + event = ui_menu_process(machine, menu, 0); + + if (event != NULL && event->itemref != NULL) + { + if ((FPTR)event->itemref == 1 && event->iptkey == IPT_UI_SELECT) + { + sw_state->ordered_by_shortname ^= 1; + sw_state->reorder = 1; + sw_state->entrylist = NULL; + // reset the char buffer if we change ordering criterion + memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); + + // reload the menu with the new order + ui_menu_reset(menu, UI_MENU_RESET_REMEMBER_REF); + popmessage("Switched Order: entries now ordered by %s", sw_state->ordered_by_shortname ? "shortname" : "description"); + } + /* handle selections */ + else if (event->iptkey == IPT_UI_SELECT) + { + device_image_interface *image = sw_state->image; + software_entry_state *entry = (software_entry_state *) event->itemref; + software_list *tmp_list = software_list_open(machine.options(), sw_state->list_name, FALSE, NULL); + software_info *tmp_info = software_list_find(tmp_list, entry->short_name, NULL); + + // if the selected software has multiple parts that can be loaded, open the submenu + if (swinfo_has_multiple_parts(tmp_info, image->image_interface())) + { + ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_parts, entry); + software_entry_state *child_menustate = (software_entry_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); + child_menustate->short_name = entry->short_name; + child_menustate->interface = image->image_interface(); + child_menustate->list_name = sw_state->list_name; + child_menustate->image = image; + ui_menu_stack_push(child_menu); + } + else + { + // otherwise, load the file + image->load(entry->short_name); + } + software_list_close(tmp_list); + + // reset the char buffer when pressing IPT_UI_SELECT + if (sw_state->filename_buffer[0] != '\0') + memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); + } + else if (event->iptkey == IPT_SPECIAL) + { + int buflen = strlen(sw_state->filename_buffer); + bool update_selected = FALSE; + + /* if it's a backspace and we can handle it, do so */ + if ((event->unichar == 8 || event->unichar == 0x7f) && buflen > 0) + { + *(char *)utf8_previous_char(&sw_state->filename_buffer[buflen]) = 0; + update_selected = TRUE; + + if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) + ui_popup_time(ERROR_MESSAGE_TIME, "%s", sw_state->filename_buffer); + } + /* if it's any other key and we're not maxed out, update */ + else if (event->unichar >= ' ' && event->unichar < 0x7f) + { + buflen += utf8_from_uchar(&sw_state->filename_buffer[buflen], ARRAY_LENGTH(sw_state->filename_buffer) - buflen, event->unichar); + sw_state->filename_buffer[buflen] = 0; + update_selected = TRUE; + + if (ARRAY_LENGTH(sw_state->filename_buffer) > 0) + ui_popup_time(ERROR_MESSAGE_TIME, "%s", sw_state->filename_buffer); + } + + if (update_selected) + { + const software_entry_state *cur_selected; + + // if the current selection is a software entry, start search from here + if ((FPTR)event->itemref != 1) + cur_selected= (const software_entry_state *)ui_menu_get_selection(menu); + // else (if we are on the 'Switch Order' entry) start from the beginning + else + cur_selected= sw_state->entrylist; + + // check for entries which matches our filename_buffer: + // from current entry to the end + for (entry = cur_selected; entry != NULL; entry = entry->next) + { + const char *compare_name = sw_state->ordered_by_shortname ? entry->short_name : entry->long_name; + + if (compare_name != NULL && sw_state->filename_buffer != NULL) + { + int match = 0; + for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) + { + if (mame_strnicmp(compare_name, sw_state->filename_buffer, i) == 0) + match = i; + } + + if (match > bestmatch) + { + bestmatch = match; + selected_entry = entry; + } + } + } + // and from the first entry to current one + for (entry = sw_state->entrylist; entry != cur_selected; entry = entry->next) + { + const char *compare_name = sw_state->ordered_by_shortname ? entry->short_name : entry->long_name; + + if (compare_name != NULL && sw_state->filename_buffer != NULL) + { + int match = 0; + for (int i = 0; i < ARRAY_LENGTH(sw_state->filename_buffer); i++) + { + if (mame_strnicmp(compare_name, sw_state->filename_buffer, i) == 0) + match = i; + } + + if (match > bestmatch) + { + bestmatch = match; + selected_entry = entry; + } + } + } + + if (selected_entry != NULL && selected_entry != cur_selected) + ui_menu_set_selection(menu, (void *) selected_entry); + } + } + else if (event->iptkey == IPT_UI_CANCEL) + { + // reset the char buffer also in this case + if (sw_state->filename_buffer[0] != '\0') + memset(sw_state->filename_buffer, '\0', ARRAY_LENGTH(sw_state->filename_buffer)); + } + } +} + +/* list of available software lists - i.e. cartridges, floppies */ +static void ui_mess_menu_populate_software_list(running_machine &machine, ui_menu *menu, device_image_interface* image) +{ + bool haveCompatible = FALSE; + const char *interface = image->image_interface(); + + // Add original software lists for this system + for (const device_t *dev = machine.config().devicelist().first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) + { + software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config(); + + for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) + { + if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_ORIGINAL_SYSTEM)) + { + software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); + + if (list) + { + bool found = FALSE; + for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) + { + software_part *part = software_find_part(swinfo, NULL, NULL); + if (strcmp(interface,part->interface_)==0) { + found = TRUE; + } + } + if (found) { + ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); + } + + software_list_close(list); + } + } + } + } + + // Add compatible software lists for this system + for (const device_t *dev = machine.config().devicelist().first(SOFTWARE_LIST); dev != NULL; dev = dev->typenext()) + { + software_list_config *swlist = (software_list_config *)downcast<const legacy_device_base *>(dev)->inline_config(); + + for (int i = 0; i < DEVINFO_STR_SWLIST_MAX - DEVINFO_STR_SWLIST_0; i++) + { + if (swlist->list_name[i] && (swlist->list_type == SOFTWARE_LIST_COMPATIBLE_SYSTEM)) + { + software_list *list = software_list_open(machine.options(), swlist->list_name[i], FALSE, NULL); + + if (list) + { + bool found = FALSE; + for (software_info *swinfo = software_list_find(list, "*", NULL); swinfo != NULL; swinfo = software_list_find(list, "*", swinfo)) + { + software_part *part = software_find_part(swinfo, NULL, NULL); + if (strcmp(interface,part->interface_)==0) { + found = TRUE; + } + } + if (found) { + if (!haveCompatible) { + ui_menu_item_append(menu, "[compatible lists]", NULL, MENU_FLAG_DISABLE, NULL); + } + ui_menu_item_append(menu, list->description, NULL, 0, swlist->list_name[i]); + } + + haveCompatible = TRUE; + software_list_close(list); + } + } + } + } + +} + +void ui_image_menu_software(running_machine &machine, ui_menu *menu, void *parameter, void *state) +{ + const ui_menu_event *event; + device_image_interface* image = (device_image_interface*)parameter; + if (!ui_menu_populated(menu)) + ui_mess_menu_populate_software_list(machine, menu, image); + + /* process the menu */ + event = ui_menu_process(machine, menu, 0); + + if (event != NULL && event->iptkey == IPT_UI_SELECT) + { + ui_menu *child_menu = ui_menu_alloc(machine, &machine.render().ui_container(), ui_mess_menu_software_list, NULL); + software_menu_state *child_menustate = (software_menu_state *)ui_menu_alloc_state(child_menu, sizeof(*child_menustate), NULL); + child_menustate->list_name = (char *)event->itemref; + child_menustate->image = image; + child_menustate->entrylist = NULL; + child_menustate->ordered_by_shortname = 1; + ui_menu_stack_push(child_menu); + } +} |