diff options
Diffstat (limited to 'src/osd/winui/datamap.cpp')
-rw-r--r-- | src/osd/winui/datamap.cpp | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/src/osd/winui/datamap.cpp b/src/osd/winui/datamap.cpp new file mode 100644 index 00000000000..28d3cc0b854 --- /dev/null +++ b/src/osd/winui/datamap.cpp @@ -0,0 +1,765 @@ +// For licensing and usage information, read docs/winui_license.txt +// MASTER +//**************************************************************************** + +//============================================================ +// +// datamap.c - Win32 dialog and options bridge code +// +//============================================================ + +// standard windows headers +#include <windows.h> +#include <windowsx.h> +#include <commctrl.h> +#include <tchar.h> + +// standard C headers + +// MAME/MAMEUI headers +#include "mui_opts.h" +#include "datamap.h" +#include "winutf8.h" + + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +enum _control_type +{ + CT_UNKNOWN, + CT_BUTTON, + CT_STATIC, + CT_EDIT, + CT_COMBOBOX, + CT_TRACKBAR, + CT_LISTVIEW +}; + +typedef enum _control_type control_type; + + +typedef struct _datamap_entry datamap_entry; +struct _datamap_entry +{ + // the basics about the entry + int dlgitem; + datamap_entry_type type; + const char *option_name; + + // callbacks + datamap_callback callbacks[DCT_COUNT]; + get_option_name_callback get_option_name; + + // formats + const char *int_format; + const char *float_format; + + // trackbar options + BOOL use_trackbar_options; + float trackbar_min; + float trackbar_max; + float trackbar_increments; +}; + + +struct _datamap +{ + int entry_count; + datamap_entry entries[256]; // 256 options entries seems enough for now... +}; + +typedef void (*datamap_default_callback)(datamap *map, HWND control, windows_options *opts, datamap_entry *entry, const char *option_name); + + + +//============================================================ +// PROTOTYPES +//============================================================ + +static datamap_entry *find_entry(datamap *map, int dlgitem); +static control_type get_control_type(HWND control); +static int control_operation(datamap *map, HWND dialog, windows_options *opts, datamap_entry *entry, datamap_callback_type callback_type); +static void read_control(datamap *map, HWND control, windows_options *opts, datamap_entry *entry, const char *option_name); +static void populate_control(datamap *map, HWND control, windows_options *opts, datamap_entry *entry, const char *option_name); +static char *tztrim(float float_value); + + +//============================================================ +// datamap_create +//============================================================ + +datamap *datamap_create(void) +{ + datamap *map = (datamap *)malloc(sizeof(*map)); + if (!map) + return NULL; + + map->entry_count = 0; + return map; +} + + + +//============================================================ +// datamap_free +//============================================================ + +void datamap_free(datamap *map) +{ + free(map); +} + + + +//============================================================ +// datamap_add +//============================================================ + +void datamap_add(datamap *map, int dlgitem, datamap_entry_type type, const char *option_name) +{ + // sanity check for too many entries + if (!(map->entry_count < ARRAY_LENGTH(map->entries))) + { + printf("Datamap.cpp Line __LINE__ too many entries\n"); + return; + } + + // add entry to the datamap + memset(&map->entries[map->entry_count], 0, sizeof(map->entries[map->entry_count])); + map->entries[map->entry_count].dlgitem = dlgitem; + map->entries[map->entry_count].type = type; + map->entries[map->entry_count].option_name = option_name; + map->entry_count++; +} + + + +//============================================================ +// datamap_set_callback +//============================================================ + +void datamap_set_callback(datamap *map, int dlgitem, datamap_callback_type callback_type, datamap_callback callback) +{ + datamap_entry *entry; + + assert(callback_type >= 0); + assert(callback_type < DCT_COUNT); + + entry = find_entry(map, dlgitem); + entry->callbacks[callback_type] = callback; +} + + + +//============================================================ +// datamap_set_option_name_callback +//============================================================ + +void datamap_set_option_name_callback(datamap *map, int dlgitem, get_option_name_callback get_option_name) +{ + datamap_entry *entry; + entry = find_entry(map, dlgitem); + entry->get_option_name = get_option_name; +} + + + +//============================================================ +// datamap_set_trackbar_range +//============================================================ + +void datamap_set_trackbar_range(datamap *map, int dlgitem, float min, float max, float increments) +{ + datamap_entry *entry = find_entry(map, dlgitem); + entry->use_trackbar_options = TRUE; + entry->trackbar_min = min; + entry->trackbar_max = max; + entry->trackbar_increments = increments; +} + + + +//============================================================ +// datamap_set_int_format +//============================================================ + +void datamap_set_int_format(datamap *map, int dlgitem, const char *format) +{ + datamap_entry *entry = find_entry(map, dlgitem); + entry->int_format = format; +} + + + +//============================================================ +// datamap_set_float_format +//============================================================ + +void datamap_set_float_format(datamap *map, int dlgitem, const char *format) +{ + datamap_entry *entry = find_entry(map, dlgitem); + entry->float_format = format; +} + + + +//============================================================ +// datamap_read_control +//============================================================ + +BOOL datamap_read_control(datamap *map, HWND dialog, windows_options &opts, int dlgitem) +{ + datamap_entry *entry = find_entry(map, dlgitem); + return control_operation(map, dialog, &opts, entry, DCT_READ_CONTROL); +} + + + +//============================================================ +// datamap_read_all_controls +//============================================================ + +void datamap_read_all_controls(datamap *map, HWND dialog, windows_options &opts) +{ + for (int i = 0; i < map->entry_count; i++) + control_operation(map, dialog, &opts, &map->entries[i], DCT_READ_CONTROL); +} + + + +//============================================================ +// datamap_populate_control +//============================================================ + +void datamap_populate_control(datamap *map, HWND dialog, windows_options &opts, int dlgitem) +{ + datamap_entry *entry = find_entry(map, dlgitem); + control_operation(map, dialog, &opts, entry, DCT_POPULATE_CONTROL); +} + + + +//============================================================ +// datamap_populate_all_controls +//============================================================ + +void datamap_populate_all_controls(datamap *map, HWND dialog, windows_options &opts) +{ + for (int i = 0; i < map->entry_count; i++) + control_operation(map, dialog, &opts, &map->entries[i], DCT_POPULATE_CONTROL); +} + + + +//============================================================ +// datamap_update_control +//============================================================ + +void datamap_update_control(datamap *map, HWND dialog, windows_options &opts, int dlgitem) +{ + datamap_entry *entry = find_entry(map, dlgitem); + control_operation(map, dialog, &opts, entry, DCT_UPDATE_STATUS); +} + + + +//============================================================ +// datamap_update_all_controls +//============================================================ + +void datamap_update_all_controls(datamap *map, HWND dialog, windows_options *opts) +{ + for (int i = 0; i < map->entry_count; i++) + control_operation(map, dialog, opts, &map->entries[i], DCT_UPDATE_STATUS); +} + + + +//============================================================ +// find_entry +//============================================================ + +static datamap_entry *find_entry(datamap *map, int dlgitem) +{ + for (int i = 0; i < map->entry_count; i++) + if (map->entries[i].dlgitem == dlgitem) + return &map->entries[i]; + + // should not reach here + printf("Datamap.cpp line __LINE__ couldn't find an entry\n"); + return NULL; +} + + + +//============================================================ +// get_control_type +//============================================================ + +static control_type get_control_type(HWND control) +{ + control_type type; + TCHAR class_name[256]; + + GetClassName(control, class_name, ARRAY_LENGTH(class_name)); + if (!_tcscmp(class_name, WC_BUTTON)) + type = CT_BUTTON; + else if (!_tcscmp(class_name, WC_STATIC)) + type = CT_STATIC; + else if (!_tcscmp(class_name, WC_EDIT)) + type = CT_EDIT; + else if (!_tcscmp(class_name, WC_COMBOBOX)) + type = CT_COMBOBOX; + else if (!_tcscmp(class_name, TRACKBAR_CLASS)) + type = CT_TRACKBAR; + else if (!_tcscmp(class_name, WC_LISTVIEW)) + type = CT_LISTVIEW; + else + type = CT_UNKNOWN; + + return type; +} + + + +//============================================================ +// is_control_displayonly +//============================================================ + +static BOOL is_control_displayonly(HWND control) +{ + BOOL displayonly = false; + switch(get_control_type(control)) + { + case CT_STATIC: + displayonly = true; + break; + + case CT_EDIT: + displayonly = (GetWindowLong(control, GWL_STYLE) & ES_READONLY) ? true : false; + break; + + default: + displayonly = false; + break; + } + + if (!IsWindowEnabled(control)) + displayonly = true; + return displayonly; +} + + + +//============================================================ +// broadcast_changes +//============================================================ + +static void broadcast_changes(datamap *map, HWND dialog, windows_options *opts, datamap_entry *entry, const char *option_name) +{ + HWND other_control; + const char *that_option_name; + + for (int i = 0; i < map->entry_count; i++) + { + // search for an entry with the same option_name, but is not the exact + // same entry + that_option_name = map->entries[i].option_name; + if (map->entries[i].option_name && (&map->entries[i] != entry) && !strcmp(that_option_name, option_name)) + { + // we've found a control sharing the same option; populate it + other_control = GetDlgItem(dialog, map->entries[i].dlgitem); + if (other_control) + populate_control(map, other_control, opts, &map->entries[i], that_option_name); + } + } +} + + + +//============================================================ +// control_operation +//============================================================ + +static int control_operation(datamap *map, HWND dialog, windows_options *opts, datamap_entry *entry, datamap_callback_type callback_type) +{ + static const datamap_default_callback default_callbacks[DCT_COUNT] = + { + read_control, + populate_control, + NULL + }; + int result = 0; + const char *option_name; + char option_name_buffer[64]; + char option_value[1024] = {0, }; + + HWND control = GetDlgItem(dialog, entry->dlgitem); + if (control) + { + // don't do anything if we're reading from a display-only control + if ((callback_type != DCT_READ_CONTROL) || !is_control_displayonly(control)) + { + // figure out the option_name + if (entry->get_option_name) + { + option_name_buffer[0] = '\0'; + entry->get_option_name(map, dialog, control, option_name_buffer, ARRAY_LENGTH(option_name_buffer)); + option_name = option_name_buffer; + } + else + option_name = entry->option_name; + + // if reading, get the option value, solely for the purposes of comparison + if ((callback_type == DCT_READ_CONTROL) && option_name) + snprintf(option_value, ARRAY_LENGTH(option_value), "%s", opts->value(option_name)); + + if (entry->callbacks[callback_type]) + { + // use custom callback + result = entry->callbacks[callback_type](map, dialog, control, opts, option_name); + } + else if (default_callbacks[callback_type] && option_name) + { + // use default callback + default_callbacks[callback_type](map, control, opts, entry, option_name); + } + + // the result is dependent on the type of control + switch(callback_type) + { + case DCT_READ_CONTROL: + // For callbacks that returned TRUE, do not broadcast_changes. + if (!result) { + // do a check to see if the control changed + result = (option_name) && (strcmp(option_value, opts->value(option_name)) != 0); + if (result) + { + // the value has changed; we may need to broadcast the change + broadcast_changes(map, dialog, opts, entry, option_name); + } + } + break; + + default: + // do nothing + break; + } + } + } + return result; +} + + + +//============================================================ +// trackbar_value_from_position +//============================================================ + +static float trackbar_value_from_position(datamap_entry *entry, int position) +{ + float position_f = position; + + if (entry->use_trackbar_options) + position_f = (position_f * entry->trackbar_increments) + entry->trackbar_min; + + return position_f; +} + + + +//============================================================ +// trackbar_position_from_value +//============================================================ + +static int trackbar_position_from_value(datamap_entry *entry, float value) +{ + if (entry->use_trackbar_options) + value = floor((value - entry->trackbar_min) / entry->trackbar_increments + 0.5); + + return (int) value; +} + + + +//============================================================ +// read_control +//============================================================ + +static void read_control(datamap *map, HWND control, windows_options *opts, datamap_entry *entry, const char *option_name) +{ + BOOL bool_value = 0; + int int_value = 0; + float float_value = 0; + const char *string_value; + int selected_index = 0; + int trackbar_pos = 0; + // use default read value behavior + switch(get_control_type(control)) + { + case CT_BUTTON: + //assert(entry->type == DM_BOOL); + bool_value = Button_GetCheck(control); + opts->set_value(option_name, bool_value, OPTION_PRIORITY_CMDLINE); + break; + + case CT_COMBOBOX: + selected_index = ComboBox_GetCurSel(control); + if (selected_index >= 0) + { + switch(entry->type) + { + case DM_INT: + int_value = (int) ComboBox_GetItemData(control, selected_index); + opts->set_value(option_name, int_value, OPTION_PRIORITY_CMDLINE); + break; + + case DM_STRING: + string_value = (const char *) ComboBox_GetItemData(control, selected_index); + opts->set_value(option_name, string_value ? string_value : "", OPTION_PRIORITY_CMDLINE); + break; + + default: + break; + } + } + break; + + case CT_TRACKBAR: + trackbar_pos = SendMessage(control, TBM_GETPOS, 0, 0); + float_value = trackbar_value_from_position(entry, trackbar_pos); + switch(entry->type) + { + case DM_INT: + int_value = (int) float_value; + if (int_value != opts->int_value(option_name)) { + opts->set_value(option_name, int_value, OPTION_PRIORITY_CMDLINE); + } + break; + + case DM_FLOAT: + // Use tztrim(float_value) or we get trailing zero's that break options_equal(). + if (float_value != opts->float_value(option_name)) { + opts->set_value(option_name, tztrim(float_value), OPTION_PRIORITY_CMDLINE); + } + break; + + default: + break; + } + break; + + case CT_EDIT: + // NYI + break; + + case CT_STATIC: + case CT_LISTVIEW: + case CT_UNKNOWN: + // non applicable + break; + } +} + + + +//============================================================ +// populate_control +//============================================================ + +static void populate_control(datamap *map, HWND control, windows_options *opts, datamap_entry *entry, const char *option_name) +{ + int i = 0; + BOOL bool_value = 0; + int int_value = 0; + float float_value = 0; + const char *string_value; + const char *item_string; + int selected_index = 0; + char buffer[128]; + int trackbar_range = 0; + int trackbar_pos = 0; + double trackbar_range_d = 0; + + // use default populate control value + switch(get_control_type(control)) + { + case CT_BUTTON: + assert(entry->type == DM_BOOL); + bool_value = opts->bool_value(option_name); + Button_SetCheck(control, bool_value); + break; + + case CT_EDIT: + case CT_STATIC: + switch(entry->type) + { + case DM_STRING: + string_value = opts->value(option_name); + break; + + case DM_INT: + int_value = opts->int_value(option_name); + if (entry->int_format != NULL) + snprintf(buffer, ARRAY_LENGTH(buffer), entry->int_format, int_value); + else + snprintf(buffer, ARRAY_LENGTH(buffer), "%d", int_value); + string_value = buffer; + break; + + case DM_FLOAT: + float_value = opts->float_value(option_name); + if (entry->float_format != NULL) + snprintf(buffer, ARRAY_LENGTH(buffer), entry->float_format, float_value); + else + snprintf(buffer, ARRAY_LENGTH(buffer), "%f", float_value); + string_value = buffer; + break; + + default: + string_value = ""; + break; + } + if (string_value == NULL) + string_value = ""; + win_set_window_text_utf8(control, string_value); + break; + + case CT_COMBOBOX: + selected_index = 0; + switch(entry->type) + { + case DM_INT: + int_value = opts->int_value(option_name); + for (i = 0; i < ComboBox_GetCount(control); i++) + { + if (int_value == (int) ComboBox_GetItemData(control, i)) + { + selected_index = i; + break; + } + } + break; + + case DM_STRING: + string_value = opts->value(option_name); + for (i = 0; i < ComboBox_GetCount(control); i++) + { + item_string = (const char *) ComboBox_GetItemData(control, i); + if (!core_stricmp(string_value, item_string ? item_string : "")) + { + selected_index = i; + break; + } + } + break; + + default: + break; + } + (void)ComboBox_SetCurSel(control, selected_index); + break; + + case CT_TRACKBAR: + // do we need to set the trackbar options? +/* if (!entry->use_trackbar_options) + { + switch(options_get_range_type(opts, option_name)) + { + case OPTION_RANGE_NONE: + // do nothing + break; + + case OPTION_RANGE_INT: + options_get_range_int(opts, option_name, &minval_int, &maxval_int); + entry->use_trackbar_options = TRUE; + entry->trackbar_min = minval_int; + entry->trackbar_max = maxval_int; + entry->trackbar_increments = 1; + break; + + case OPTION_RANGE_FLOAT: + options_get_range_float(opts, option_name, &minval_float, &maxval_float); + entry->use_trackbar_options = TRUE; + entry->trackbar_min = minval_float; + entry->trackbar_max = maxval_float; + entry->trackbar_increments = (float)0.05; + break; + } + } + */ + + // do we specify default options for this control? if so, we need to specify + // the range + if (entry->use_trackbar_options) + { + trackbar_range_d = floor(((entry->trackbar_max - entry->trackbar_min) + / entry->trackbar_increments) + 0.5); + trackbar_range = (int) trackbar_range_d; + SendMessage(control, TBM_SETRANGEMIN, (WPARAM) FALSE, (LPARAM) 0); + SendMessage(control, TBM_SETRANGEMAX, (WPARAM) FALSE, (LPARAM) trackbar_range); + } + + switch(entry->type) + { + case DM_INT: + int_value = opts->int_value(option_name); + trackbar_pos = trackbar_position_from_value(entry, int_value); + break; + + case DM_FLOAT: + float_value = opts->float_value(option_name); + trackbar_pos = trackbar_position_from_value(entry, float_value); + break; + + default: + trackbar_pos = 0; + break; + } + SendMessage(control, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) trackbar_pos); + break; + + case CT_LISTVIEW: + case CT_UNKNOWN: + // non applicable + break; + } +} + +// Return a string from a float value with trailing zeros removed. +static char *tztrim(float float_value) +{ + static char tz_string[20]; + char float_string[20]; + int i = 0; + + sprintf(float_string, "%f", float_value); + + char* ptr = float_string; + + // Copy before the '.' + while (*ptr && *ptr != '.') + tz_string[i++] = *ptr++; + + // add the '.' and the next digit + if (*ptr == '.') + { + tz_string[i++] = *ptr++; + tz_string[i++] = *ptr++; + } + // Keep copying until we hit a '0' + while (*ptr && *ptr != '0') + tz_string[i++] = *ptr++; + + // Null terminate + tz_string[i] = '\0'; + return tz_string; +} + |