/* NetHack may be freely redistributed.  See license for details. */

#include "winMS.h"
#include <assert.h>
#include "mhmenu.h"
#include "mhmain.h"
#include "mhmsg.h"
#include "mhcmd.h"
#include "mhinput.h"
#include "mhfont.h"
#include "mhcolor.h"
#include "mhtxtbuf.h"

#define MENU_MARGIN			0
#define NHMENU_STR_SIZE     BUFSZ
#define MIN_TABSTOP_SIZE	0
#define NUMTABS				15
#define TAB_SEPARATION		10 /* pixels between each tab stop */

typedef struct mswin_menu_item {
	int				glyph;
	ANY_P			identifier;
	CHAR_P			accelerator;
	CHAR_P			group_accel;
	int				attr;
	char			str[NHMENU_STR_SIZE];
	BOOLEAN_P		presel;
	int				count;
	BOOL			has_focus;
	BOOL			has_tab;
} NHMenuItem, *PNHMenuItem;

typedef struct mswin_nethack_menu_window {
	int type;		/* MENU_TYPE_TEXT or MENU_TYPE_MENU */
	int how;		/* for menus: PICK_NONE, PICK_ONE, PICK_ANY */

	union {
		struct menu_list {
			int				 size;			/* number of items in items[] */
			int				 allocated;		/* number of allocated slots in items[] */
			PNHMenuItem		 items;			/* menu items */
			char			 gacc[QBUFSZ];	/* group accelerators */
			BOOL			 counting;		/* counting flag */
			char			 prompt[QBUFSZ]; /* menu prompt */
			int				 tab_stop_size[NUMTABS];/* tabstops to align option values */
		} menu;

		struct menu_text {
			PNHTextBuffer	text;
		} text;
	};
	int result;
	int done;

	HBITMAP bmpChecked;
	HBITMAP bmpCheckedCount;
	HBITMAP bmpNotChecked;
} NHMenuWindow, *PNHMenuWindow;

extern short glyph2tile[];

static WNDPROC wndProcListViewOrig = NULL;
static WNDPROC editControlWndProc = NULL;

#define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL)
#define NHMENU_IS_SELECTED(item) ((item).count!=0)

LRESULT CALLBACK	MenuWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM);
static void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
static LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
static LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam);
static void LayoutMenu(HWND hwnd);
static void SetMenuType(HWND hwnd, int type);
static void SetMenuListType(HWND hwnd, int now);
static HWND GetMenuControl(HWND hwnd);
static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count);
static void reset_menu_count(HWND hwndList, PNHMenuWindow data);
static LRESULT onListChar(HWND hWnd, HWND hwndList, WORD ch);
static char* parse_menu_str(char* dest, const char* src, size_t size);

HWND mswin_init_menu_window (int type) {
	HWND ret;

	ret = CreateDialog(
			GetNHApp()->hApp,
			MAKEINTRESOURCE(IDD_MENU),
			GetNHApp()->hMainWnd,
			MenuWndProc
	);
	if( !ret ) {
		panic("Cannot create menu window");
	}
	
	SetMenuType(ret, type);
	return ret;
}


int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected)
{
	PNHMenuWindow data;
	int ret_val;
    MENU_ITEM_P *selected = NULL;
	int i;
	char* ap;
	char accell_str[256];

	assert( _selected!=NULL );
	*_selected = NULL;
	ret_val = -1;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

	/* set menu type */
	SetMenuListType(hWnd, how);

	/* Ok, now give items a unique accelerators */
	ZeroMemory(accell_str, sizeof(accell_str));
	ap = accell_str;

#if defined(WIN_CE_SMARTPHONE)
	if( data->menu.size>10 ) {
		*ap++ = MENU_FIRST_PAGE;
		*ap++ = MENU_LAST_PAGE;
		*ap++ = MENU_NEXT_PAGE;
		*ap++ = MENU_PREVIOUS_PAGE;
		if( data->how == PICK_ANY ) {
			*ap++ = MENU_SELECT_ALL;
			*ap++ = MENU_UNSELECT_ALL;
			*ap++ = MENU_INVERT_ALL;
			*ap++ = MENU_SELECT_PAGE;
			*ap++ = MENU_UNSELECT_PAGE;
			*ap++ = MENU_INVERT_PAGE;
		}
		*ap++ = MENU_SEARCH;
	}
#endif

	if( data->type == MENU_TYPE_MENU ) {
		char next_char = 'a';

		for( i=0; i<data->menu.size;  i++) {
			if( data->menu.items[i].accelerator!=0 ) {
				*ap++ = data->menu.items[i].accelerator;
				next_char = (char)(data->menu.items[i].accelerator+1);
			} else if( NHMENU_IS_SELECTABLE(data->menu.items[i]) ) {
				if ( (next_char>='a' && next_char<='z') ||
					 (next_char>='A' && next_char<='Z') )  {
					 data->menu.items[i].accelerator = next_char;
					 *ap++ = data->menu.items[i].accelerator;
				} else {
					if( next_char > 'z' ) next_char = 'A';
					else if ( next_char > 'Z' ) break;

					data->menu.items[i].accelerator = next_char;
					*ap++ = data->menu.items[i].accelerator;
				}

				next_char ++;
			}
		}

		/* collect group accelerators */
		data->menu.gacc[0] = '\0';
		ap = data->menu.gacc;
		if( data->how != PICK_NONE ) {
			for( i=0; i<data->menu.size;  i++) {
				if( data->menu.items[i].group_accel && 
					!strchr(data->menu.gacc, data->menu.items[i].group_accel) ) {
					*ap++ = data->menu.items[i].group_accel;
					*ap = '\x0';
				}
			}
		}

		reset_menu_count(NULL, data);
	}

#if defined(WIN_CE_SMARTPHONE)
	if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadFromString( accell_str );
#endif

	mswin_popup_display(hWnd, &data->done);

	/* get the result */
	if( data->result != -1 ) {
		if(how==PICK_NONE) {
			if(data->result>=0) ret_val=0;
			else				ret_val=-1;
		} else if(how==PICK_ONE || how==PICK_ANY) {
			/* count selected items */
			ret_val = 0;
			for(i=0; i<data->menu.size; i++ ) {
				if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
					NHMENU_IS_SELECTED(data->menu.items[i]) ) {
					ret_val++;
				}
			}
			if( ret_val > 0 ) {
				int sel_ind;

				selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P));
				if( !selected ) panic("out of memory");

				sel_ind = 0;
				for(i=0; i<data->menu.size; i++ ) {
					if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
						NHMENU_IS_SELECTED(data->menu.items[i]) ) {
						selected[sel_ind].item = data->menu.items[i].identifier;
						selected[sel_ind].count = data->menu.items[i].count;
						sel_ind++;
					}
				}
				ret_val = sel_ind;
				*_selected = selected;
			}
		}
	}

	mswin_popup_destroy(hWnd);

#if defined(WIN_CE_SMARTPHONE)
	if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadDefault();
#endif

	return ret_val;
}
   
LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PNHMenuWindow data;
	
	CheckInputDialog(hWnd, message, wParam, lParam);	

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	switch (message) 
	{
	case WM_INITDIALOG: {
		HWND text_control;
		HDC hDC;

		text_control = GetDlgItem(hWnd, IDC_MENU_TEXT);

		data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow));
		ZeroMemory(data, sizeof(NHMenuWindow));
		data->type = MENU_TYPE_TEXT;
		data->how = PICK_NONE;
		data->result = 0;
		data->done = 0;
		data->bmpChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL));
		data->bmpCheckedCount = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT));
		data->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL));
		SetWindowLong(hWnd, GWL_USERDATA, (LONG)data);

		/* subclass edit control */
		editControlWndProc = (WNDPROC)GetWindowLong(text_control, GWL_WNDPROC);
		SetWindowLong(text_control, GWL_WNDPROC, (LONG)NHMenuTextWndProc);

		/* set text window font */
		hDC = GetDC(text_control);
		SendMessage(
			text_control, 
			WM_SETFONT, 
			(WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE),
			(LPARAM)0
		);
		ReleaseDC(text_control, hDC);

#if defined(WIN_CE_SMARTPHONE)
		/* special initialization for SmartPhone dialogs */ 
		NHSPhoneDialogSetup(hWnd, FALSE, GetNHApp()->bFullScreen);
#endif
	} break;

	case WM_MSNH_COMMAND:
		onMSNHCommand(hWnd, wParam, lParam);
	break;

	case WM_SIZE:
		LayoutMenu(hWnd);
	return FALSE;

	case WM_COMMAND: 
	{
		switch (LOWORD(wParam)) 
        { 
		case IDCANCEL:
			if( data->type == MENU_TYPE_MENU && 
			    (data->how==PICK_ONE || data->how==PICK_ANY) &&
			    data->menu.counting) {
				HWND list;
				int i;

				/* reset counter if counting is in progress */
				list = GetMenuControl(hWnd);
				i = ListView_GetNextItem(list, -1,	LVNI_FOCUSED);
				if( i>=0 ) {
					SelectMenuItem(list, data, i,  0);
				}
				return FALSE;
			} else {
				data->result = -1;
				data->done = 1;
			}
		return FALSE;

		case IDOK:
			data->done = 1;
			data->result = 0;
		return FALSE;
		}
	} break;

	case WM_NOTIFY:
	{
		LPNMHDR	lpnmhdr = (LPNMHDR)lParam;
		switch (LOWORD(wParam)) {
		case IDC_MENU_LIST:
		{
			if( !data || data->type!=MENU_TYPE_MENU ) break;

			switch(lpnmhdr->code) {
			case LVN_ITEMACTIVATE: 
			{
				LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
				if(data->how==PICK_ONE) {
					if( lpnmlv->iItem>=0 &&
						lpnmlv->iItem<data->menu.size &&
						NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) {
						SelectMenuItem(
							lpnmlv->hdr.hwndFrom, 
							data, 
							lpnmlv->iItem, 
							-1
						);
						data->done = 1;
						data->result = 0;
						return TRUE;
					}
				} else if( data->how==PICK_ANY ) {
					if( lpnmlv->iItem>=0 &&
						lpnmlv->iItem<data->menu.size &&
						NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) {
						SelectMenuItem(
							lpnmlv->hdr.hwndFrom, 
							data, 
							lpnmlv->iItem, 
							NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
						);
					}
				}
			} break;

			case NM_CLICK: {
				LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam;
				if( lpnmlv->iItem==-1 ) return 0;
				if( data->how==PICK_ANY ) {
					SelectMenuItem(
						lpnmlv->hdr.hwndFrom, 
						data, 
						lpnmlv->iItem, 
						NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1
					);
				}
			} break;

			case LVN_ITEMCHANGED: 
			{
				LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam;
				if( lpnmlv->iItem==-1 ) return 0;
				if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0;

				/* update item that has the focus */
				data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
				ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);

				/* update count for single-selection menu (follow the listview selection) */
				if( data->how==PICK_ONE ) {
					if( lpnmlv->uNewState & LVIS_SELECTED ) {
						SelectMenuItem(
							lpnmlv->hdr.hwndFrom, 
							data, 
							lpnmlv->iItem, 
							-1
						);
					}
				}

				/* check item focus */
				data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED);
				ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem);
			} break;

			case NM_KILLFOCUS:
				reset_menu_count(lpnmhdr->hwndFrom, data);
			break;

			}
		} break;
		}
	} break;

	case WM_SETFOCUS:
		if( hWnd!=GetNHApp()->hPopupWnd ) {
			SetFocus(GetNHApp()->hPopupWnd );
			return 0;
		}
	break;
	
    case WM_MEASUREITEM: 
		if( wParam==IDC_MENU_LIST )
			return onMeasureItem(hWnd, wParam, lParam);
		else
			return FALSE;

    case WM_DRAWITEM:
		if( wParam==IDC_MENU_LIST )
			return onDrawItem(hWnd, wParam, lParam);
		else
			return FALSE;

	case WM_CTLCOLORBTN:
	case WM_CTLCOLOREDIT:
	case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */
		HDC hdcEdit = (HDC) wParam; 
		HWND hwndEdit = (HWND) lParam;
		if( hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT) ) {
			SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG));
			SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG)); 
			return (BOOL)mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG);
		}
	} return FALSE;

	case WM_DESTROY:
		if( data ) {
			DeleteObject(data->bmpChecked);
			DeleteObject(data->bmpCheckedCount);
			DeleteObject(data->bmpNotChecked);
			if( data->type == MENU_TYPE_TEXT ) {
				if( data->text.text ) {
					mswin_free_text_buffer(data->text.text);
					data->text.text = NULL;
				}
			}
			free(data);
			SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
		}
		return TRUE;
	}
	return FALSE;
}

void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
#if defined(WIN_CE_POCKETPC)
	PNHMenuWindow data;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

	if( !( data && 
		   data->type==MENU_TYPE_MENU &&
		   (data->how==PICK_ONE || data->how==PICK_ANY) ) ) return;
	
	switch(message) {
	case WM_SETFOCUS:
		if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
	return;

	case WM_DESTROY:
	case WM_KILLFOCUS:
		if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
	return;

	case WM_NOTIFY:
	{
		LPNMHDR	lpnmhdr = (LPNMHDR)lParam;
		switch(lpnmhdr->code) {
		case NM_SETFOCUS:
			if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
		break;
		case NM_KILLFOCUS:
			if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
		break;
		}
	} return;

	case WM_COMMAND:
		switch(HIWORD(wParam)) {
		case BN_SETFOCUS:
			if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP);
		break;
		case BN_KILLFOCUS:
			if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN);
		break;
		}
	return;

	} /* end switch */
#endif
}

void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
	PNHMenuWindow data;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	switch( wParam ) {
	case MSNH_MSG_PUTSTR: 
	{
		PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam;
		HWND   text_view;

		if( data->type!=MENU_TYPE_TEXT )
			SetMenuType(hWnd, MENU_TYPE_TEXT);

		if( !data->text.text ) {
			data->text.text = mswin_init_text_buffer(
						program_state.gameover? FALSE : GetNHApp()->bWrapText
					);
			if( !data->text.text ) break;
		}
		
		mswin_add_text(data->text.text, msg_data->attr, msg_data->text); 
		
		text_view = GetDlgItem(hWnd, IDC_MENU_TEXT);
		if( !text_view ) panic("cannot get text view window");
		mswin_render_text(data->text.text, text_view);
	} break;

	case MSNH_MSG_STARTMENU:
	{
		int i;

		if( data->type!=MENU_TYPE_MENU )
			SetMenuType(hWnd, MENU_TYPE_MENU);

		if( data->menu.items ) free(data->menu.items);
		data->how = PICK_NONE;
		data->menu.items = NULL;
		data->menu.size = 0;
		data->menu.allocated = 0;
		data->done = 0;
		data->result = 0;
		for (i = 0; i < NUMTABS; ++i)
			data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE;
	} break;

	case MSNH_MSG_ADDMENU:
	{
		PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam;
		char *p, *p1;
		int new_item;
		HDC hDC;
		int column;
		HFONT saveFont;
		
		if( data->type!=MENU_TYPE_MENU ) break;
		if( strlen(msg_data->str)==0 ) break;

		if( data->menu.size==data->menu.allocated ) {
			data->menu.allocated += 10;
			data->menu.items = (PNHMenuItem)realloc(data->menu.items, data->menu.allocated*sizeof(NHMenuItem));
		}

		new_item = data->menu.size;
		ZeroMemory( &data->menu.items[new_item], sizeof(data->menu.items[new_item]));
		data->menu.items[new_item].glyph = msg_data->glyph;
		data->menu.items[new_item].identifier = *msg_data->identifier;
		data->menu.items[new_item].accelerator = msg_data->accelerator;
		data->menu.items[new_item].group_accel = msg_data->group_accel;
		data->menu.items[new_item].attr = msg_data->attr;
		parse_menu_str(data->menu.items[new_item].str, msg_data->str, NHMENU_STR_SIZE);
		data->menu.items[new_item].presel = msg_data->presel;

		/* calculate tabstop size */
		p = strchr(data->menu.items[new_item].str, '\t');
		if( p ) {
			data->menu.items[new_item].has_tab = TRUE;
			hDC = GetDC(hWnd);
			saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE));
			p1 = data->menu.items[new_item].str;
			column = 0;
			for (;;) {
				TCHAR wbuf[BUFSZ];
				RECT drawRect;
				SetRect ( &drawRect, 0, 0, 1, 1 );
				if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
				DrawText(hDC,
					NH_A2W(p1, wbuf, BUFSZ),
					strlen(p1),
					&drawRect,
					DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE
				);
				data->menu.tab_stop_size[column] =
					max( data->menu.tab_stop_size[column], drawRect.right - drawRect.left );
				if (p != NULL) *p = '\t';
				else /* last string so, */ break;

				++column;
				p1 = p + 1;
				p = strchr(p1, '\t');
			}
			SelectObject(hDC, saveFont);
			ReleaseDC(hWnd, hDC);
		} else {
			data->menu.items[new_item].has_tab = FALSE;
		}

		/* increment size */
		data->menu.size++;
	} break;

	case MSNH_MSG_ENDMENU:
	{
		PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam;
		if( msg_data->text ) {
			strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 );
		} else {
			ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt));
		}
	} break;

	} /* end switch */
}

void LayoutMenu(HWND hWnd) 
{
	PNHMenuWindow data;
	HWND  menu_ok;
	HWND  menu_cancel;
	RECT  clrt, rt;
	POINT pt_elem, pt_ok, pt_cancel;
	SIZE  sz_elem, sz_ok, sz_cancel;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	menu_ok = GetDlgItem(hWnd, IDOK);
	menu_cancel = GetDlgItem(hWnd, IDCANCEL);

	/* get window coordinates */
	GetClientRect(hWnd, &clrt );
	
	/* set window placements */
	if( IsWindow(menu_ok) ) {
		GetWindowRect(menu_ok, &rt);
		sz_ok.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
		sz_ok.cy = rt.bottom-rt.top;
		pt_ok.x = clrt.left + MENU_MARGIN;
		pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy;
		MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE );
	} else {
		pt_ok.x = 0;
		pt_ok.y = clrt.bottom;
		sz_ok.cx = sz_ok.cy = 0;
	}
	
	if( IsWindow(menu_cancel) ) {
		GetWindowRect(menu_cancel, &rt);
		sz_cancel.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN;
		sz_cancel.cy = rt.bottom-rt.top;
		pt_cancel.x = (clrt.left + clrt.right)/2 + MENU_MARGIN;
		pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy;
		MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE );
	} else {
		pt_cancel.x = 0;
		pt_cancel.y = clrt.bottom;
		sz_cancel.cx = sz_cancel.cy = 0;
	}

	pt_elem.x = clrt.left + MENU_MARGIN;
	pt_elem.y = clrt.top + MENU_MARGIN;
	sz_elem.cx = (clrt.right - clrt.left) - 2*MENU_MARGIN;
	sz_elem.cy = min(pt_cancel.y, pt_ok.y) - MENU_MARGIN - pt_elem.y;
	MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE );

	/* reformat text for the text menu */
	if(	data && 
		data->type==MENU_TYPE_TEXT &&
		data->text.text )
		mswin_render_text(data->text.text, GetMenuControl(hWnd));
}

void SetMenuType(HWND hWnd, int type)
{
	PNHMenuWindow data;
	HWND list, text;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

	data->type = type;
	
	text = GetDlgItem(hWnd, IDC_MENU_TEXT);
	list = GetDlgItem(hWnd, IDC_MENU_LIST);
	if(data->type==MENU_TYPE_TEXT) {
		ShowWindow(list, SW_HIDE);
		EnableWindow(list, FALSE);
		EnableWindow(text, TRUE);
		ShowWindow(text, SW_SHOW);
		SetFocus(text);
	} else {
		ShowWindow(text, SW_HIDE);
		EnableWindow(text, FALSE);
		EnableWindow(list, TRUE);
		ShowWindow(list, SW_SHOW);
		SetFocus(list);
	}
	LayoutMenu(hWnd);
}

void SetMenuListType(HWND hWnd, int how)
{
	PNHMenuWindow data;
	RECT rt;
	DWORD dwStyles;
	char buf[BUFSZ];
	TCHAR wbuf[BUFSZ];
	int nItem;
	int i;
	HWND control;
	LVCOLUMN lvcol;
	LRESULT fnt;
	SIZE wnd_size;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	if( data->type != MENU_TYPE_MENU ) return;

	data->how = how;

	switch(how) {
	case PICK_NONE: 
		dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
			| WS_VSCROLL | WS_HSCROLL | LVS_REPORT
			| LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
		break;
	case PICK_ONE: 
		dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
			| WS_VSCROLL | WS_HSCROLL | LVS_REPORT
			| LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
		break;
	case PICK_ANY: 
		dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD 
			| WS_VSCROLL | WS_HSCROLL | LVS_REPORT
			| LVS_OWNERDRAWFIXED | LVS_SINGLESEL; 
		break;
	default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY");
	};
	if( strlen(data->menu.prompt)==0 ) {
		dwStyles |= LVS_NOCOLUMNHEADER ;
	}

	GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt);
	DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST));
	control = CreateWindow(WC_LISTVIEW, NULL, 
		dwStyles,
		rt.left,
		rt.top,
		rt.right - rt.left,
		rt.bottom - rt.top,
		hWnd,
		(HMENU)IDC_MENU_LIST,
		GetNHApp()->hApp,
		NULL );
	if( !control ) panic( "cannot create menu control" );
	
	/* install the hook for the control window procedure */
	wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC);
	SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc);

	/* set control font */
	fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0);
	SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0);

	/* set control colors */
	ListView_SetBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
	ListView_SetTextBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG));
	ListView_SetTextColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG));

	/* add column to the list view */
	mswin_menu_window_size(hWnd, &wnd_size);

	ZeroMemory(&lvcol, sizeof(lvcol));
	lvcol.mask = LVCF_WIDTH | LVCF_TEXT;
	lvcol.cx = max( wnd_size.cx, GetSystemMetrics(SM_CXSCREEN));
	lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ);
	ListView_InsertColumn(control, 0, &lvcol);

	/* add items to the list view */
	for(i=0; i<data->menu.size; i++ ) {
		LVITEM lvitem;
		ZeroMemory( &lvitem, sizeof(lvitem) );
		sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str );

		lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
		lvitem.iItem = i;
		lvitem.iSubItem = 0;
		lvitem.state = data->menu.items[i].presel? LVIS_SELECTED : 0;
		lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ);
		lvitem.lParam = (LPARAM)&data->menu.items[i];
		nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) buf); 
		if( ListView_InsertItem(control, &lvitem)==-1 ) {
			panic("cannot insert menu item");
		}
	}
	SetFocus(control);
}


HWND GetMenuControl(HWND hWnd)
{
	PNHMenuWindow data;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

	if(data->type==MENU_TYPE_TEXT) {
		return GetDlgItem(hWnd, IDC_MENU_TEXT);
	} else {
		return GetDlgItem(hWnd, IDC_MENU_LIST);
	}
}


LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    LPMEASUREITEMSTRUCT lpmis; 
    TEXTMETRIC tm;
	HGDIOBJ saveFont;
	HDC hdc;
	PNHMenuWindow data;
	RECT list_rect;

    lpmis = (LPMEASUREITEMSTRUCT) lParam; 
	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	GetClientRect(GetMenuControl(hWnd), &list_rect);

	hdc = GetDC(GetMenuControl(hWnd));
	saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE));
	GetTextMetrics(hdc, &tm);

    /* Set the height of the list box items. */
    lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2;
	lpmis->itemWidth = list_rect.right - list_rect.left;

	SelectObject(hdc, saveFont);
	ReleaseDC(GetMenuControl(hWnd), hdc);
	return TRUE;
}

LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    LPDRAWITEMSTRUCT lpdis; 
	PNHMenuItem item;
	PNHMenuWindow data;
    TEXTMETRIC tm;
	HGDIOBJ saveFont;
	HDC tileDC;
	short ntile;
	int t_x, t_y;
	int x, y;
	TCHAR wbuf[BUFSZ];
	RECT drawRect;
	COLORREF OldBg, OldFg, NewBg;
	char *p, *p1;
	int column;

	lpdis = (LPDRAWITEMSTRUCT) lParam; 

    /* If there are no list box items, skip this message. */
    if (lpdis->itemID == -1) return FALSE;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

    item = &data->menu.items[lpdis->itemID];

	tileDC = CreateCompatibleDC(lpdis->hDC);
	saveFont = SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE));
	NewBg = mswin_get_color(NHW_MENU, MSWIN_COLOR_BG);
	OldBg = SetBkColor(lpdis->hDC, NewBg);
	OldFg = SetTextColor(lpdis->hDC, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG)); 

    GetTextMetrics(lpdis->hDC, &tm);

	x = lpdis->rcItem.left + 1;

	/* print check mark if it is a "selectable" menu */
	if( data->how!=PICK_NONE ) {
		if( NHMENU_IS_SELECTABLE(*item) ) {
			HGDIOBJ saveBrush;
			HBRUSH	hbrCheckMark;
			char buf[2];

			switch(item->count) {
			case -1: hbrCheckMark = CreatePatternBrush(data->bmpChecked); break;
			case 0: hbrCheckMark = CreatePatternBrush(data->bmpNotChecked); break;
			default: hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount); break;
			}

			y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; 
			SetBrushOrgEx(lpdis->hDC, x, y, NULL);
			saveBrush = SelectObject(lpdis->hDC, hbrCheckMark);
			PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY);
			SelectObject(lpdis->hDC, saveBrush);
			DeleteObject(hbrCheckMark);

			x += TILE_X + 5;

			if(item->accelerator!=0) {
				buf[0] = item->accelerator;
				buf[1] = '\x0';

				SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom );
				DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
			}
			x += tm.tmAveCharWidth + tm.tmOverhang + 5;
		} else {
			x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10;
		}
	}

	/* print glyph if present */
	if( item->glyph != NO_GLYPH ) {
		HGDIOBJ saveBmp;

		saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles);				
		ntile = glyph2tile[ item->glyph ];
		t_x = (ntile % TILES_PER_LINE)*TILE_X;
		t_y = (ntile / TILES_PER_LINE)*TILE_Y;

		y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; 

		nhapply_image_transparent(
			lpdis->hDC, x, y, TILE_X, TILE_Y, 
			tileDC, t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR );
		SelectObject(tileDC, saveBmp);
	}

	x += TILE_X + 5;

	/* draw item text */
	if( item->has_tab ) {
		p1 = item->str;
		p = strchr(item->str, '\t');
		column = 0;
		SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right),
			lpdis->rcItem.bottom );
		for (;;) {
			TCHAR wbuf[BUFSZ];
			if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
			DrawText(lpdis->hDC,
				NH_A2W(p1, wbuf, BUFSZ),
				strlen(p1),
				&drawRect,
				DT_LEFT | DT_VCENTER | DT_SINGLELINE
			);
			if (p != NULL) *p = '\t';
			else /* last string so, */ break;

			p1 = p + 1;
			p = strchr(p1, '\t');
			drawRect.left = drawRect.right + TAB_SEPARATION;
			++column;
			drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right);
		}
	} else {
		TCHAR wbuf[BUFSZ];
		SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom);
		DrawText(lpdis->hDC,
			NH_A2W(item->str, wbuf, BUFSZ),
			strlen(item->str),
			&drawRect,
			DT_LEFT | DT_VCENTER | DT_SINGLELINE
		);
	}

	/* draw focused item */
	if( item->has_focus ) {
		RECT client_rt;
		HBRUSH bkBrush;

		GetClientRect(lpdis->hwndItem, &client_rt);
		if( NHMENU_IS_SELECTABLE(*item) && 
			data->menu.items[lpdis->itemID].count>0 &&
			item->glyph != NO_GLYPH ) {
			if( data->menu.items[lpdis->itemID].count==-1 ) {
				_stprintf(wbuf, TEXT("Count: All") );
			} else {
				_stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count );
			}

			SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE));

			/* calculate text rectangle */
			SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
			DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, 
					 DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
			
			/* erase text rectangle */
			drawRect.left = max(client_rt.left+1, client_rt.right - (drawRect.right - drawRect.left) - 10);
			drawRect.right = client_rt.right-1;
			drawRect.top = lpdis->rcItem.top;
			drawRect.bottom = lpdis->rcItem.bottom;
			bkBrush = CreateSolidBrush( GetBkColor(lpdis->hDC) );
			FillRect(lpdis->hDC, &drawRect, bkBrush );
			DeleteObject( bkBrush );

			/* draw text */
			DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, 
					 DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX );
		}

		/* draw focus rect */
		SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom );
		DrawFocusRect(lpdis->hDC, &drawRect);
	}

	SetTextColor (lpdis->hDC, OldFg);
	SetBkColor (lpdis->hDC, OldBg);
	SelectObject(lpdis->hDC, saveFont);
	DeleteDC(tileDC);
	return TRUE;
}

BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch)
{
	int i = 0;
	PNHMenuWindow data;
	int curIndex, topIndex, pageSize;
	boolean is_accelerator = FALSE;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);

	switch( ch ) {
	case MENU_FIRST_PAGE:
		i = 0;
		ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
		ListView_EnsureVisible(hwndList, i, FALSE);
	return -2;

	case MENU_LAST_PAGE:
		i = max(0, data->menu.size-1);
		ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
		ListView_EnsureVisible(hwndList, i, FALSE);
	return -2;

	case MENU_NEXT_PAGE:
		topIndex = ListView_GetTopIndex( hwndList );
		pageSize = ListView_GetCountPerPage( hwndList );
        curIndex = ListView_GetNextItem(hwndList, -1,	LVNI_FOCUSED);
        /* Focus down one page */
		i = min(curIndex+pageSize, data->menu.size-1);
		ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
        /* Scrollpos down one page */
        i = min(topIndex+(2*pageSize - 1), data->menu.size-1);
		ListView_EnsureVisible(hwndList, i, FALSE);
	return -2;

	case MENU_PREVIOUS_PAGE:
		topIndex = ListView_GetTopIndex( hwndList );
		pageSize = ListView_GetCountPerPage( hwndList );
        curIndex = ListView_GetNextItem(hwndList, -1,	LVNI_FOCUSED);
        /* Focus up one page */
		i = max(curIndex-pageSize, 0);
		ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
        /* Scrollpos up one page */
		i = max(topIndex-pageSize, 0);
		ListView_EnsureVisible(hwndList, i, FALSE);
	break;

	case MENU_SELECT_ALL:
		if( data->how == PICK_ANY ) {
			reset_menu_count(hwndList, data);
			for(i=0; i<data->menu.size; i++ ) {
				SelectMenuItem(hwndList, data, i, -1);
			}
			return -2;
		}
	break;

	case MENU_UNSELECT_ALL:
		if( data->how == PICK_ANY ) {
			reset_menu_count(hwndList, data);
			for(i=0; i<data->menu.size; i++ ) {
				SelectMenuItem(hwndList, data, i, 0);
			}
			return -2;
		}
	break;

	case MENU_INVERT_ALL:
		if( data->how == PICK_ANY ) {
			reset_menu_count(hwndList, data);
			for(i=0; i<data->menu.size; i++ ) {
				SelectMenuItem(
					hwndList, 
					data, 
					i, 
					NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
				);
			}
			return -2;
		}
	break;

	case MENU_SELECT_PAGE:
		if( data->how == PICK_ANY ) {
			int from, to;
			reset_menu_count(hwndList, data);
			topIndex = ListView_GetTopIndex( hwndList );
			pageSize = ListView_GetCountPerPage( hwndList );
			from = max(0, topIndex);
			to = min(data->menu.size, from+pageSize);
			for(i=from; i<to; i++ ) {
				SelectMenuItem(hwndList, data, i, -1);
			}
			return -2;
		}
	break;

	case MENU_UNSELECT_PAGE:
		if( data->how == PICK_ANY ) {
			int from, to;
			reset_menu_count(hwndList, data);
			topIndex = ListView_GetTopIndex( hwndList );
			pageSize = ListView_GetCountPerPage( hwndList );
			from = max(0, topIndex);
			to = min(data->menu.size, from+pageSize);
			for(i=from; i<to; i++ ) {
				SelectMenuItem(hwndList, data, i, 0);
			}
			return -2;
		}
	break;

	case MENU_INVERT_PAGE:
		if( data->how == PICK_ANY ) {
			int from, to;
			reset_menu_count(hwndList, data);
			topIndex = ListView_GetTopIndex( hwndList );
			pageSize = ListView_GetCountPerPage( hwndList );
			from = max(0, topIndex);
			to = min(data->menu.size, from+pageSize);
			for(i=from; i<to; i++ ) {
				SelectMenuItem(
					hwndList, 
					data, 
					i, 
					NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
				);
			}
			return -2;
		}
	break;

	case MENU_SEARCH:
	    if( data->how==PICK_ANY || data->how==PICK_ONE ) {
			char buf[BUFSZ];
			int  selected_item;
			
			reset_menu_count(hwndList, data);
			mswin_getlin("Search for:", buf);
			if (!*buf || *buf == '\033') return -2;
			selected_item = -1;
			for(i=0; i<data->menu.size; i++ ) {
				if( NHMENU_IS_SELECTABLE(data->menu.items[i])
					&& strstr(data->menu.items[i].str, buf) ) {
					if (data->how == PICK_ANY) {
						SelectMenuItem(
							hwndList, 
							data, 
							i, 
							NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
						);
						/* save the first item - we will move focus to it */
						if( selected_item == -1 ) selected_item = i;
					} else if( data->how == PICK_ONE ) {
						SelectMenuItem(
							hwndList, 
							data, 
							i, 
							-1
						);
						selected_item = i;
						break;
					}
				}
			} 

			if( selected_item>0 ) {
				ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED, LVIS_FOCUSED);
				ListView_EnsureVisible(hwndList, selected_item, FALSE);
			}
		} else {
			mswin_nhbell();
	    }
	return -2;

	case ' ':
		/* ends menu for PICK_ONE/PICK_NONE
		   select item for PICK_ANY */
		if( data->how==PICK_ONE || data->how==PICK_NONE ) {
			data->done = 1;
			data->result = 0;
			return -2;
		} else if( data->how==PICK_ANY ) {
			i = ListView_GetNextItem(hwndList, -1,	LVNI_FOCUSED);
			if( i>=0 ) {
				SelectMenuItem(
					hwndList, 
					data, 
					i, 
					NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
				);
			}
			return -2;
		}
	break;

	default:
		if( strchr(data->menu.gacc, ch) &&
			!(ch=='0' && data->menu.counting) ) {
			/* matched a group accelerator */
			if (data->how == PICK_ANY || data->how == PICK_ONE) {
				reset_menu_count(hwndList, data);
				for(i=0; i<data->menu.size; i++ ) {
					if( NHMENU_IS_SELECTABLE(data->menu.items[i]) &&
						data->menu.items[i].group_accel == ch ) {
						if( data->how == PICK_ANY ) {
							SelectMenuItem(
								hwndList, 
								data, 
								i, 
								NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
							);
						} else if( data->how == PICK_ONE ) {
							SelectMenuItem(
								hwndList, 
								data, 
								i, 
								-1
							);
							data->result = 0;
							data->done = 1;
							return -2;
						}
					}
				}
				return -2;
			} else {
				mswin_nhbell();
				return -2;
			}
		}

		if (isdigit(ch)) {
			int count;
			i = ListView_GetNextItem(hwndList, -1,	LVNI_FOCUSED);
			if( i>=0 ) {
				count = data->menu.items[i].count;
				if( count==-1 ) count=0;
				count *= 10L;
				count += (int)(ch - '0');
				if (count != 0)	/* ignore leading zeros */ {
					data->menu.counting = TRUE;
					data->menu.items[i].count = min(100000, count);
					ListView_RedrawItems( hwndList, i, i ); /* update count mark */
				}
			}
			return -2;
		}

		is_accelerator = FALSE;
		for(i=0; i<data->menu.size; i++) {
			if( data->menu.items[i].accelerator == ch ) {
				is_accelerator = TRUE;
				break;
			}
		}

		if( (ch>='a' && ch<='z') ||
			(ch>='A' && ch<='Z') || is_accelerator) {
			if (data->how == PICK_ANY || data->how == PICK_ONE) {
				for(i=0; i<data->menu.size; i++ ) {
					if( data->menu.items[i].accelerator == ch ) {
						if( data->how == PICK_ANY ) {
							SelectMenuItem(
								hwndList, 
								data, 
								i, 
								NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1
							);
							ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED);
							ListView_EnsureVisible(hwndList, i, FALSE);
							return -2;
						} else if( data->how == PICK_ONE ) {
							SelectMenuItem(
								hwndList, 
								data, 
								i, 
								-1
							);
							data->result = 0;
							data->done = 1;
							return -2;
						}
					}
				}
			}
		}
	break;
	}

	reset_menu_count(hwndList, data);
	return -1;
}

void mswin_menu_window_size (HWND hWnd, LPSIZE sz)
{
    TEXTMETRIC tm;
	HWND control;
	HGDIOBJ saveFont;
	HDC hdc;
	PNHMenuWindow data;
	int i;
	RECT rt, wrt;
	int extra_cx;

	GetClientRect(hWnd, &rt);
	sz->cx = rt.right - rt.left;
	sz->cy = rt.bottom - rt.top;

	GetWindowRect(hWnd, &wrt);
	extra_cx = (wrt.right-wrt.left) - sz->cx;

	data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA);
	if(data) {
		control = GetMenuControl(hWnd);
		hdc = GetDC(control);

		if( data->type==MENU_TYPE_MENU ) {
			/* Calculate the width of the list box. */
			saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE));
			GetTextMetrics(hdc, &tm);
			for(i=0; i<data->menu.size; i++ ) {
				LONG menuitemwidth = 0;
				int column;
				char *p, *p1;

				p1 = data->menu.items[i].str;
				p = strchr(data->menu.items[i].str, '\t');
				column = 0;
				for (;;) {
					TCHAR wbuf[BUFSZ];
					RECT tabRect;
					SetRect ( &tabRect, 0, 0, 1, 1 );
					if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */
					DrawText(hdc,
						NH_A2W(p1, wbuf, BUFSZ),
						strlen(p1),
						&tabRect,
						DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE
					);
					/* it probably isn't necessary to recompute the tab width now, but do so
					 * just in case, honoring the previously computed value
					 */
					menuitemwidth += max(data->menu.tab_stop_size[column],
					    tabRect.right - tabRect.left);
					if (p != NULL) *p = '\t';
					else /* last string so, */ break;
					/* add the separation only when not the last item */
					/* in the last item, we break out of the loop, in the statement just above */
					menuitemwidth += TAB_SEPARATION;
					++column;
					p1 = p + 1;
					p = strchr(p1, '\t');
				}

				sz->cx = max(sz->cx, 
					(LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang));
			}
			SelectObject(hdc, saveFont);
		} else {
			/* do not change size for text output - the text will be formatted to
			   fit any window */
		}
		sz->cx += extra_cx;

		ReleaseDC(control, hdc);
	}
}

void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count)
{
	int i;

	if( item<0 || item>=data->menu.size ) return;

	if( data->how==PICK_ONE && count!=0 ) {
		for(i=0; i<data->menu.size; i++) 
			if( item!=i && data->menu.items[i].count!=0 ) {
				data->menu.items[i].count = 0;
				ListView_RedrawItems( hwndList, i, i );
			};
	}

	data->menu.items[item].count = count;
	ListView_RedrawItems( hwndList, item, item );
	reset_menu_count(hwndList, data);
}

void reset_menu_count(HWND hwndList, PNHMenuWindow data) 
{
	int i; 
	data->menu.counting = FALSE;
	if( IsWindow(hwndList) ) {
		i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED);
		if( i>=0 ) ListView_RedrawItems( hwndList, i, i ); 
	}
}

/* List window Proc */
LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	BOOL bUpdateFocusItem = FALSE;

	switch(message) {

	/* filter keyboard input for the control */
#if !defined(WIN_CE_SMARTPHONE)
	case WM_KEYDOWN:
	case WM_KEYUP: {
		MSG msg;

		if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) {
			if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) {
				return 0;
			}
		}

		if( wParam==VK_LEFT || wParam==VK_RIGHT )
			bUpdateFocusItem = TRUE;
	} break;

#else /* defined(WIN_CE_SMARTPHONE) */
	case WM_KEYDOWN:
		if( wParam==VK_TACTION ) {
			if( onListChar(GetParent(hWnd), hWnd, ' ')==-2 ) {
				return 0;
			}
		} else if( NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE) ) {
			PMSNHEvent	evt;
			BOOL processed = FALSE;
			if( mswin_have_input() ) {
				evt = mswin_input_pop();
				if( evt->type==NHEVENT_CHAR && 
					onListChar(GetParent(hWnd), hWnd, evt->kbd.ch)==-2 ) {
					processed = TRUE;
				}

				/* eat the rest of the events */
				if( mswin_have_input() ) mswin_input_pop();
			}
			if( processed ) return 0;
		}

		if( wParam==VK_LEFT || wParam==VK_RIGHT )
			bUpdateFocusItem = TRUE;
	break;

	case WM_KEYUP:
		/* translate SmartPhone keyboard message */
		if( NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE) )
			return 0;
	break;

	/* tell Windows not to process default button on VK_RETURN */
	case WM_GETDLGCODE: 
		return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS |
			   (wndProcListViewOrig? 
						CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam) 
						: 0 );
#endif

	case WM_SIZE:
	case WM_HSCROLL:
		bUpdateFocusItem = TRUE;
	break;

	}

	if(	bUpdateFocusItem ) {
		int i;
		RECT rt;

		/* invalidate the focus rectangle */
		i = ListView_GetNextItem(hWnd, -1,	LVNI_FOCUSED);
		if( i!=-1 ) {
			ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS);
			InvalidateRect(hWnd, &rt, TRUE);
		}
	}

	if( wndProcListViewOrig ) 
		return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam);
	else 
		return 0;
}

/* Text control window proc - implements close on space */
LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message) {
	case WM_KEYUP:
		switch( wParam ) {
		case VK_SPACE:
		case VK_RETURN:
			/* close on space */
			PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0);
			return 0;
		
		case VK_UP:
			/* scoll up */
			PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL);
			return 0;

		case VK_DOWN:
			/* scoll down */
			PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL);
			return 0;

		case VK_LEFT:
			/* scoll left */
			PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0), (LPARAM)NULL);
			return 0;

		case VK_RIGHT:
			/* scoll right */
			PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0), (LPARAM)NULL);
			return 0;
		}
		break; /* case WM_KEYUP: */
	}

	if( editControlWndProc ) 
		return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam);
	else 
		return 0;
}
/*----------------------------------------------------------------------------*/
char* parse_menu_str(char* dest, const char* src, size_t size)
{
	char *p1, *p2;
	if( !dest || size==0 ) return NULL;

	strncpy(dest, src, size);
	dest[size-1] = '\x0';

	/* replace "[ ]*\[" with "\t\[" */
	p1 = p2 = strstr(dest, " [");
	if( p1 ) {
		while( p1!=dest && *p1==' ') p1--;
		p1++; /* backup to space */
		*p2 = '\t';
		memmove(p1, p2, strlen(p2));
		p1[strlen(p2)] = '\x0';
	}
	return dest;
}
