//============================================================
//
//	menu.c - Win32 MESS menus handling
//
//============================================================

// standard windows headers
#include <windows.h>
#include <commdlg.h>
#include <winuser.h>
#include <ctype.h>
#include <tchar.h>

// MAME/MESS headers
#include "mame.h"
#include "menu.h"
#include "ui.h"
#include "messres.h"
#include "inputx.h"
#include "windows/video.h"
#include "windows/input.h"
#include "dialog.h"
#include "opcntrl.h"
#include "uitext.h"
#include "strconv.h"
#include "utils.h"
#include "tapedlg.h"
#include "artworkx.h"
#include "debug/debugcpu.h"
#include "inptport.h"
#include "devices/cassette.h"
#include "windows/window.h"
#include "uimess.h"
#include "winutf8.h"

#ifdef UNDER_CE
#include "invokegx.h"
#else
#include "configms.h"
#endif

//============================================================
//	IMPORTS
//============================================================

// from timer.c
extern void win_timer_enable(int enabled);


//============================================================
//	PARAMETERS
//============================================================

#define ID_FRAMESKIP_0				10000
#define ID_DEVICE_0					11000
#define ID_JOYSTICK_0				12000
#define ID_INPUT_0					13000
#define ID_VIDEO_VIEW_0				14000

#define MAX_JOYSTICKS				(8)

#define USE_TAPEDLG	0

enum
{
	DEVOPTION_OPEN,
	DEVOPTION_CREATE,
	DEVOPTION_CLOSE,
	DEVOPTION_CASSETTE_PLAYRECORD,
	DEVOPTION_CASSETTE_STOPPAUSE,
	DEVOPTION_CASSETTE_PLAY,
	DEVOPTION_CASSETTE_RECORD,
#if USE_TAPEDLG
	DEVOPTION_CASSETTE_DIALOG,
#else
	DEVOPTION_CASSETTE_REWIND,
	DEVOPTION_CASSETTE_FASTFORWARD,
#endif
	DEVOPTION_MAX
};

#ifdef MAME_DEBUG
#define HAS_PROFILER	1
#define HAS_DEBUGGER	1
#else
#define HAS_PROFILER	0
#define HAS_DEBUGGER	0
#endif

#ifdef UNDER_CE
#define HAS_TOGGLEMENUBAR		0
#define HAS_TOGGLEFULLSCREEN	0
#else
#define HAS_TOGGLEMENUBAR		1
#define HAS_TOGGLEFULLSCREEN	1
#endif

#ifdef UNDER_CE
#define WM_INITMENU		WM_INITMENUPOPUP
#define MFS_GRAYED		MF_GRAYED
#define WMSZ_BOTTOM		6
#endif

//============================================================
//	LOCAL VARIABLES
//============================================================

static int win_use_natural_keyboard;
static HICON device_icons[IO_COUNT];
static int use_input_categories;
static int joystick_menu_setup;
static char state_filename[MAX_PATH];

static int add_filter_entry(char *dest, size_t dest_len, const char *description, const char *extensions);


//============================================================
//	customize_input
//============================================================

static void customize_input(HWND wnd, const char *title, artwork_cust_type cust_type, int player, int inputclass, const char *section)
{
	dialog_box *dlg;
	input_port_entry *in;
	png_info png;
	struct inputform_customization customizations[128];
	RECT *pr;
	int this_inputclass, this_player, portslot_count, i;

	struct
	{
		input_port_entry *in;
		const RECT *pr;
	} portslots[256];

	/* check to see if there is any custom artwork for this configure dialog,
	 * and if so, retrieve the info */
	portslot_count = artwork_get_inputscreen_customizations(&png, cust_type, section,
		customizations, sizeof(customizations) / sizeof(customizations[0]));

	/* zero out the portslots that correspond to customizations */
	memset(portslots, 0, sizeof(portslots[0]) * portslot_count);

	/* create the dialog */
	dlg = win_dialog_init(title, NULL);
	if (!dlg)
		goto done;

	if (png.width > 0)
	{
		win_dialog_add_image(dlg, &png);
		win_dialog_add_separator(dlg);
	}

	in = Machine->input_ports;
	while(in->type != IPT_END)
	{
		this_inputclass = input_classify_port(in);
		if (input_port_name(in) && (this_inputclass == inputclass))
		{
			/* most of the time, the player parameter is the player number
			 * but in the case of INPUT_CLASS_CATEGORIZED, it is the
			 * category */
			if (inputclass == INPUT_CLASS_CATEGORIZED)
				this_player = in->category;
			else
                this_player = input_player_number(in);

			if (this_player == player)
			{
				/* check to see if the custom artwork for this configure dialog
				 * says anything about this input */
				pr = NULL;
				for (i = 0; customizations[i].ipt != IPT_END; i++)
				{
					if (in->type == customizations[i].ipt)
					{
						pr = (RECT *) alloca(sizeof(*pr));
						pr->left = customizations[i].x;
						pr->top = customizations[i].y;
						pr->right = pr->left + customizations[i].width;
						pr->bottom = pr->top + customizations[i].height;
						break;
					}
				}

				/* store this InputPort/RECT combo in our list.  we do not
				 * necessarily want to add it yet because we the INI might
				 * want to reorder the tab order */
				if (customizations[i].ipt == IPT_END)
					i = portslot_count++;
				if (i < (sizeof(portslots) / sizeof(portslots[0])))
				{
					portslots[i].in = in;
					portslots[i].pr = pr;
				}
			}
		}
		in++;
	}

	/* finally add the portselects to the dialog */
	if (portslot_count > (sizeof(portslots) / sizeof(portslots[0])))
		portslot_count = sizeof(portslots) / sizeof(portslots[0]);
	for (i = 0; i < portslot_count; i++)
	{
		if (portslots[i].in)
		{
			if (win_dialog_add_portselect(dlg, portslots[i].in, portslots[i].pr))
				goto done;
		}
	}

	/* ...and now add OK/Cancel */
	if (win_dialog_add_standard_buttons(dlg))
		goto done;

	/* ...and finally display the dialog */
	win_dialog_runmodal(wnd, dlg);

done:
	if (dlg)
		win_dialog_exit(dlg);
}



//============================================================
//	customize_joystick
//============================================================

static void customize_joystick(HWND wnd, int joystick_num)
{
	customize_input(wnd, "Joysticks/Controllers", ARTWORK_CUSTTYPE_JOYSTICK, joystick_num, INPUT_CLASS_CONTROLLER, NULL);
}



//============================================================
//	customize_keyboard
//============================================================

static void customize_keyboard(HWND wnd)
{
	customize_input(wnd, "Emulated Keyboard", ARTWORK_CUSTTYPE_KEYBOARD, 0, INPUT_CLASS_KEYBOARD, NULL);
}



//============================================================
//	customize_miscinput
//============================================================

static void customize_miscinput(HWND wnd)
{
	customize_input(wnd, "Miscellaneous Input", ARTWORK_CUSTTYPE_MISC, 0, INPUT_CLASS_MISC, NULL);
}



//============================================================
//	customize_categorizedinput
//============================================================

static void customize_categorizedinput(HWND wnd, const char *section, int category)
{
	assert(category > 0);
	customize_input(wnd, "Input", ARTWORK_CUSTTYPE_JOYSTICK, category, INPUT_CLASS_CATEGORIZED, section);
}



//============================================================
//	storeval_inputport
//============================================================

static void storeval_inputport(void *param, int val)
{
	input_port_entry *in = (input_port_entry *) param;
	in->default_value = (UINT16) val;
}



//============================================================
//	customize_switches
//============================================================

static void customize_switches(HWND wnd, int title_string_num, UINT32 ipt_name, UINT32 ipt_setting)
{
	dialog_box *dlg;
	input_port_entry *in;
	const char *switch_name = NULL;
	UINT32 type;

	dlg = win_dialog_init(ui_getstring(title_string_num), NULL);
	if (!dlg)
		goto done;

	for (in = Machine->input_ports; in->type != IPT_END; in++)
	{
		type = in->type;

		if (type == ipt_name)
		{
			if (input_port_active(in))
			{
				switch_name = input_port_name(in);
				if (win_dialog_add_combobox(dlg, switch_name, in->default_value, storeval_inputport, in))
					goto done;
			}
			else
			{
				switch_name = NULL;
			}
		}
		else if (type == ipt_setting)
		{
			if (switch_name)
			{
				if (win_dialog_add_combobox_item(dlg, input_port_name(in), in->default_value))
					goto done;
			}
		}
	}

	if (win_dialog_add_standard_buttons(dlg))
		goto done;

	win_dialog_runmodal(wnd, dlg);

done:
	if (dlg)
		win_dialog_exit(dlg);
}



//============================================================
//	customize_dipswitches
//============================================================

static void customize_dipswitches(HWND wnd)
{
	customize_switches(wnd, UI_dipswitches, IPT_DIPSWITCH_NAME, IPT_DIPSWITCH_SETTING);
}



//============================================================
//	customize_configuration
//============================================================

static void customize_configuration(HWND wnd)
{
	customize_switches(wnd, UI_configuration, IPT_CONFIG_NAME, IPT_CONFIG_SETTING);
}



//============================================================
//	customize_analogcontrols
//============================================================

static void store_delta(void *param, int val)
{
	((input_port_entry *) param)->analog.delta = val;
}



static void store_centerdelta(void *param, int val)
{
	((input_port_entry *) param)->analog.centerdelta = val;
}



static void store_reverse(void *param, int val)
{
	((input_port_entry *) param)->analog.reverse = val;
}



static void store_sensitivity(void *param, int val)
{
	((input_port_entry *) param)->analog.sensitivity = val;
}



static void customize_analogcontrols(HWND wnd)
{
	dialog_box *dlg;
	input_port_entry *in;
	const char *name;
	char buf[255];
	static const struct dialog_layout layout = { 120, 52 };

	dlg = win_dialog_init(ui_getstring(UI_analogcontrols), &layout);
	if (!dlg)
		goto done;

	in = Machine->input_ports;

	while (in->type != IPT_END)
	{
		if (port_type_is_analog(in->type))
		{
			name = input_port_name(in);

			_snprintf(buf, sizeof(buf) / sizeof(buf[0]),
				"%s %s", name, ui_getstring(UI_keyjoyspeed));
			if (win_dialog_add_adjuster(dlg, buf, in->analog.delta, 1, 255, FALSE, store_delta, in))
				goto done;

			_snprintf(buf, sizeof(buf) / sizeof(buf[0]),
				"%s %s", name, ui_getstring(UI_centerspeed));
			if (win_dialog_add_adjuster(dlg, buf, in->analog.centerdelta, 1, 255, FALSE, store_centerdelta, in))
				goto done;

			_snprintf(buf, sizeof(buf) / sizeof(buf[0]),
				"%s %s", name, ui_getstring(UI_reverse));
			if (win_dialog_add_combobox(dlg, buf, in->analog.reverse ? 1 : 0, store_reverse, in))
				goto done;
			if (win_dialog_add_combobox_item(dlg, ui_getstring(UI_off), 0))
				goto done;
			if (win_dialog_add_combobox_item(dlg, ui_getstring(UI_on), 1))
				goto done;

			_snprintf(buf, sizeof(buf) / sizeof(buf[0]),
				"%s %s", name, ui_getstring(UI_sensitivity));
			if (win_dialog_add_adjuster(dlg, buf, in->analog.sensitivity, 1, 255, TRUE, store_sensitivity, in))
				goto done;
		}
		in++;
	}

	if (win_dialog_add_standard_buttons(dlg))
		goto done;

	win_dialog_runmodal(wnd, dlg);

done:
	if (dlg)
		win_dialog_exit(dlg);
}



//============================================================
//	state_dialog
//============================================================

static void state_dialog(HWND wnd, win_file_dialog_type dlgtype,
	DWORD fileproc_flags, void (*mameproc)(running_machine *machine, const char *),
	running_machine *machine)
{
	win_open_file_name ofn;
	char *dir;
	int result = 0;
	char *src;
	char *dst;

	if (state_filename[0])
	{
		dir = osd_dirname(state_filename);
	}
	else
	{
		snprintf(state_filename, sizeof(state_filename) / sizeof(state_filename[0]),
			"%s State.sta", Machine->gamedrv->description);
		dir = NULL;

		src = state_filename;
		dst = state_filename;
		do
		{
			if ((*src == '\0') || isalnum(*src) || isspace(*src) || strchr("(),.", *src))
				*(dst++) = *src;
		}
		while(*(src++));
	}

	memset(&ofn, 0, sizeof(ofn));
	ofn.type = dlgtype;
	ofn.owner = wnd;
	ofn.flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | fileproc_flags;
	ofn.filter = "State Files (*.sta)|*.sta|All Files (*.*)|*.*";
	ofn.initial_directory = dir;

	snprintf(ofn.filename, ARRAY_LENGTH(ofn.filename), "%s", state_filename);

	result = win_get_file_name_dialog(&ofn);
	if (result)
	{
		snprintf(state_filename, ARRAY_LENGTH(state_filename), "%s", ofn.filename);

		mameproc(machine, state_filename);
	}
	if (dir)
		free(dir);
}



static void state_load(HWND wnd, running_machine *machine)
{
	state_dialog(wnd, WIN_FILE_DIALOG_OPEN, OFN_FILEMUSTEXIST, mame_schedule_load, machine);
}

static void state_save_as(HWND wnd, running_machine *machine)
{
	state_dialog(wnd, WIN_FILE_DIALOG_SAVE, OFN_OVERWRITEPROMPT, mame_schedule_save, machine);
}

static void state_save(running_machine *machine)
{
	mame_schedule_save(machine, state_filename);
}



//============================================================
//	format_combo_changed
//============================================================

struct file_dialog_params
{
	const struct IODevice *dev;
	int *create_format;
	option_resolution **create_args;
};

static void format_combo_changed(dialog_box *dialog, HWND dlgwnd, NMHDR *notification, void *changed_param)
{
	HWND wnd;
	int format_combo_val;
	const struct IODevice *dev;
	const struct OptionGuide *guide;
	const char *optspec;
	struct file_dialog_params *params;
	int has_option;
	TCHAR t_buf1[128];
	char *buf1;

	params = (struct file_dialog_params *) changed_param;

	// locate the format control
	format_combo_val = notification ? (((OFNOTIFY *) notification)->lpOFN->nFilterIndex - 1) : 0;
	if (format_combo_val < 0)
		format_combo_val = 0;
	*(params->create_format) = format_combo_val;

	// compute our parameters
	dev = params->dev;
	guide = dev->createimage_optguide;
	optspec = dev->createimage_options[format_combo_val].optspec;

	// set the default extension
	CommDlg_OpenSave_SetDefExt(GetParent(dlgwnd),
		dev->createimage_options[format_combo_val].extensions);

	// enumerate through all of the child windows
	wnd = NULL;
	while((wnd = FindWindowEx(dlgwnd, wnd, NULL, NULL)) != NULL)
	{
		// get label text, removing trailing NULL
		GetWindowText(wnd, t_buf1, sizeof(t_buf1) / sizeof(t_buf1[0]));
		buf1 = utf8_from_tstring(t_buf1);
		assert(buf1[strlen(buf1)-1] == ':');
		buf1[strlen(buf1)-1] = '\0';

		// find guide entry
		while(guide->option_type && strcmp(buf1, guide->display_name))
			guide++;

		wnd = GetNextWindow(wnd, GW_HWNDNEXT);
		if (wnd && guide)
		{
			// we now have the handle to the window, and the guide entry
			has_option = option_resolution_contains(optspec, guide->parameter);

			SendMessage(wnd, CB_GETLBTEXT, SendMessage(wnd, CB_GETCURSEL, 0, 0), (LPARAM) t_buf1);
			SendMessage(wnd, CB_RESETCONTENT, 0, 0);

			win_prepare_option_control(wnd,
				has_option ? guide : NULL,
				has_option ? optspec : NULL);
		}
		free(buf1);
	}
}



//============================================================
//	storeval_option_resolution
//============================================================

struct storeval_optres_params
{
	struct file_dialog_params *fdparams;
	const struct OptionGuide *guide_entry;
};

static void storeval_option_resolution(void *storeval_param, int val)
{
	option_resolution *resolution;
	struct storeval_optres_params *params;
	const struct IODevice *dev;
	char buf[16];

	params = (struct storeval_optres_params *) storeval_param;
	dev = params->fdparams->dev;

	// create the resolution, if necessary
	resolution = *(params->fdparams->create_args);
	if (!resolution)
	{
		resolution = option_resolution_create(dev->createimage_optguide, dev->createimage_options[*(params->fdparams->create_format)].optspec);
		if (!resolution)
			return;
		*(params->fdparams->create_args) = resolution;
	}

	snprintf(buf, sizeof(buf) / sizeof(buf[0]), "%d", val);
	option_resolution_add_param(resolution, params->guide_entry->identifier, buf);
}



//============================================================
//	build_option_dialog
//============================================================

static dialog_box *build_option_dialog(const struct IODevice *dev, char *filter, size_t filter_len, int *create_format, option_resolution **create_args)
{
	dialog_box *dialog;
	const struct OptionGuide *guide_entry;
	int found, i, pos;
	char buf[256];
	struct file_dialog_params *params;
	struct storeval_optres_params *storeval_params;
	const struct dialog_layout filedialog_layout = { 44, 220 };

	// make the filter
	pos = 0;
	for (i = 0; dev->createimage_options[i].optspec; i++)
	{
		pos += add_filter_entry(filter + pos, filter_len - pos,
			dev->createimage_options[i].description,
			dev->createimage_options[i].extensions);
	}

	// create the dialog
	dialog = win_dialog_init(NULL, &filedialog_layout);
	if (!dialog)
		goto error;

	// allocate the params
	params = (struct file_dialog_params *) win_dialog_malloc(dialog, sizeof(*params));
	if (!params)
		goto error;
	params->dev = dev;
	params->create_format = create_format;
	params->create_args = create_args;

	// set the notify handler; so that we get notified when the format dialog changed
	if (win_dialog_add_notification(dialog, CDN_TYPECHANGE, format_combo_changed, params))
		goto error;

	// loop through the entries
	for (guide_entry = dev->createimage_optguide; guide_entry->option_type != OPTIONTYPE_END; guide_entry++)
	{
		// make sure that this entry is present on at least one option specification
		found = FALSE;
		for (i = 0; dev->createimage_options[i].optspec; i++)
		{
			if (option_resolution_contains(dev->createimage_options[i].optspec, guide_entry->parameter))
			{
				found = TRUE;
				break;
			}
		}

		if (found)
		{
			storeval_params = (struct storeval_optres_params *) win_dialog_malloc(dialog, sizeof(*storeval_params));
			if (!storeval_params)
				goto error;
			storeval_params->fdparams = params;
			storeval_params->guide_entry = guide_entry;

			// this option is present on at least one of the specs
			switch(guide_entry->option_type) {
			case OPTIONTYPE_INT:
				snprintf(buf, sizeof(buf) / sizeof(buf[0]), "%s:", guide_entry->display_name);
				if (win_dialog_add_combobox(dialog, buf, 0, storeval_option_resolution, storeval_params))
					goto error;
				break;

			default:
				break;
			}
		}
	}

	return dialog;

error:
	if (dialog)
		win_dialog_exit(dialog);
	return NULL;
}



//============================================================
//	copy_extension_list
//============================================================

static int copy_extension_list(char *dest, size_t dest_len, const char *extensions)
{
	const char *s;
	int pos = 0;

	// our extension lists are comma delimited; Win32 expects to see lists
	// delimited by semicolons
	s = extensions;
	while(*s)
	{
		// append a semicolon if not at the beginning
		if (s != extensions)
			pos += snprintf(&dest[pos], dest_len - pos, ";");

		// append ".*"
		pos += snprintf(&dest[pos], dest_len - pos, "*.");

		// append the file extension
		while(*s && (*s != ','))
		{
			pos += snprintf(&dest[pos], dest_len - pos, "%c", *s);
			s++;
		}

		// if we found a comma, advance
		while(*s == ',')
			s++;
	}
	return pos;
}



//============================================================
//	add_filter_entry
//============================================================

static int add_filter_entry(char *dest, size_t dest_len, const char *description, const char *extensions)
{
	int pos = 0;

	// add the description
	pos += snprintf(&dest[pos], dest_len - pos, "%s (", description);

	// add the extensions to the description
	pos += copy_extension_list(&dest[pos], dest_len - pos, extensions);

	// add the trailing rparen and '|' character
	pos += snprintf(&dest[pos], dest_len - pos, ")|");

	// now add the extension list itself
	pos += copy_extension_list(&dest[pos], dest_len - pos, extensions);

	// append a '|'
	pos += snprintf(&dest[pos], dest_len - pos, "|");

	return pos;
}



//============================================================
//	build_generic_filter
//============================================================

static void build_generic_filter(const struct IODevice *dev, int is_save, char *filter, size_t filter_len)
{
	char *s;
	const char *file_extensions;

	s = filter;

	// common image types
	file_extensions = device_get_info_string(&dev->devclass, DEVINFO_STR_FILE_EXTENSIONS);
	s += add_filter_entry(filter, filter_len, "Common image types", file_extensions);

	// all files
	s += sprintf(s, "All files (*.*)|*.*|");

	// compressed
	if (!is_save)
		s += sprintf(s, "Compressed Images (*.zip)|*.zip|");

	*(s++) = '\0';
}



//============================================================
//	change_device
//============================================================

static void change_device(HWND wnd, mess_image *img, int is_save)
{
	dialog_box *dialog = NULL;
	char filter[2048];
	char filename[MAX_PATH];
	char buffer[512];
	const struct IODevice *dev = image_device(img);
	const char *initial_dir;
	BOOL result;
	int create_format = 0;
	option_resolution *create_args = NULL;
	image_error_t err;

	assert(dev);

	// get the file
	if (image_exists(img))
	{
		const char *imgname;
		imgname = image_basename(img);
		strncpyz(filename, imgname, sizeof(filename) / sizeof(filename[0]));
	}
	else
	{
		filename[0] = '\0';
	}

	// use image directory, if it is there
	get_devicedirectory(dev->type);
	initial_dir = image_working_directory(img);

	// add custom dialog elements, if appropriate
	if (is_save && dev->createimage_optguide && dev->createimage_options[0].optspec)
	{
		dialog = build_option_dialog(dev, filter, sizeof(filter) / sizeof(filter[0]), &create_format, &create_args);
		if (!dialog)
			goto done;
	}
	else
	{
		// build a normal filter
		build_generic_filter(dev, is_save, filter, sizeof(filter) / sizeof(filter[0]));
	}

	// display the dialog
	result = win_file_dialog(wnd, is_save ? WIN_FILE_DIALOG_SAVE : WIN_FILE_DIALOG_OPEN,
		dialog, filter, initial_dir, filename, sizeof(filename) / sizeof(filename[0]));
	if (result)
	{
		// mount the image
		if (is_save)
			err = image_create(img, filename, create_format, create_args);
		else
			err = image_load(img, filename);

		// error?
		if (err)
		{
			snprintf(buffer, sizeof(buffer) / sizeof(buffer[0]),
				"Error when %s the image: %s",
				is_save ? "creating" : "loading",
				image_error(img));

			win_message_box_utf8(wnd, buffer, APPNAME, MB_OK);
		}
	}

done:
	if (dialog)
		win_dialog_exit(dialog);
	if (create_args)
		option_resolution_close(create_args);
}



//============================================================
//	paste
//============================================================

static void paste(void)
{
	HANDLE h;
	LPSTR text;
	size_t mb_size;
	size_t w_size;
	LPWSTR wtext;

	if (!OpenClipboard(NULL))
		return;

	h = GetClipboardData(CF_TEXT);
	if (h)
	{
		text = GlobalLock(h);
		if (text)
		{
			mb_size = strlen(text);
			w_size = MultiByteToWideChar(CP_ACP, 0, text, mb_size, NULL, 0);
			wtext = (LPWSTR) alloca(w_size * sizeof(WCHAR));
			MultiByteToWideChar(CP_ACP, 0, text, mb_size, wtext, w_size);
			inputx_postn_utf16(wtext, w_size);
			GlobalUnlock(h);
		}
	}

	CloseClipboard();
}



//============================================================
//	pause
//============================================================

static void pause(running_machine *machine)
{
	mame_pause(machine, !winwindow_ui_is_paused());
}



//============================================================
//	get_menu_item_string
//============================================================

static BOOL get_menu_item_string(HMENU menu, UINT item, BOOL by_position, HMENU *sub_menu, LPTSTR buffer, size_t buffer_len)
{
	MENUITEMINFO mii;

	// clear out results
	memset(buffer, '\0', sizeof(*buffer) * buffer_len);
	if (sub_menu)
		*sub_menu = NULL;

	// prepare MENUITEMINFO structure
	memset(&mii, 0, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_TYPE | (sub_menu ? MIIM_SUBMENU : 0);
	mii.dwTypeData = buffer;
	mii.cch = buffer_len;

	// call GetMenuItemInfo()
	if (!GetMenuItemInfo(menu, item, by_position, &mii))
		return FALSE;

	// return results
	if (sub_menu)
		*sub_menu = mii.hSubMenu;
	if (mii.fType == MFT_SEPARATOR)
		_sntprintf(buffer, buffer_len, TEXT("-"));
	return TRUE;
}



//============================================================
//	find_sub_menu
//============================================================

static HMENU find_sub_menu(HMENU menu, const char *menutext, int create_sub_menu)
{
	int i;
	TCHAR buf[128];
	HMENU sub_menu;

	while(*menutext)
	{
		TCHAR *t_menutext = tstring_from_utf8(menutext);

		i = -1;
		do
		{
			if (!get_menu_item_string(menu, ++i, TRUE, &sub_menu, buf, ARRAY_LENGTH(buf)))
			{
				free(t_menutext);
				return NULL;
			}
		}
		while(_tcscmp(t_menutext, buf));

		free(t_menutext);

		if (!sub_menu && create_sub_menu)
		{
			MENUITEMINFO mii;
			memset(&mii, 0, sizeof(mii));
			mii.cbSize = sizeof(mii);
			mii.fMask = MIIM_SUBMENU;
			mii.hSubMenu = CreateMenu();
			if (!SetMenuItemInfo(menu, i, TRUE, &mii))
			{
				i = GetLastError();
				return NULL;
			}
			sub_menu = mii.hSubMenu;
		}
		menu = sub_menu;
		if (!menu)
			return NULL;
		menutext += strlen(menutext) + 1;
	}
	return menu;
}



//============================================================
//	set_command_state
//============================================================

static void set_command_state(HMENU menu_bar, UINT command, UINT state)
{
	BOOL result;

#ifdef UNDER_CE
	result = EnableMenuItem(menu_bar, command, (state & MFS_GRAYED ? MF_GRAYED : MF_ENABLED) | MF_BYCOMMAND);
	if (result)
		result = CheckMenuItem(menu_bar, command, (state & MFS_CHECKED ? MF_CHECKED : MF_UNCHECKED) | MF_BYCOMMAND) != 0xffffffff;
#else
	MENUITEMINFO mii;

	memset(&mii, 0, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE;
	mii.fState = state;
	result = SetMenuItemInfo(menu_bar, command, FALSE, &mii);
#endif
}



//============================================================
//	append_menu_utf8
//============================================================

static void append_menu_utf8(HMENU menu, UINT flags, UINT_PTR id, const char *str)
{
	TCHAR *t_str = str ? tstring_from_utf8(str) : NULL;
	AppendMenu(menu, flags, id, t_str);
	if (t_str)
		free(t_str);
}



//============================================================
//	append_menu_uistring
//============================================================

static void append_menu_uistring(HMENU menu, UINT flags, UINT_PTR id, int uistring)
{
	append_menu_utf8(menu, flags, id, (uistring >= 0) ? ui_getstring(uistring) : NULL);
}



//============================================================
//	remove_menu_items
//============================================================

static void remove_menu_items(HMENU menu)
{
	while(RemoveMenu(menu, 0, MF_BYPOSITION))
		;
}



//============================================================
//	setup_joystick_menu
//============================================================

static void setup_joystick_menu(HMENU menu_bar)
{
	int joystick_count = 0;
	HMENU joystick_menu;
	int i, j;
	HMENU submenu = NULL;
	const input_port_entry *in;
	const input_port_entry *in_setting;
	char buf[256];
	int child_count = 0;

	in = Machine->input_ports;
	use_input_categories = 0;
	while(in->type != IPT_END)
	{
		if (in->type == IPT_CATEGORY_NAME)
		{
			use_input_categories = 1;
			break;
		}
		in++;
	}

	joystick_menu = find_sub_menu(menu_bar, "&Options\0&Joysticks\0", TRUE);
	if (!joystick_menu)
		return;

	if (use_input_categories)
	{
		// using input categories
		for (i = 0; Machine->input_ports[i].type != IPT_END; i++)
		{
			in = &Machine->input_ports[i];
			if ((in->type) == IPT_CATEGORY_NAME)
			{
				submenu = CreateMenu();
				if (!submenu)
					return;

				// append all of the category settings
				for (j = i + 1; (Machine->input_ports[j].type) == IPT_CATEGORY_SETTING; j++)
				{
					in_setting = &Machine->input_ports[j];
					append_menu_utf8(submenu, MF_STRING, ID_INPUT_0 + j, in_setting->name);
				}

				// tack on the final items and the menu item
				AppendMenu(submenu, MF_SEPARATOR, 0, NULL);
				AppendMenu(submenu, MF_STRING, ID_INPUT_0 + i, TEXT("&Configure..."));
				append_menu_utf8(joystick_menu, MF_STRING | MF_POPUP, (UINT_PTR) submenu, in->name);
				child_count++;
			}
		}
	}
	else
	{
		// set up joystick menu
#ifndef UNDER_CE
		joystick_count = input_count_players();
#endif
		if (joystick_count > 0)
		{
			for(i = 0; i < joystick_count; i++)
			{
				snprintf(buf, sizeof(buf) / sizeof(buf[0]), "Joystick %i", i + 1);
				append_menu_utf8(joystick_menu, MF_STRING, ID_JOYSTICK_0 + i, buf);
				child_count++;
			}
		}
	}

	// last but not least, enable the joystick menu (or not)
	set_command_state(menu_bar, ID_OPTIONS_JOYSTICKS, child_count ? MFS_ENABLED : MFS_GRAYED);
}



//============================================================
//	is_windowed
//============================================================

static int is_windowed(void)
{
	return video_config.windowed;
}



//============================================================
//	frameskip_level_count
//============================================================

static int frameskip_level_count(void)
{
	static int count = -1;

	if (count < 0)
	{
		int frameskip_min = 0;
		int frameskip_max = 10;
		options_get_range_int(mame_options(), OPTION_FRAMESKIP, &frameskip_min, &frameskip_max);
		count = frameskip_max + 1;
	}
	return count;
}



//============================================================
//	prepare_menus
//============================================================

static void prepare_menus(HWND wnd)
{
	int i;
	const struct IODevice *dev;
	char buf[MAX_PATH];
	TCHAR t_buf[MAX_PATH];
	const char *s;
	HMENU menu_bar;
	HMENU video_menu;
	HMENU device_menu;
	HMENU sub_menu;
	UINT_PTR new_item;
	UINT flags;
	UINT flags_for_exists;
	UINT flags_for_writing;
	mess_image *img;
	int has_config, has_dipswitch, has_keyboard, has_analog, has_misc;
	const input_port_entry *in;
	UINT16 in_cat_value = 0;
	int frameskip;
	int orientation;
	int speed;
	LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA);
	win_window_info *window = (win_window_info *)ptr;
	const char *view_name;
	int view_index;

	menu_bar = GetMenu(wnd);
	if (!menu_bar)
		return;

	if (!joystick_menu_setup)
	{
		setup_joystick_menu(menu_bar);
		joystick_menu_setup = 1;
	}

	frameskip = video_get_frameskip();

	orientation = render_target_get_orientation(window->target);

	speed = video_get_throttle() ? video_get_speed_factor() : 0;

	has_config		= input_has_input_class(INPUT_CLASS_CONFIG);
	has_dipswitch	= input_has_input_class(INPUT_CLASS_DIPSWITCH);
	has_keyboard	= input_has_input_class(INPUT_CLASS_KEYBOARD);
	has_misc		= input_has_input_class(INPUT_CLASS_MISC);

	has_analog = 0;
	for (in = Machine->input_ports; in->type != IPT_END; in++)
	{
		if (port_type_is_analog(in->type))
		{
			has_analog = 1;
			break;
		}
	}

	set_command_state(menu_bar, ID_FILE_SAVESTATE,			state_filename[0] != '\0'					? MFS_ENABLED : MFS_GRAYED);

	set_command_state(menu_bar, ID_EDIT_PASTE,				inputx_can_post()							? MFS_ENABLED : MFS_GRAYED);

	set_command_state(menu_bar, ID_OPTIONS_PAUSE,			winwindow_ui_is_paused()					? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_OPTIONS_CONFIGURATION,	has_config									? MFS_ENABLED : MFS_GRAYED);
	set_command_state(menu_bar, ID_OPTIONS_DIPSWITCHES,		has_dipswitch								? MFS_ENABLED : MFS_GRAYED);
	set_command_state(menu_bar, ID_OPTIONS_MISCINPUT,		has_misc									? MFS_ENABLED : MFS_GRAYED);
	set_command_state(menu_bar, ID_OPTIONS_ANALOGCONTROLS,	has_analog									? MFS_ENABLED : MFS_GRAYED);
#if HAS_TOGGLEFULLSCREEN
	set_command_state(menu_bar, ID_OPTIONS_FULLSCREEN,		!is_windowed()								? MFS_CHECKED : MFS_ENABLED);
#endif // HAS_TOGGLEFULLSCREEN
	set_command_state(menu_bar, ID_OPTIONS_TOGGLEFPS,		ui_get_show_fps()							? MFS_CHECKED : MFS_ENABLED);
#if HAS_PROFILER
	set_command_state(menu_bar, ID_OPTIONS_PROFILER,		ui_get_show_profiler()						? MFS_CHECKED : MFS_ENABLED);
#endif

	set_command_state(menu_bar, ID_KEYBOARD_EMULATED,		(has_keyboard) ?
																(!win_use_natural_keyboard					? MFS_CHECKED : MFS_ENABLED)
																												: MFS_GRAYED);
	set_command_state(menu_bar, ID_KEYBOARD_NATURAL,		(has_keyboard && inputx_can_post()) ?
																(win_use_natural_keyboard					? MFS_CHECKED : MFS_ENABLED)
																												: MFS_GRAYED);
	set_command_state(menu_bar, ID_KEYBOARD_CUSTOMIZE,		has_keyboard								? MFS_ENABLED : MFS_GRAYED);

	set_command_state(menu_bar, ID_VIDEO_ROTATE_0,			(orientation == ROT0)						? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_VIDEO_ROTATE_90,			(orientation == ROT90)						? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_VIDEO_ROTATE_180,		(orientation == ROT180)						? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_VIDEO_ROTATE_270,		(orientation == ROT270)						? MFS_CHECKED : MFS_ENABLED);

	set_command_state(menu_bar, ID_THROTTLE_50,				(speed == 50)								? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_THROTTLE_100,			(speed == 100)								? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_THROTTLE_200,			(speed == 200)								? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_THROTTLE_500,			(speed == 500)								? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_THROTTLE_1000,			(speed == 1000)								? MFS_CHECKED : MFS_ENABLED);
	set_command_state(menu_bar, ID_THROTTLE_UNTHROTTLED,	(speed == 0)								? MFS_CHECKED : MFS_ENABLED);

	set_command_state(menu_bar, ID_FRAMESKIP_AUTO,			(frameskip < 0)								? MFS_CHECKED : MFS_ENABLED);
	for (i = 0; i < frameskip_level_count(); i++)
		set_command_state(menu_bar, ID_FRAMESKIP_0 + i,		(frameskip == i)							? MFS_CHECKED : MFS_ENABLED);

	// if we are using categorized input, we need to properly checkmark the categories
	if (use_input_categories)
	{
		for (i = 0; Machine->input_ports[i].type != IPT_END; i++)
		{
			in = &Machine->input_ports[i];
			switch(in->type) {
			case IPT_CATEGORY_NAME:
				in_cat_value = in->default_value;
				break;

			case IPT_CATEGORY_SETTING:
				set_command_state(menu_bar, ID_INPUT_0 + i, (in->default_value == in_cat_value) ? MFS_CHECKED : MFS_ENABLED);
				break;
			}
		}
	}

	// set up screens in video menu
	video_menu = find_sub_menu(menu_bar, "&Options\0&Video\0", FALSE);
	do
	{
		get_menu_item_string(video_menu, 0, TRUE, NULL, t_buf, ARRAY_LENGTH(t_buf));
		if (_tcscmp(t_buf, TEXT("-")))
			RemoveMenu(video_menu, 0, MF_BYPOSITION);
	}
	while(_tcscmp(t_buf, TEXT("-")));
	i = 0;
	view_index = render_target_get_view(window->target);
	while((view_name = render_target_get_view_name(window->target, i)) != NULL)
	{
		TCHAR *t_view_name = tstring_from_utf8(view_name);
		InsertMenu(video_menu, i, MF_BYPOSITION | (i == view_index ? MF_CHECKED : 0),
			ID_VIDEO_VIEW_0 + i, t_view_name);
		free(t_view_name);
		i++;
	}

	// set up device menu; first remove all existing menu items
	device_menu = find_sub_menu(menu_bar, "&Devices\0", FALSE);
	remove_menu_items(device_menu);

	// then set up the actual devices
	for (dev = Machine->devices; dev->type < IO_COUNT; dev++)
	{
		for (i = 0; i < dev->count; i++)
		{
			img = image_from_device_and_index(dev, i);

			if (!dev->not_working)
			{
				new_item = ID_DEVICE_0 + (image_absolute_index(img) * DEVOPTION_MAX);
				flags_for_exists = MF_STRING;

				if (!image_exists(img))
					flags_for_exists |= MF_GRAYED;

				flags_for_writing = flags_for_exists;
				if (!image_is_writable(img))
					flags_for_writing |= MF_GRAYED;

				sub_menu = CreateMenu();
				append_menu_uistring(sub_menu, MF_STRING,		new_item + DEVOPTION_OPEN,		UI_mount);

				if (dev->creatable)
					append_menu_uistring(sub_menu, MF_STRING,	new_item + DEVOPTION_CREATE,	UI_create);

				append_menu_uistring(sub_menu, flags_for_exists,	new_item + DEVOPTION_CLOSE,	UI_unmount);

#if HAS_WAVE
				if ((dev->type == IO_CASSETTE) && !strcmp(dev->file_extensions, "wav"))
				{
					cassette_state state;
					state = image_exists(img) ? (cassette_get_state(img) & CASSETTE_MASK_UISTATE) : CASSETTE_STOPPED;
					append_menu_uistring(sub_menu, MF_SEPARATOR, 0, -1);
					append_menu_uistring(sub_menu, flags_for_exists	| ((state == CASSETTE_STOPPED)	? MF_CHECKED : 0),	new_item + DEVOPTION_CASSETTE_STOPPAUSE,	UI_pauseorstop);
					append_menu_uistring(sub_menu, flags_for_exists	| ((state == CASSETTE_PLAY)		? MF_CHECKED : 0),	new_item + DEVOPTION_CASSETTE_PLAY,			UI_play);
					append_menu_uistring(sub_menu, flags_for_writing	| ((state == CASSETTE_RECORD)	? MF_CHECKED : 0),	new_item + DEVOPTION_CASSETTE_RECORD,		UI_record);
#if USE_TAPEDLG
					append_menu_uistring(sub_menu, flags_for_exists,														new_item + DEVOPTION_CASSETTE_DIALOG,		UI_tapecontrol);
#else
					append_menu_uistring(sub_menu, flags_for_exists,														new_item + DEVOPTION_CASSETTE_REWIND,		UI_rewind);
					append_menu_uistring(sub_menu, flags_for_exists,														new_item + DEVOPTION_CASSETTE_FASTFORWARD,	UI_fastforward);
#endif
				}
#endif /* HAS_WAVE */
				s = image_exists(img) ? image_filename(img) : ui_getstring(UI_emptyslot);
				flags = MF_POPUP;
			}
			else
			{
				sub_menu = NULL;
				s = "Not working";
				flags = MF_DISABLED | MF_GRAYED;
			}

			snprintf(buf, sizeof(buf) / sizeof(buf[0]), "%s: %s", image_typename_id(img), s);
			append_menu_utf8(device_menu, flags, (UINT_PTR) sub_menu, buf);
		}
	}
}



//============================================================
//	set_seped
//============================================================

static void set_speed(int speed)
{
	if (speed != 0)
		video_set_speed_factor(speed);
	video_set_throttle(speed != 0);
}



//============================================================
//	win_toggle_menubar
//============================================================

void win_toggle_menubar(void)
{
	win_window_info *window;
	LONG width_diff;
	LONG height_diff;
	DWORD style, exstyle;
	HWND hwnd;
	HMENU menu;

	for (window = win_window_list; window != NULL; window = window->next)
	{
		RECT before_rect = { 100, 100, 200, 200 };
		RECT after_rect = { 100, 100, 200, 200 };

		hwnd = window->hwnd;

		// get current menu
		menu = GetMenu(hwnd);

		// get before rect
		style = GetWindowLong(hwnd, GWL_STYLE);
		exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
		AdjustWindowRectEx(&before_rect, style, menu ? TRUE : FALSE, exstyle);

		// toggle the menu
		if (menu)
		{
			SetProp(hwnd, TEXT("menu"), (HANDLE) menu);
			menu = NULL;
		}
		else
		{
			menu = (HMENU) GetProp(hwnd, TEXT("menu"));
		}
		SetMenu(hwnd, menu);

		// get after rect, and width/height diff
		AdjustWindowRectEx(&after_rect, style, menu ? TRUE : FALSE, exstyle);
		width_diff = (after_rect.right - after_rect.left) - (before_rect.right - before_rect.left);
		height_diff = (after_rect.bottom - after_rect.top) - (before_rect.bottom - before_rect.top);

		if (is_windowed())
		{
			RECT window_rect;
			GetWindowRect(hwnd, &window_rect);
			SetWindowPos(hwnd, HWND_TOP, 0, 0,
				window_rect.right - window_rect.left + width_diff,
				window_rect.bottom - window_rect.top + height_diff,
				SWP_NOMOVE | SWP_NOZORDER);
		}

		RedrawWindow(hwnd, NULL, NULL, 0);
	}
}



//============================================================
//	device_command
//============================================================

static void device_command(HWND wnd, mess_image *img, int devoption)
{
	switch(devoption)
	{
		case DEVOPTION_OPEN:
			change_device(wnd, img, FALSE);
			break;

		case DEVOPTION_CREATE:
			change_device(wnd, img, TRUE);
			break;

		case DEVOPTION_CLOSE:
			image_unload(img);
			break;

		default:
			switch(image_devtype(img))
			{
#if HAS_WAVE
				case IO_CASSETTE:
					switch(devoption) {
					case DEVOPTION_CASSETTE_STOPPAUSE:
						cassette_change_state(img, CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
						break;

					case DEVOPTION_CASSETTE_PLAY:
						cassette_change_state(img, CASSETTE_PLAY, CASSETTE_MASK_UISTATE);
						break;

					case DEVOPTION_CASSETTE_RECORD:
						cassette_change_state(img, CASSETTE_RECORD, CASSETTE_MASK_UISTATE);
						break;

#if USE_TAPEDLG
					case DEVOPTION_CASSETTE_DIALOG:
						tapedialog_show(wnd, id);
						break;
#else
					case DEVOPTION_CASSETTE_REWIND:
						cassette_seek(img, +1.0, SEEK_CUR);
						break;

					case DEVOPTION_CASSETTE_FASTFORWARD:
						cassette_seek(img, +1.0, SEEK_CUR);
						break;
#endif
				}
				break;
#endif /* HAS_WAVE */

			default:
				break;
		}
	}
}



//============================================================
//	help_display
//============================================================

static void help_display(HWND wnd, const char *chapter)
{
	typedef HWND (WINAPI *htmlhelpproc)(HWND hwndCaller, LPCTSTR pszFile, UINT uCommand, DWORD_PTR dwData);
	static htmlhelpproc htmlhelp;
	static DWORD htmlhelp_cookie;
	LPCSTR htmlhelp_funcname;

	if (htmlhelp == NULL)
	{
#ifdef UNICODE
		htmlhelp_funcname = "HtmlHelpW";
#else
		htmlhelp_funcname = "HtmlHelpA";
#endif
		htmlhelp = (htmlhelpproc) GetProcAddress(LoadLibrary(TEXT("hhctrl.ocx")), htmlhelp_funcname);
		if (!htmlhelp)
			return;
		htmlhelp(NULL, NULL, 28 /*HH_INITIALIZE*/, (DWORD_PTR) &htmlhelp_cookie);
	}

	// if full screen, turn it off
	if (!is_windowed())
		winwindow_toggle_full_screen();

	{
		TCHAR *t_chapter = tstring_from_utf8(chapter);
		htmlhelp(wnd, t_chapter, 0 /*HH_DISPLAY_TOPIC*/, 0);
		free(t_chapter);
	}
}



//============================================================
//	help_about_mess
//============================================================

static void help_about_mess(HWND wnd)
{
	help_display(wnd, "mess.chm::/windows/main.htm");
}



//============================================================
//	help_about_thissystem
//============================================================

static void help_about_thissystem(HWND wnd)
{
	char buf[256];
	snprintf(buf, sizeof(buf) / sizeof(buf[0]), "mess.chm::/sysinfo/%s.htm", Machine->gamedrv->name);
	help_display(wnd, buf);
}



//============================================================
//	decode_deviceoption
//============================================================

static mess_image *decode_deviceoption(int command, int *devoption)
{
	int absolute_index;

	command -= ID_DEVICE_0;
	absolute_index = command / DEVOPTION_MAX;

	if (devoption)
		*devoption = command % DEVOPTION_MAX;

	return image_from_absolute_index(absolute_index);
}



//============================================================
//	set_window_orientation
//============================================================

static void set_window_orientation(win_window_info *window, int orientation)
{
	render_target_set_orientation(window->target, orientation);
	if (window->target == render_get_ui_target())
		render_container_set_orientation(render_container_get_ui(), orientation);
	winwindow_video_window_update(window);
}



//============================================================
//	pause_for_command
//============================================================

static int pause_for_command(UINT command)
{
	// we really should be more conservative and only pause for commands
	// that do dialog stuff
	return (command != ID_OPTIONS_PAUSE);
}



//============================================================
//	invoke_command
//============================================================

static int invoke_command(HWND wnd, UINT command)
{
	int handled = 1;
	int dev_command, i;
	mess_image *img;
	int port_count;
	UINT16 setting, category;
	input_port_entry *in;
	const char *section;
	LONG_PTR ptr = GetWindowLongPtr(wnd, GWLP_USERDATA);
	win_window_info *window = (win_window_info *)ptr;

	// pause while invoking certain commands
	if (pause_for_command(command))
		winwindow_ui_pause_from_window_thread(TRUE);

	switch(command)
	{
		case ID_FILE_LOADSTATE:
			state_load(wnd, Machine);
			break;

		case ID_FILE_SAVESTATE:
			state_save(Machine);
			break;

		case ID_FILE_SAVESTATE_AS:
			state_save_as(wnd, Machine);
			break;

		case ID_FILE_SAVESCREENSHOT:
			video_save_active_screen_snapshots(Machine);
			break;

		case ID_FILE_EXIT:
			mame_schedule_exit(Machine);
			break;

		case ID_EDIT_PASTE:
			paste();
			break;

		case ID_KEYBOARD_NATURAL:
			win_use_natural_keyboard = 1;
			break;

		case ID_KEYBOARD_EMULATED:
			win_use_natural_keyboard = 0;
			break;

		case ID_KEYBOARD_CUSTOMIZE:
			customize_keyboard(wnd);
			break;

		case ID_VIDEO_ROTATE_0:
			set_window_orientation(window, ROT0);
			break;

		case ID_VIDEO_ROTATE_90:
			set_window_orientation(window, ROT90);
			break;

		case ID_VIDEO_ROTATE_180:
			set_window_orientation(window, ROT180);
			break;

		case ID_VIDEO_ROTATE_270:
			set_window_orientation(window, ROT270);
			break;

		case ID_OPTIONS_PAUSE:
			pause(Machine);
			break;

		case ID_OPTIONS_HARDRESET:
			mame_schedule_hard_reset(Machine);
			break;

		case ID_OPTIONS_SOFTRESET:
			mame_schedule_soft_reset(Machine);
			break;

#if HAS_PROFILER
		case ID_OPTIONS_PROFILER:
			ui_set_show_profiler(!ui_get_show_profiler());
			break;
#endif // HAS_PROFILER

#if HAS_DEBUGGER
		case ID_OPTIONS_DEBUGGER:
			debug_halt_on_next_instruction();
			break;
#endif // HAS_DEBUGGER

		case ID_OPTIONS_CONFIGURATION:
			customize_configuration(wnd);
			break;

		case ID_OPTIONS_DIPSWITCHES:
			customize_dipswitches(wnd);
			break;

		case ID_OPTIONS_MISCINPUT:
			customize_miscinput(wnd);
			break;

		case ID_OPTIONS_ANALOGCONTROLS:
			customize_analogcontrols(wnd);
			break;

#if HAS_TOGGLEFULLSCREEN
		case ID_OPTIONS_FULLSCREEN:
			winwindow_toggle_full_screen();
			break;
#endif

		case ID_OPTIONS_TOGGLEFPS:
			ui_set_show_fps(!ui_get_show_fps());
			break;

		case ID_OPTIONS_USEMOUSE:
			{
				// FIXME
//				extern int win_use_mouse;
//				win_use_mouse = !win_use_mouse;
			}
			break;

#if HAS_TOGGLEMENUBAR
		case ID_OPTIONS_TOGGLEMENUBAR:
			win_toggle_menubar();
			break;
#endif

		case ID_FRAMESKIP_AUTO:
			video_set_frameskip(-1);
			break;

		case ID_HELP_ABOUT:
			help_about_mess(wnd);
			break;

		case ID_HELP_ABOUTSYSTEM:
			help_about_thissystem(wnd);
			break;

		case ID_THROTTLE_50:
			set_speed(50);
			break;

		case ID_THROTTLE_100:
			set_speed(100);
			break;

		case ID_THROTTLE_200:
			set_speed(200);
			break;

		case ID_THROTTLE_500:
			set_speed(500);
			break;

		case ID_THROTTLE_1000:
			set_speed(1000);
			break;

		case ID_THROTTLE_UNTHROTTLED:
			set_speed(0);
			break;

		default:
			// quickly come up with a port count, so we can upper bound commands
			// near ID_INPUT_0
			port_count = 0;
			while(Machine->input_ports[port_count].type != IPT_END)
				port_count++;

			if ((command >= ID_FRAMESKIP_0) && (command < ID_FRAMESKIP_0 + frameskip_level_count()))
			{
				// change frameskip
				video_set_frameskip(command - ID_FRAMESKIP_0);
			}
			else if ((command >= ID_DEVICE_0) && (command < ID_DEVICE_0 + (MAX_DEV_INSTANCES*IO_COUNT*DEVOPTION_MAX)))
			{
				// change device
				img = decode_deviceoption(command, &dev_command);
				device_command(wnd, img, dev_command);
			}
			else if ((command >= ID_JOYSTICK_0) && (command < ID_JOYSTICK_0 + MAX_JOYSTICKS))
			{
				// customize joystick
				customize_joystick(wnd, command - ID_JOYSTICK_0);
			}
			else if ((command >= ID_VIDEO_VIEW_0) && (command < ID_VIDEO_VIEW_0 + 1000))
			{
				// render views
				render_target_set_view(window->target, command - ID_VIDEO_VIEW_0);
			}
			else if ((command >= ID_INPUT_0) && (command < ID_INPUT_0 + port_count))
			{
				// customize categorized input
				in = &Machine->input_ports[command - ID_INPUT_0];
				switch(in->type) {
				case IPT_CATEGORY_NAME:
					// customize the input type
					category = 0;
					section = NULL;
					for (i = 1; (in[i].type) == IPT_CATEGORY_SETTING; i++)
					{
						if (in[i].default_value == in[0].default_value)
						{
							category = in[i].category;
							section = in[i].name;
						}
					}
					customize_categorizedinput(wnd, section, category);
					break;

				case IPT_CATEGORY_SETTING:
					// change the input type for this category
					setting = in->default_value;
					while((in->type) != IPT_CATEGORY_NAME)
						in--;
					in->default_value = setting;
					break;

				default:
					// should never happen
					handled = 0;
					break;
				}
			}
			else
			{
				// bogus command
				handled = 0;
			}
			break;
	}

	// resume emulation
	if (pause_for_command(command))
		winwindow_ui_pause_from_window_thread(FALSE);

	return handled;
}



//============================================================
//	set_menu_text
//============================================================

static void set_menu_text(HMENU menu_bar, int command, const char *text)
{
	TCHAR *t_text;
	MENUITEMINFO mii;

	// convert to TCHAR
	t_text = tstring_from_utf8(text);

	// invoke SetMenuItemInfo()
	memset(&mii, 0, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_TYPE;
	mii.dwTypeData = t_text;
	SetMenuItemInfo(menu_bar, command, FALSE, &mii);

	// cleanup
	free(t_text);
}



//============================================================
//	win_setup_menus
//============================================================

int win_setup_menus(HMODULE module, HMENU menu_bar)
{
	HMENU frameskip_menu;
	char buf[256];
	int i;

	static const int bitmap_ids[][2] =
	{
		{ IO_CARTSLOT,	IDI_ICON_CART },
		{ IO_HARDDISK,	IDI_ICON_HARD },
		{ IO_CASSETTE,	IDI_ICON_CASS },
		{ IO_FLOPPY,	IDI_ICON_FLOP },
		{ IO_PRINTER,	IDI_ICON_PRIN },
		{ IO_SERIAL,	IDI_ICON_SERL },
		{ IO_SNAPSHOT,	IDI_ICON_SNAP }
	};

	// verify that our magic numbers work
	assert((ID_DEVICE_0 + IO_COUNT * MAX_DEV_INSTANCES * DEVOPTION_MAX) < ID_JOYSTICK_0);

	// initialize critical values
	joystick_menu_setup = 0;

	// get the device icons
	memset(device_icons, 0, sizeof(device_icons));
	for (i = 0; i < sizeof(bitmap_ids) / sizeof(bitmap_ids[0]); i++)
		device_icons[bitmap_ids[i][0]] = LoadIcon(module, MAKEINTRESOURCE(bitmap_ids[i][1]));

	// remove the profiler menu item if it doesn't exist
#if HAS_PROFILER
	ui_set_show_profiler(0);
#else
	DeleteMenu(menu_bar, ID_OPTIONS_PROFILER, MF_BYCOMMAND);
#endif

	if (!HAS_DEBUGGER || !Machine->debug_mode)
		DeleteMenu(menu_bar, ID_OPTIONS_DEBUGGER, MF_BYCOMMAND);

#if !HAS_TOGGLEFULLSCREEN
	DeleteMenu(menu_bar, ID_OPTIONS_FULLSCREEN, MF_BYCOMMAND);
#endif

#if !HAS_TOGGLEMENUBAR
	DeleteMenu(menu_bar, ID_OPTIONS_TOGGLEMENUBAR, MF_BYCOMMAND);
#endif

	// set up frameskip menu
	frameskip_menu = find_sub_menu(menu_bar, "&Options\0&Frameskip\0", FALSE);
	if (!frameskip_menu)
		return 1;
	for(i = 0; i < frameskip_level_count(); i++)
	{
		snprintf(buf, sizeof(buf) / sizeof(buf[0]), "%i", i);
		append_menu_utf8(frameskip_menu, MF_STRING, ID_FRAMESKIP_0 + i, buf);
	}

	// set the help menu to refer to this machine
	snprintf(buf, sizeof(buf) / sizeof(buf[0]), "About %s (%s)...", Machine->gamedrv->description, Machine->gamedrv->name);
	set_menu_text(menu_bar, ID_HELP_ABOUTSYSTEM, buf);
	return 0;
}



//============================================================
//	win_resource_module
//============================================================

HMODULE win_resource_module(void)
{
	static HMODULE module;
	if (module == NULL)
	{
#ifdef _MSC_VER
		// doing this because of lame build problems with EMULATORDLL, and how
		// vconv is invoked
		MEMORY_BASIC_INFORMATION info;
		if ((VirtualQuery(win_resource_module, &info, sizeof(info))) == sizeof(info))
			module = (HMODULE)info.AllocationBase;
#else // !_MSC_VER
		module = LoadLibrary(TEXT(EMULATORDLL));
#endif // MSC_VER
	}
	return module;
}



//============================================================
//	win_create_menu
//============================================================

#ifndef UNDER_CE
int win_create_menu(HMENU *menus)
{
	HMENU menu_bar = NULL;
	HMODULE module;

	// determine whether we are using the natural keyboard or not
	win_use_natural_keyboard = options_get_bool(mame_options(), "natural");

	if (mess_use_new_ui())
	{
		module = win_resource_module();
		menu_bar = LoadMenu(module, MAKEINTRESOURCE(IDR_RUNTIME_MENU));
		if (!menu_bar)
			goto error;

		if (win_setup_menus(module, menu_bar))
			goto error;
	}

	*menus = menu_bar;
	return 0;

error:
	if (menu_bar)
		DestroyMenu(menu_bar);
	return 1;
}
#endif /* UNDER_CE */



//============================================================
//	win_mess_window_proc
//============================================================

LRESULT CALLBACK win_mess_window_proc(HWND wnd, UINT message, WPARAM wparam, LPARAM lparam)
{
	int i;
	MSG msg;

	static const WPARAM keytrans[][2] =
	{
		{ VK_ESCAPE,	UCHAR_MAMEKEY(ESC) },
		{ VK_F1,		UCHAR_MAMEKEY(F1) },
		{ VK_F2,		UCHAR_MAMEKEY(F2) },
		{ VK_F3,		UCHAR_MAMEKEY(F3) },
		{ VK_F4,		UCHAR_MAMEKEY(F4) },
		{ VK_F5,		UCHAR_MAMEKEY(F5) },
		{ VK_F6,		UCHAR_MAMEKEY(F6) },
		{ VK_F7,		UCHAR_MAMEKEY(F7) },
		{ VK_F8,		UCHAR_MAMEKEY(F8) },
		{ VK_F9,		UCHAR_MAMEKEY(F9) },
		{ VK_F10,		UCHAR_MAMEKEY(F10) },
		{ VK_F11,		UCHAR_MAMEKEY(F11) },
		{ VK_F12,		UCHAR_MAMEKEY(F12) },
		{ VK_NUMLOCK,	UCHAR_MAMEKEY(F12) },
		{ VK_SCROLL,	UCHAR_MAMEKEY(F12) },
		{ VK_NUMPAD0,	UCHAR_MAMEKEY(0_PAD) },
		{ VK_NUMPAD1,	UCHAR_MAMEKEY(1_PAD) },
		{ VK_NUMPAD2,	UCHAR_MAMEKEY(2_PAD) },
		{ VK_NUMPAD3,	UCHAR_MAMEKEY(3_PAD) },
		{ VK_NUMPAD4,	UCHAR_MAMEKEY(4_PAD) },
		{ VK_NUMPAD5,	UCHAR_MAMEKEY(5_PAD) },
		{ VK_NUMPAD6,	UCHAR_MAMEKEY(6_PAD) },
		{ VK_NUMPAD7,	UCHAR_MAMEKEY(7_PAD) },
		{ VK_NUMPAD8,	UCHAR_MAMEKEY(8_PAD) },
		{ VK_NUMPAD9,	UCHAR_MAMEKEY(9_PAD) },
		{ VK_DECIMAL,	UCHAR_MAMEKEY(DEL_PAD) },
		{ VK_ADD,		UCHAR_MAMEKEY(PLUS_PAD) },
		{ VK_SUBTRACT,	UCHAR_MAMEKEY(MINUS_PAD) },
		{ VK_INSERT,	UCHAR_MAMEKEY(INSERT) },
		{ VK_DELETE,	UCHAR_MAMEKEY(DEL) },
		{ VK_HOME,		UCHAR_MAMEKEY(HOME) },
		{ VK_END,		UCHAR_MAMEKEY(END) },
		{ VK_PRIOR,		UCHAR_MAMEKEY(PGUP) },
		{ VK_NEXT,		UCHAR_MAMEKEY(PGDN) },
		{ VK_UP,		UCHAR_MAMEKEY(UP) },
		{ VK_DOWN,		UCHAR_MAMEKEY(DOWN) },
		{ VK_LEFT,		UCHAR_MAMEKEY(LEFT) },
		{ VK_RIGHT,		UCHAR_MAMEKEY(RIGHT) },
		{ VK_PAUSE,		UCHAR_MAMEKEY(PAUSE) },
		{ VK_CANCEL,	UCHAR_MAMEKEY(CANCEL) }
	};

	if (win_use_natural_keyboard && (message == WM_KEYDOWN))
	{
		for (i = 0; i < sizeof(keytrans) / sizeof(keytrans[0]); i++)
		{
			if (wparam == keytrans[i][0])
			{
				inputx_postc(keytrans[i][1]);
				message = WM_NULL;

				/* check to see if there is a corresponding WM_CHAR in our
				 * future.  If so, remove it
				 */
				PeekMessage(&msg, wnd, 0, 0, PM_NOREMOVE);
				if ((msg.message == WM_CHAR) && (msg.lParam == lparam))
					PeekMessage(&msg, wnd, 0, 0, PM_REMOVE);
				break;
			}
		}
	}

	switch(message)
	{
		case WM_INITMENU:
			prepare_menus(wnd);
			break;

		case WM_CHAR:
			if (win_use_natural_keyboard)
				inputx_postc(wparam);
			break;

		case WM_COMMAND:
			if (invoke_command(wnd, wparam))
				break;
			/* fall through */

		default:
			return winwindow_video_window_proc(wnd, message, wparam, lparam);
	}
	return 0;
}

//============================================================
//	osd_keyboard_disabled
//============================================================

int osd_keyboard_disabled(void)
{
	return win_use_natural_keyboard;
}
