/* Copyright (C) 2001 by Alex Kompel */ /* NetHack may be freely redistributed. See license for details. */ #include "winMS.h" #include #include #include "resource.h" #include "mhmenu.h" #include "mhmain.h" #include "mhmsg.h" #include "mhfont.h" #define MENU_MARGIN 0 #define NHMENU_STR_SIZE 50 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; } NHMenuItem, *PNHMenuItem; typedef struct mswin_nethack_menu_window { int type; int how; union { struct menu_list { int size; int allocated; PNHMenuItem items; } menu; struct menu_text { int size; char* text; } text; }; int result; int done; HBITMAP bmpChecked; HBITMAP bmpNotChecked; } NHMenuWindow, *PNHMenuWindow; extern short glyph2tile[]; #define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL) LRESULT CALLBACK MenuWndProc(HWND, UINT, WPARAM, 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); DWORD CALLBACK NHTextStreamCallback( DWORD dwCookie, // application-defined value LPBYTE pbBuff, // data buffer LONG cb, // number of bytes to read or write LONG *pcb // number of bytes transferred ); 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) { MSG msg; PNHMenuWindow data; int ret_val; MENU_ITEM_P *selected = NULL; int i; assert( _selected!=NULL ); *_selected = NULL; ret_val = -1; data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); /* Ok, now give items a unique accelerators */ if( data->type == MENU_TYPE_MENU ) { char next_char = 'a'; for( i=0; imenu.size; i++) { if( data->menu.items[i].accelerator!=0 ) { 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; } else { if( next_char > 'z' ) next_char = 'A'; else if ( next_char > 'Z' ) break; data->menu.items[i].accelerator = next_char; } next_char ++; } } } /* activate the menu window */ GetNHApp()->hMenuWnd = hWnd; SetMenuListType(hWnd, how); mswin_layout_main_window(hWnd); EnableWindow(mswin_hwnd_from_winid(WIN_MAP), FALSE); EnableWindow(mswin_hwnd_from_winid(WIN_MESSAGE), FALSE); EnableWindow(mswin_hwnd_from_winid(WIN_STATUS), FALSE); EnableWindow(GetNHApp()->hMainWnd, FALSE); SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); while( IsWindow(hWnd) && !data->done && GetMessage(&msg, NULL, 0, 0)!=0 ) { if( !IsDialogMessage(hWnd, &msg) ) { if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } /* 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) { HWND menu_list; int nItem; menu_list = GetMenuControl(hWnd); nItem = SendMessage(menu_list, LB_GETCURSEL, 0, 0); if(nItem==LB_ERR) { ret_val = 0; } else { assert(nItem>=0 && nItemmenu.size); ret_val = 1; selected = malloc( sizeof(MENU_ITEM_P) ); assert (selected != NULL); selected[0].item = data->menu.items[nItem].identifier; selected[0].count = -1; *_selected = selected; } } else if(how==PICK_ANY) { HWND menu_list; int buffer[255]; int n_sel_items_in_buffer; int i; menu_list = GetMenuControl(hWnd); ret_val = SendMessage(menu_list, LB_GETSELCOUNT, 0, 0); if( ret_val==LB_ERR ) ret_val = 0; if( ret_val > 0 ) { n_sel_items_in_buffer = SendMessage(menu_list, LB_GETSELITEMS, sizeof(buffer)/sizeof(buffer[0]), (LPARAM) buffer); ret_val = n_sel_items_in_buffer; selected = malloc( n_sel_items_in_buffer*sizeof(MENU_ITEM_P) ); assert (selected != NULL); ret_val = 0; for(i=0; i=0 && buffer[i]menu.size); if( NHMENU_IS_SELECTABLE(data->menu.items[buffer[i]]) ) { selected[ret_val].item = data->menu.items[buffer[i]].identifier; selected[ret_val].count = -1; ret_val++; } } *_selected = selected; } } } /* restore window state */ EnableWindow(GetNHApp()->hMainWnd, TRUE); EnableWindow(mswin_hwnd_from_winid(WIN_MAP), TRUE); EnableWindow(mswin_hwnd_from_winid(WIN_MESSAGE), TRUE); EnableWindow(mswin_hwnd_from_winid(WIN_STATUS), TRUE); SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); GetNHApp()->hMenuWnd = NULL; mswin_window_mark_dead( mswin_winid_from_handle(hWnd) ); DestroyWindow(hWnd); mswin_layout_main_window(hWnd); SetFocus(GetTopWindow(GetNHApp()->hMainWnd)); return ret_val; } LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PNHMenuWindow data; int nItem; HDC hdc; data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); switch (message) { case WM_INITDIALOG: 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->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL)); SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); /* set text control font & the rest*/ if( data->type==MENU_TYPE_MENU ) { hdc = GetDC(GetDlgItem(hWnd, IDC_MENU_TEXT)); SendDlgItemMessage(hWnd, IDC_MENU_TEXT, WM_SETFONT, (WPARAM)mswin_create_font(NHW_MENU, ATR_NONE, hdc), 0); ReleaseDC(GetDlgItem(hWnd, IDC_MENU_TEXT), hdc); SetFocus(GetMenuControl(hWnd)); } else { SetFocus(GetDlgItem(hWnd, IDOK)); } return FALSE; 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: data->result = -1; data->done = 1; return TRUE; case IDOK: data->done = 1; data->result = 0; return TRUE; case IDC_MENU_LIST: { if( !data || data->type!=MENU_TYPE_MENU ) break; switch(HIWORD(wParam)) { case LBN_DBLCLK: if(data->how==PICK_ONE) { nItem = SendMessage((HWND)lParam, LB_GETCURSEL, (WPARAM)0, (LPARAM)0); if( nItem!=LB_ERR && nItem>0 && nItemmenu.size && data->menu.items[nItem].accelerator!=0 ) { data->done = 1; data->result = 0; return TRUE; } } break; } } break; } } break; case WM_SETFOCUS: if( hWnd!=GetNHApp()->hMenuWnd ) { SetFocus(GetNHApp()->hMainWnd ); } break; case WM_MEASUREITEM: return onMeasureItem(hWnd, wParam, lParam); case WM_DRAWITEM: return onDrawItem(hWnd, wParam, lParam); case WM_VKEYTOITEM: { WORD c[4]; BYTE kbd_state[256]; ZeroMemory(kbd_state, sizeof(kbd_state)); ZeroMemory(c, sizeof(c)); GetKeyboardState(kbd_state); if( ToAscii( LOWORD(wParam), 0, kbd_state, c, 0)==1 ) { int i = 0; for(i=0; imenu.size; i++ ) { if( data->menu.items[i].accelerator == c[0] ) { if( data->how == PICK_ANY ) { SendMessage((HWND)lParam, LB_SETSEL, (WPARAM)!SendMessage((HWND)lParam, LB_GETSEL, (WPARAM)i, (LPARAM)0), (LPARAM)i); return -2; } else if( data->how == PICK_ONE ) { SendMessage((HWND)lParam, LB_SETCURSEL, (WPARAM)i, (LPARAM)0); data->result = 0; data->done = 1; return -2; } } } } } return -1; case WM_DESTROY: DeleteObject(data->bmpChecked); DeleteObject(data->bmpNotChecked); free(data); SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); return TRUE; } return FALSE; } 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; EDITSTREAM txt_edit; CHARFORMAT txt_format; if( data->type!=MENU_TYPE_TEXT ) SetMenuType(hWnd, MENU_TYPE_TEXT); text_view = GetDlgItem(hWnd, IDC_MENU_TEXT); if( !text_view ) panic("cannot get text view window"); /* apply text format to the selection */ ZeroMemory(&txt_format, sizeof(txt_format)); txt_format.cbSize = sizeof(txt_format); txt_format.dwMask = CFM_BOLD | CFM_ITALIC | CFM_STRIKEOUT; txt_format.dwEffects = ((msg_data->attr==ATR_BOLD || msg_data->attr==ATR_INVERSE)? CFE_BOLD : 0) | ((msg_data->attr==ATR_BLINK)? CFE_ITALIC : 0) | ((msg_data->attr==ATR_ULINE)? CFE_STRIKEOUT : 0) ; /* txt_format.yHeight; */ /* txt_format.yOffset; */ /* txt_format.crTextColor; */ /* txt_format.bCharSet; */ /* txt_format.bPitchAndFamily = FIXED_PITCH; */ /* txt_format.szFaceName[LF_FACESIZE]; */ SendMessage(text_view, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&txt_format); /* inject text into control (see NHTextStreamCallback) */ ZeroMemory(&txt_edit, sizeof(txt_edit)); txt_edit.dwCookie = (DWORD)msg_data->text; txt_edit.pfnCallback = NHTextStreamCallback; SendMessage(text_view, EM_STREAMIN, SF_TEXT | SFF_SELECTION, (LPARAM)&txt_edit); } break; case MSNH_MSG_STARTMENU: 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; break; case MSNH_MSG_ADDMENU: { PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam; 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)); } data->menu.items[data->menu.size].glyph = msg_data->glyph; data->menu.items[data->menu.size].identifier = *msg_data->identifier; data->menu.items[data->menu.size].accelerator = msg_data->accelerator; data->menu.items[data->menu.size].group_accel = msg_data->group_accel; data->menu.items[data->menu.size].attr = msg_data->attr; strncpy(data->menu.items[data->menu.size].str, msg_data->str, NHMENU_STR_SIZE); data->menu.items[data->menu.size].presel = msg_data->presel; data->menu.size++; } break; } } void LayoutMenu(HWND hWnd) { PNHMenuWindow data; // HWND menu_list; // HWND menu_text; 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_list = GetDlgItem(hWnd, IDC_MENU_LIST); // menu_text = GetDlgItem(hWnd, IDC_MENU_TEXT); menu_ok = GetDlgItem(hWnd, IDOK); menu_cancel = GetDlgItem(hWnd, IDCANCEL); /* get window coordinates */ GetClientRect(hWnd, &clrt ); /* set window placements */ 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; 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; 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) - 2*MENU_MARGIN; MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE ); MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE ); } void SetMenuType(HWND hWnd, int type) { PNHMenuWindow data; HWND list, text; data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); // if( type == data->type ) return; 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); } else { ShowWindow(text, SW_HIDE); EnableWindow(text, FALSE); EnableWindow(list, TRUE); ShowWindow(list, SW_SHOW); } LayoutMenu(hWnd); } void SetMenuListType(HWND hWnd, int how) { PNHMenuWindow data; RECT rt; DWORD dwStyles; char buf[255]; int nItem; int i; HWND control; LRESULT fnt; 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 | LBS_WANTKEYBOARDINPUT | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | LBS_NOSEL | LBS_OWNERDRAWFIXED; break; case PICK_ONE: dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL | WS_HSCROLL | LBS_WANTKEYBOARDINPUT | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED; break; case PICK_ANY: dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VSCROLL | WS_HSCROLL | LBS_WANTKEYBOARDINPUT | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | LBS_MULTIPLESEL | LBS_OWNERDRAWFIXED; break; default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY"); }; GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt); DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST)); control = CreateWindow( TEXT("LISTBOX"), /* registered class name */ NULL, /* window name */ dwStyles, /* window style */ rt.left, /* horizontal position of window */ rt.top, /* vertical position of window */ rt.right - rt.left, /* window width */ rt.bottom - rt.top, /* window height */ hWnd, /* handle to parent or owner window */ (HMENU)IDC_MENU_LIST, /* menu handle or child identifier */ GetNHApp()->hApp, /* handle to application instance */ NULL ); /* window-creation data */ fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0); SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0); for(i=0; imenu.size; i++ ) { TCHAR wbuf[255]; sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str ); nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) NH_A2W(buf, wbuf, sizeof(wbuf))); if( data->menu.items[i].presel ) { if( data->how==PICK_ONE ) { nItem = SendMessage(control, LB_SETCURSEL, (WPARAM)nItem, (LPARAM)0); } else if( data->how==PICK_ANY ) { nItem = SendMessage(control, LB_SETSEL, (WPARAM)TRUE, (LPARAM)nItem); } } } 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 client_rect; lpmis = (LPMEASUREITEMSTRUCT) lParam; data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); hdc = GetDC(GetMenuControl(hWnd)); saveFont = SelectObject(hdc, mswin_create_font(NHW_MENU, ATR_INVERSE, hdc)); GetTextMetrics(hdc, &tm); /* Set the height of the list box items. */ lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2; /* Set windth of list box items */ GetClientRect(hWnd, &client_rect); lpmis->itemWidth = client_rect.right - client_rect.left; mswin_destroy_font(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 saveBmp; HGDIOBJ saveFont; HGDIOBJ savePen; HPEN pen; HDC tileDC; short ntile; int t_x, t_y; int x, y; TCHAR wbuf[255]; 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); switch (lpdis->itemAction) { case ODA_SELECT: case ODA_DRAWENTIRE: item = &data->menu.items[lpdis->itemID]; tileDC = CreateCompatibleDC(lpdis->hDC); saveFont = SelectObject(lpdis->hDC, mswin_create_font(NHW_MENU, item->attr, lpdis->hDC)); GetTextMetrics(lpdis->hDC, &tm); x = lpdis->rcItem.left; /* print check mark */ if( NHMENU_IS_SELECTABLE(*item) ) { char buf[2]; saveBmp = SelectObject(tileDC, (lpdis->itemState & ODS_SELECTED)? data->bmpChecked: data->bmpNotChecked ); y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; BitBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, tileDC, 0, 0, SRCCOPY ); x += TILE_X + 5; if(item->accelerator!=0) { y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; buf[0] = item->accelerator; buf[1] = '\x0'; TextOut(lpdis->hDC, x, y, NH_A2W(buf, wbuf, sizeof(wbuf)), 1); } x += tm.tmAveCharWidth + 5; } 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; BitBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, tileDC, t_x, t_y, SRCCOPY ); x += TILE_X + 5; y = (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2; TextOut(lpdis->hDC, x, y, NH_A2W(item->str, wbuf, sizeof(wbuf)), strlen(item->str)); mswin_destroy_font(SelectObject(lpdis->hDC, saveFont)); SelectObject(tileDC, saveBmp); DeleteDC(tileDC); break; case ODA_FOCUS: if( data->how==PICK_NONE ) break; if( lpdis->itemState & ODS_FOCUS ) pen = CreatePen(PS_DOT, 0, RGB(0,0,0)); else pen = CreatePen(PS_DOT, 0, GetBkColor(lpdis->hDC)); savePen = SelectObject(lpdis->hDC, pen); MoveToEx(lpdis->hDC,lpdis->rcItem.left, lpdis->rcItem.top, NULL); LineTo(lpdis->hDC, lpdis->rcItem.right-1, lpdis->rcItem.top); LineTo(lpdis->hDC, lpdis->rcItem.right-1, lpdis->rcItem.bottom-1); LineTo(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.bottom-1); LineTo(lpdis->hDC, lpdis->rcItem.left, lpdis->rcItem.top); DeleteObject(SelectObject(lpdis->hDC, savePen)); break; } return TRUE; } void mswin_menu_window_size (HWND hWnd, LPSIZE sz) { TEXTMETRIC tm; HGDIOBJ saveFont; HDC hdc; PNHMenuWindow data; int i; RECT rt; GetWindowRect(hWnd, &rt); sz->cx = rt.right - rt.left; sz->cy = rt.bottom - rt.top; data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); if(data) { hdc = GetDC(GetMenuControl(hWnd)); saveFont = SelectObject(hdc, mswin_create_font(NHW_MENU, ATR_INVERSE, hdc)); GetTextMetrics(hdc, &tm); /* Set the height of the list box items. */ for(i=0; imenu.size; i++ ) { sz->cx = max(sz->cx, (LONG)(2*TILE_X + tm.tmAveCharWidth*(strlen(data->menu.items[i].str)+1))); } mswin_destroy_font(SelectObject(hdc, saveFont)); ReleaseDC(GetMenuControl(hWnd), hdc); } } DWORD CALLBACK NHTextStreamCallback( DWORD dwCookie, // application-defined value LPBYTE pbBuff, // data buffer LONG cb, // number of bytes to read or write LONG *pcb // number of bytes transferred ) { static int st = 0; static char* _text_buf = 0; size_t to_copy; switch(st) { case 0: _text_buf = (char*)dwCookie; st = 1; /* fall through */ case 1: to_copy = min((size_t)cb, strlen(_text_buf)); if( *pcb>0 ) { strncpy((char*)pbBuff, _text_buf, to_copy); _text_buf += to_copy; *pcb = (LONG)to_copy; } if( !_text_buf[0] ) st = 3; break; case 3: *pcb = 2; strncpy( (char*)pbBuff, "\r\n", *pcb); st = 4; break; case 4: *pcb = 0; st = 0; break; } return 0; }