/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS user32.dll * FILE: win32ss/user/user32/windows/menu.c * PURPOSE: Menus * * PROGRAMMERS: Casper S. Hornstrup * James Tabor */ #include BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj); WINE_DEFAULT_DEBUG_CHANNEL(menu); /* internal popup menu window messages */ #define MM_SETMENUHANDLE (WM_USER + 0) #define MM_GETMENUHANDLE (WM_USER + 1) #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR) #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK) #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP) #define MENUITEMINFO_TYPE_MASK \ (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \ MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \ MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ ) #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU) #define STATE_MASK (~TYPE_MASK) #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT)) #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT) /* macro to test that flags do not indicate bitmap, ownerdraw or separator */ #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags)) #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1)) #define IS_SYSTEM_MENU(MenuInfo) \ (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU)) #define IS_SYSTEM_POPUP(MenuInfo) \ (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU)) #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags)) /********************************************************************* * PopupMenu class descriptor */ const struct builtin_class_descr POPUPMENU_builtin_class = { WC_MENU, /* name */ CS_SAVEBITS | CS_DBLCLKS, /* style */ NULL, /* FIXME - procA */ PopupMenuWndProcW, /* FIXME - procW */ sizeof(MENUINFO *), /* extra */ (LPCWSTR) IDC_ARROW, /* cursor */ (HBRUSH)(COLOR_MENU + 1) /* brush */ }; #ifndef GET_WORD #define GET_WORD(ptr) (*(WORD *)(ptr)) #endif #ifndef GET_DWORD #define GET_DWORD(ptr) (*(DWORD *)(ptr)) #endif /*********************************************************************** * MENU_GetMenu * * Validate the given menu handle and returns the menu structure pointer. */ FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu) { return ValidateHandleNoErr(hMenu, TYPE_MENU); } /*********************************************************************** * MENU_FindItem * * Find a menu item. Return a pointer on the item, and modifies *hmenu * in case the item was in a sub-menu. */ ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags ) { MENU *menu; ITEM *fallback = NULL; UINT fallback_pos = 0; UINT i; PITEM pItem; if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL; if (wFlags & MF_BYPOSITION) { if (*nPos >= menu->cItems) return NULL; pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; if (pItem) pItem = &pItem[*nPos]; return pItem; } else { PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; for (i = 0; item && (i < menu->cItems); i++, item++) { if (item->spSubMenu) { PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu); HMENU hsubmenu = UserHMGetHandle(pSubMenu); ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags ); if (subitem) { *hmenu = hsubmenu; return subitem; } else if (item->wID == *nPos) { /* fallback to this item if nothing else found */ fallback_pos = i; fallback = item; } } else if (item->wID == *nPos) { *nPos = i; return item; } } } if (fallback) *nPos = fallback_pos; return fallback; } UINT FASTCALL IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc) { UINT i = 0; PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL; /* empty menu */ if (!Item) return -1; while ( !( Item->fState & MFS_DEFAULT ) ) { i++; Item++; if (i >= Menu->cItems ) return -1; } /* default: don't return disabled items */ if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (Item->fState & MFS_DISABLED )) return -1; /* search rekursiv when needed */ if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && Item->spSubMenu ) { UINT ret; (*gismc)++; ret = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc ); (*gismc)--; if ( -1 != ret ) return ret; /* when item not found in submenu, return the popup item */ } return ( fByPos ) ? i : Item->wID; } static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos, LPMENUITEMINFOW lpmii, BOOL unicode) { ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0); //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, ""); if (!pItem) { SetLastError( ERROR_MENU_ITEM_NOT_FOUND); return FALSE; } if( lpmii->fMask & MIIM_TYPE) { if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) { ERR("invalid combination of fMask bits used\n"); /* this does not happen on Win9x/ME */ SetLastError( ERROR_INVALID_PARAMETER); return FALSE; } lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK; if (pItem->hbmp && !IS_MAGIC_BITMAP(pItem->hbmp)) lpmii->fType |= MFT_BITMAP; lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */ if( lpmii->fType & MFT_BITMAP) { lpmii->dwTypeData = (LPWSTR) pItem->hbmp; lpmii->cch = 0; } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) { /* this does not happen on Win9x/ME */ lpmii->dwTypeData = 0; lpmii->cch = 0; } } /* copy the text string */ if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) { if( !pItem->Xlpstr ) { // Very strange this fixes a wine test with a crash. if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) ) { if( unicode) *((WCHAR *)lpmii->dwTypeData) = 0; else *((CHAR *)lpmii->dwTypeData) = 0; } lpmii->cch = 0; } else { int len; LPWSTR text = DesktopPtrToUser(pItem->Xlpstr); if (unicode) { len = strlenW(text); if(lpmii->dwTypeData && lpmii->cch) lstrcpynW(lpmii->dwTypeData, text, lpmii->cch); } else { len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1; if(lpmii->dwTypeData && lpmii->cch) if (!WideCharToMultiByte( CP_ACP, 0, text, -1, (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL )) ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0; } /* if we've copied a substring we return its length */ if(lpmii->dwTypeData && lpmii->cch) if (lpmii->cch <= len + 1) lpmii->cch--; else lpmii->cch = len; else { /* return length of string */ /* not on Win9x/ME if fType & MFT_BITMAP */ lpmii->cch = len; } } } if (lpmii->fMask & MIIM_FTYPE) lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK; if (lpmii->fMask & MIIM_BITMAP) lpmii->hbmpItem = pItem->hbmp; if (lpmii->fMask & MIIM_STATE) lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK; if (lpmii->fMask & MIIM_ID) lpmii->wID = pItem->wID; if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu ) { PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); HMENU hSubMenu = UserHMGetHandle(pSubMenu); lpmii->hSubMenu = hSubMenu; } else { /* hSubMenu is always cleared * (not on Win9x/ME ) */ lpmii->hSubMenu = 0; } if (lpmii->fMask & MIIM_CHECKMARKS) { lpmii->hbmpChecked = pItem->hbmpChecked; lpmii->hbmpUnchecked = pItem->hbmpUnchecked; } if (lpmii->fMask & MIIM_DATA) lpmii->dwItemData = pItem->dwItemData; return TRUE; } // // User side Menu Class Proc. // LRESULT WINAPI PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) { LRESULT lResult; PWND pWnd; TRACE("PMWPW : hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); pWnd = ValidateHwnd(Wnd); if (pWnd) { if (!pWnd->fnid) { if (Message != WM_NCCREATE) { return DefWindowProcW(Wnd, Message, wParam, lParam); } } else { if (pWnd->fnid != FNID_MENU) { ERR("Wrong window class for Menu!\n"); return 0; } } } switch(Message) { case WM_DESTROY: case WM_NCDESTROY: case WM_NCCREATE: case WM_CREATE: case MM_SETMENUHANDLE: case MM_GETMENUHANDLE: case MN_SETHMENU: case MN_GETHMENU: case WM_PAINT: case WM_PRINTCLIENT: { TRACE("Menu Class ProcW\n"); NtUserMessageCall( Wnd, Message, wParam, lParam, (ULONG_PTR)&lResult, FNID_MENU, FALSE); return lResult; } case WM_MOUSEACTIVATE: /* We don't want to be activated */ return MA_NOACTIVATE; case WM_ERASEBKGND: return 1; case WM_SHOWWINDOW: // Not sure what this does.... if (0 != wParam) { if (0 == GetWindowLongPtrW(Wnd, 0)) { OutputDebugStringA("no menu to display\n"); } } else { //SetWindowLongPtrW(Wnd, 0, 0); } break; default: return DefWindowProcW(Wnd, Message, wParam, lParam); } return 0; } LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) { PWND pWnd; pWnd = ValidateHwnd(Wnd); if (pWnd && !pWnd->fnid && Message != WM_NCCREATE) { return DefWindowProcA(Wnd, Message, wParam, lParam); } TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); switch(Message) { case WM_NCCREATE: case WM_CREATE: case WM_MOUSEACTIVATE: case WM_PAINT: case WM_PRINTCLIENT: case WM_ERASEBKGND: case WM_DESTROY: case WM_NCDESTROY: case WM_SHOWWINDOW: case MM_SETMENUHANDLE: case MM_GETMENUHANDLE: case MN_SETHMENU: case MN_GETHMENU: return PopupMenuWndProcW(Wnd, Message, wParam, lParam); default: return DefWindowProcA(Wnd, Message, wParam, lParam); } return 0; } /********************************************************************** * MENU_ParseResource * * Parse a standard menu resource and add items to the menu. * Return a pointer to the end of the resource. * * NOTE: flags is equivalent to the mtOption field */ static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu) { WORD flags, id = 0; HMENU hSubMenu; LPCWSTR str; BOOL end = FALSE; do { flags = GET_WORD(res); /* remove MF_END flag before passing it to AppendMenu()! */ end = (flags & MF_END); if(end) flags ^= MF_END; res += sizeof(WORD); if(!(flags & MF_POPUP)) { id = GET_WORD(res); res += sizeof(WORD); } str = (LPCWSTR)res; res += (strlenW(str) + 1) * sizeof(WCHAR); if (flags & MF_POPUP) { hSubMenu = CreatePopupMenu(); if(!hSubMenu) return NULL; if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL; AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str); } else /* Not a popup */ { AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL); } } while(!end); return res; } /********************************************************************** * MENUEX_ParseResource * * Parse an extended menu resource and add items to the menu. * Return a pointer to the end of the resource. */ static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu) { WORD resinfo; do { MENUITEMINFOW mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE; mii.fType = GET_DWORD(res); res += sizeof(DWORD); mii.fState = GET_DWORD(res); res += sizeof(DWORD); mii.wID = GET_DWORD(res); res += sizeof(DWORD); resinfo = GET_WORD(res); res += sizeof(WORD); /* Align the text on a word boundary. */ res += (~((UINT_PTR)res - 1)) & 1; mii.dwTypeData = (LPWSTR)res; mii.cch = strlenW(mii.dwTypeData); res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR); /* Align the following fields on a dword boundary. */ res += (~((UINT_PTR)res - 1)) & 3; TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n", mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData); if (resinfo & 1) /* Pop-up? */ { /* DWORD helpid = GET_DWORD(res); FIXME: use this. */ res += sizeof(DWORD); mii.hSubMenu = CreatePopupMenu(); if (!mii.hSubMenu) { ERR("CreatePopupMenu failed\n"); return NULL; } if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) { ERR("MENUEX_ParseResource failed\n"); DestroyMenu(mii.hSubMenu); return NULL; } mii.fMask |= MIIM_SUBMENU; mii.fType |= MF_POPUP; } else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR)) { WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n", mii.wID, mii.fType); mii.fType |= MF_SEPARATOR; } InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii); } while (!(resinfo & MF_END)); return res; } /********************************************************************** * MENU_mnu2mnuii * * Uses flags, id and text ptr, passed by InsertMenu() and * ModifyMenu() to setup a MenuItemInfo structure. */ static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode) { RtlZeroMemory( pmii, sizeof( MENUITEMINFOW)); pmii->cbSize = sizeof( MENUITEMINFOW); pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE; /* setting bitmap clears text and vice versa */ if( IS_STRING_ITEM(flags)) { pmii->fMask |= MIIM_STRING | MIIM_BITMAP; if( !str) flags |= MF_SEPARATOR; /* Item beginning with a backspace is a help item */ /* FIXME: wrong place, this is only true in win16 */ else { if (Unicode) { if (*str == '\b') { flags |= MF_HELP; str++; } } else { LPCSTR NewItemA = (LPCSTR) str; if (*NewItemA == '\b') { flags |= MF_HELP; NewItemA++; str = (LPCWSTR) NewItemA; } TRACE("A cch %d\n",strlen(NewItemA)); } } pmii->dwTypeData = (LPWSTR)str; } else if( flags & MFT_BITMAP){ pmii->fMask |= MIIM_BITMAP | MIIM_STRING; pmii->hbmpItem = (HBITMAP)str; } if( flags & MF_OWNERDRAW){ pmii->fMask |= MIIM_DATA; pmii->dwItemData = (ULONG_PTR) str; } if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) { pmii->fMask |= MIIM_SUBMENU; pmii->hSubMenu = (HMENU)id; } if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED; pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT; pmii->fType = flags & MENUITEMINFO_TYPE_MASK; pmii->wID = (UINT)id; } /********************************************************************** * MENU_NormalizeMenuItemInfoStruct * * Helper for SetMenuItemInfo and InsertMenuItemInfo: * check, copy and extend the MENUITEMINFO struct from the version that the application * supplied to the version used by wine source. */ static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in, MENUITEMINFOW *pmii_out ) { /* do we recognize the size? */ if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) && pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) { SetLastError( ERROR_INVALID_PARAMETER); return FALSE; } /* copy the fields that we have */ memcpy( pmii_out, pmii_in, pmii_in->cbSize); /* if the hbmpItem member is missing then extend */ if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) { pmii_out->cbSize = sizeof( MENUITEMINFOW); pmii_out->hbmpItem = NULL; } /* test for invalid bit combinations */ if( (pmii_out->fMask & MIIM_TYPE && pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) || (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) { ERR("invalid combination of fMask bits used\n"); /* this does not happen on Win9x/ME */ SetLastError( ERROR_INVALID_PARAMETER); return FALSE; } /* convert old style (MIIM_TYPE) to the new and keep the old one too */ if( pmii_out->fMask & MIIM_TYPE){ pmii_out->fMask |= MIIM_FTYPE; if( IS_STRING_ITEM(pmii_out->fType)){ pmii_out->fMask |= MIIM_STRING; } else if( (pmii_out->fType) & MFT_BITMAP){ pmii_out->fMask |= MIIM_BITMAP; pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData)); } } if (pmii_out->fMask & MIIM_FTYPE ) { pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK; pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK; } if (pmii_out->fMask & MIIM_STATE) /* Other menu items having MFS_DEFAULT are not converted to normal items */ pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK; if (pmii_out->fMask & MIIM_SUBMENU) { if ((pmii_out->hSubMenu != NULL) && !IsMenu(pmii_out->hSubMenu)) return FALSE; } return TRUE; } BOOL MenuInit(VOID) { return TRUE; } VOID MenuCleanup(VOID) { } NTSTATUS WINAPI User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength) { LRESULT Result = 0; // Use this for Menu Ole!! return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS)); } NTSTATUS WINAPI User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength) { PLOADMENU_CALLBACK_ARGUMENTS Common; LRESULT Result; Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments; Result = (LRESULT)LoadMenuW( Common->hModule, Common->InterSource ? MAKEINTRESOURCE(Common->InterSource) : (LPCWSTR)&Common->MenuName); return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS); } /* FUNCTIONS *****************************************************************/ /* * @implemented */ BOOL WINAPI AppendMenuA(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING UnicodeString; BOOL res; RtlInitUnicodeString(&UnicodeString, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; } mii.dwTypeData = UnicodeString.Buffer; mii.cch = UnicodeString.Length / sizeof(WCHAR); } else { TRACE("AMA Handle bitmaps\n"); } ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append? res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString); if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); return res; } /* * @implemented */ BOOL WINAPI AppendMenuW(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCWSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING MenuText; BOOL res; RtlInitUnicodeString(&MenuText, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); mii.dwTypeData = MenuText.Buffer; mii.cch = MenuText.Length / sizeof(WCHAR); } res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText); return res; } /* * @implemented */ DWORD WINAPI CheckMenuItem(HMENU hmenu, UINT uIDCheckItem, UINT uCheck) { PITEM item; DWORD Ret; UINT uID = uIDCheckItem; if (!ValidateHandle(hmenu, TYPE_MENU)) return -1; if (!(item = MENU_FindItem( &hmenu, &uID, uCheck ))) return -1; Ret = item->fState & MFS_CHECKED; if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked... return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck); } /* * @implemented */ BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last, UINT check, UINT bypos) { BOOL done = FALSE; UINT i; PITEM mi_first = NULL, mi_check; HMENU m_first, m_check; MENUITEMINFOW mii; mii.cbSize = sizeof( mii); for (i = first; i <= last; i++) { UINT pos = i; if (!mi_first) { m_first = hMenu; mi_first = MENU_FindItem(&m_first, &pos, bypos); if (!mi_first) continue; mi_check = mi_first; m_check = m_first; } else { m_check = hMenu; mi_check = MENU_FindItem(&m_check, &pos, bypos); if (!mi_check) continue; } if (m_first != m_check) continue; if (mi_check->fType == MFT_SEPARATOR) continue; if (i == check) { if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED)) { mii.fMask = MIIM_FTYPE | MIIM_STATE; mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK; mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED; NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL); } done = TRUE; } else { /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */ if (mi_check->fState & MFS_CHECKED) { mii.fMask = MIIM_STATE; mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED; NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL); } } } return done; } /* * @implemented */ HMENU WINAPI CreateMenu(VOID) { return NtUserxCreateMenu(); } /* * @implemented */ HMENU WINAPI CreatePopupMenu(VOID) { return NtUserxCreatePopupMenu(); } /* * @implemented */ BOOL WINAPI DrawMenuBar(HWND hWnd) { return NtUserxDrawMenuBar(hWnd); } /* * @implemented */ BOOL WINAPI EnableMenuItem(HMENU hMenu, UINT uIDEnableItem, UINT uEnable) { return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable); } /* * @implemented */ HMENU WINAPI GetMenu(HWND hWnd) { PWND Wnd = ValidateHwnd(hWnd); if (!Wnd) return NULL; return UlongToHandle(Wnd->IDMenu); } /* * @implemented */ LONG WINAPI GetMenuCheckMarkDimensions(VOID) { return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK))); } /* * @implemented */ DWORD WINAPI GetMenuContextHelpId(HMENU hmenu) { PMENU pMenu; if ((pMenu = ValidateHandle(hmenu, TYPE_MENU))) return pMenu->dwContextHelpId; return 0; } /* * @implemented */ UINT WINAPI GetMenuDefaultItem(HMENU hMenu, UINT fByPos, UINT gmdiFlags) { PMENU pMenu; DWORD gismc = 0; if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU))) return (UINT)-1; return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc); } /* * @implemented */ BOOL WINAPI GetMenuInfo(HMENU hmenu, LPMENUINFO lpcmi) { PMENU pMenu; if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU))) return FALSE; if (lpcmi->fMask & MIM_BACKGROUND) lpcmi->hbrBack = pMenu->hbrBack; if (lpcmi->fMask & MIM_HELPID) lpcmi->dwContextHelpID = pMenu->dwContextHelpId; if (lpcmi->fMask & MIM_MAXHEIGHT) lpcmi->cyMax = pMenu->cyMax; if (lpcmi->fMask & MIM_MENUDATA) lpcmi->dwMenuData = pMenu->dwMenuData; if (lpcmi->fMask & MIM_STYLE) lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK; return TRUE; } /* * @implemented */ int WINAPI GetMenuItemCount(HMENU hmenu) { PMENU pMenu; if ((pMenu = ValidateHandle(hmenu, TYPE_MENU))) return pMenu->cItems; return -1; } /* * @implemented */ UINT WINAPI GetMenuItemID(HMENU hMenu, int nPos) { ITEM * lpmi; if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1; if (lpmi->spSubMenu) return -1; return lpmi->wID; } /* * @implemented */ BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos, LPMENUITEMINFOA lpmii) { BOOL ret; MENUITEMINFOA mii; if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { SetLastError( ERROR_INVALID_PARAMETER); return FALSE; } memcpy( &mii, lpmii, lpmii->cbSize); mii.cbSize = sizeof( mii); ret = GetMenuItemInfo_common (hmenu, item, bypos, (LPMENUITEMINFOW)&mii, FALSE); mii.cbSize = lpmii->cbSize; memcpy( lpmii, &mii, mii.cbSize); return ret; } /* * @implemented */ BOOL WINAPI GetMenuItemInfoW( HMENU hMenu, UINT Item, BOOL bypos, LPMENUITEMINFOW lpmii) { BOOL ret; MENUITEMINFOW mii; if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { SetLastError( ERROR_INVALID_PARAMETER); return FALSE; } memcpy( &mii, lpmii, lpmii->cbSize); mii.cbSize = sizeof( mii); ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE); mii.cbSize = lpmii->cbSize; memcpy( lpmii, &mii, mii.cbSize); return ret; } /* * @implemented */ UINT WINAPI GetMenuState( HMENU hMenu, UINT uId, UINT uFlags) { PITEM pItem; UINT Type = 0; TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags); if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1; if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP; if (pItem->spSubMenu) { PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); HMENU hsubmenu = UserHMGetHandle(pSubMenu); Type |= MF_POPUP; // Fix CORE-9269 if (!IsMenu(hsubmenu)) return (UINT)-1; else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|Type) & 0xff); } else return (pItem->fType | pItem->fState | Type); } /* * @implemented */ int WINAPI GetMenuStringA( HMENU hMenu, UINT uIDItem, LPSTR lpString, int nMaxCount, UINT uFlag) { ITEM *item; LPWSTR text; ////// wine Code, seems to be faster. TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag ); if (lpString && nMaxCount) lpString[0] = '\0'; if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag ))) { SetLastError( ERROR_MENU_ITEM_NOT_FOUND); return 0; } text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; if (!text) return 0; if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ); if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL )) lpString[nMaxCount-1] = 0; TRACE("A returning %s\n", lpString); return strlen(lpString); } /* * @implemented */ int WINAPI GetMenuStringW( HMENU hMenu, UINT uIDItem, LPWSTR lpString, int nMaxCount, UINT uFlag) { ITEM *item; LPWSTR text; TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag ); if (lpString && nMaxCount) lpString[0] = '\0'; if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag ))) { SetLastError( ERROR_MENU_ITEM_NOT_FOUND); return 0; } text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; if (!lpString || !nMaxCount) return text ? strlenW(text) : 0; if( !(text)) { lpString[0] = 0; return 0; } lstrcpynW( lpString, text, nMaxCount ); TRACE("W returning %S\n", lpString); return strlenW(lpString); } /* * @implemented */ HMENU WINAPI GetSubMenu( HMENU hMenu, int nPos) { PITEM pItem; if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL; if (pItem->spSubMenu) { PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); HMENU hsubmenu = UserHMGetHandle(pSubMenu); if (IsMenu(hsubmenu)) return hsubmenu; } return NULL; } /* * @implemented */ HMENU WINAPI GetSystemMenu(HWND hWnd, BOOL bRevert) { return NtUserGetSystemMenu(hWnd, bRevert); } /* * @implemented */ BOOL WINAPI InsertMenuA( HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING UnicodeString; BOOL res; RtlInitUnicodeString(&UnicodeString, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; } mii.dwTypeData = UnicodeString.Buffer; mii.cch = UnicodeString.Length / sizeof(WCHAR); } else { TRACE("Handle bitmaps\n"); } res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &UnicodeString); if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); return res; } /* * @implemented */ BOOL WINAPI InsertMenuItemA( HMENU hMenu, UINT uItem, BOOL fByPosition, LPCMENUITEMINFOA lpmii) { MENUITEMINFOW mii; UNICODE_STRING UnicodeString; BOOL res; TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); RtlInitUnicodeString(&UnicodeString, 0); if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; /* copy the text string */ if (((mii.fMask & MIIM_STRING) || ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) { if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; } mii.dwTypeData = UnicodeString.Buffer; mii.cch = UnicodeString.Length / sizeof(WCHAR); } else { TRACE("Handle bitmaps\n"); } res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString); if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); return res; } /* * @implemented */ BOOL WINAPI InsertMenuItemW( HMENU hMenu, UINT uItem, BOOL fByPosition, LPCMENUITEMINFOW lpmii) { MENUITEMINFOW mii; UNICODE_STRING MenuText; BOOL res = FALSE; /* while we could just pass 'lpmii' to win32k, we make a copy so that if a bad user passes bad data, we crash his process instead of the entire kernel */ TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); RtlInitUnicodeString(&MenuText, 0); if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; /* copy the text string */ if (((mii.fMask & MIIM_STRING) || ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) { RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData); mii.dwTypeData = MenuText.Buffer; mii.cch = MenuText.Length / sizeof(WCHAR); } res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText); return res; } /* * @implemented */ BOOL WINAPI InsertMenuW( HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCWSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING MenuText; BOOL res; RtlInitUnicodeString(&MenuText, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); mii.dwTypeData = MenuText.Buffer; mii.cch = MenuText.Length / sizeof(WCHAR); } res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &MenuText); return res; } /* * @implemented */ BOOL WINAPI IsMenu( HMENU Menu) { if (ValidateHandle(Menu, TYPE_MENU)) return TRUE; return FALSE; } /* * @implemented */ HMENU WINAPI LoadMenuA(HINSTANCE hInstance, LPCSTR lpMenuName) { HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4)); if (Resource == NULL) { return(NULL); } return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource))); } /* * @implemented */ HMENU WINAPI LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate) { return(LoadMenuIndirectW(lpMenuTemplate)); } /* * @implemented */ HMENU WINAPI LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate) { HMENU hMenu; WORD version, offset; LPCSTR p = (LPCSTR)lpMenuTemplate; version = GET_WORD(p); p += sizeof(WORD); switch (version) { case 0: /* standard format is version of 0 */ offset = GET_WORD(p); p += sizeof(WORD) + offset; if (!(hMenu = CreateMenu())) return 0; if (!MENU_ParseResource(p, hMenu)) { DestroyMenu(hMenu); return 0; } return hMenu; case 1: /* extended format is version of 1 */ offset = GET_WORD(p); p += sizeof(WORD) + offset; if (!(hMenu = CreateMenu())) return 0; if (!MENUEX_ParseResource(p, hMenu)) { DestroyMenu( hMenu ); return 0; } return hMenu; default: ERR("Menu template version %d not supported.\n", version); return 0; } } /* * @implemented */ HMENU WINAPI LoadMenuW(HINSTANCE hInstance, LPCWSTR lpMenuName) { HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU); if (Resource == NULL) { return(NULL); } return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource))); } /* * @implemented */ BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING UnicodeString; BOOL res; RtlInitUnicodeString(&UnicodeString, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; } mii.dwTypeData = UnicodeString.Buffer; mii.cch = UnicodeString.Length / sizeof(WCHAR); } else { TRACE("Handle bitmaps\n"); } res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &UnicodeString); if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); return res; } /* * @implemented */ BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCWSTR lpNewItem) { MENUITEMINFOW mii; UNICODE_STRING MenuText; BOOL res; RtlInitUnicodeString(&MenuText, 0); MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); /* copy the text string, it will be one or the other */ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) { RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); mii.dwTypeData = MenuText.Buffer; mii.cch = MenuText.Length / sizeof(WCHAR); } else { TRACE("Handle bitmaps\n"); } res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &MenuText); return res; } /* * @implemented */ BOOL WINAPI SetMenu(HWND hWnd, HMENU hMenu) { return NtUserSetMenu(hWnd, hMenu, TRUE); } /* * @implemented */ BOOL WINAPI SetMenuInfo( HMENU hmenu, LPCMENUINFO lpcmi) { MENUINFO mi; BOOL res = FALSE; if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) { SetLastError(ERROR_INVALID_PARAMETER); return res; } memcpy(&mi, lpcmi, sizeof(MENUINFO)); return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi); } /* * @implemented */ BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT uPosition, UINT uFlags, HBITMAP hBitmapUnchecked, HBITMAP hBitmapChecked) { MENUITEMINFOW uItem; memset ( &uItem, 0, sizeof(uItem) ); uItem.cbSize = sizeof(MENUITEMINFOW); uItem.fMask = MIIM_CHECKMARKS; uItem.hbmpUnchecked = hBitmapUnchecked; uItem.hbmpChecked = hBitmapChecked; return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem); } /* * @implemented */ BOOL WINAPI SetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos, LPCMENUITEMINFOA lpmii) { MENUITEMINFOW mii; UNICODE_STRING UnicodeString; BOOL Ret; TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); RtlInitUnicodeString(&UnicodeString, 0); if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; /* * MIIM_STRING == good * MIIM_TYPE & MFT_STRING == good * MIIM_STRING & MFT_STRING == good * MIIM_STRING & MFT_OWNERDRAW == good */ if (((mii.fMask & MIIM_STRING) || ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) { /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) { SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; } mii.dwTypeData = UnicodeString.Buffer; mii.cch = UnicodeString.Length / sizeof(WCHAR); } else { UnicodeString.Buffer = NULL; } Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString); if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString); return Ret; } /* * @implemented */ BOOL WINAPI SetMenuItemInfoW( HMENU hMenu, UINT uItem, BOOL fByPosition, LPCMENUITEMINFOW lpmii) { MENUITEMINFOW MenuItemInfoW; UNICODE_STRING UnicodeString; BOOL Ret; TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); RtlInitUnicodeString(&UnicodeString, 0); if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE; if (((MenuItemInfoW.fMask & MIIM_STRING) || ((MenuItemInfoW.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING))) && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) ) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData); MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData); } Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString); return Ret; } /* * @implemented */ BOOL WINAPI SetSystemMenu ( HWND hwnd, HMENU hMenu) { if(!hwnd) { SetLastError(ERROR_INVALID_WINDOW_HANDLE); return FALSE; } if(!hMenu) { SetLastError(ERROR_INVALID_MENU_HANDLE); return FALSE; } return NtUserSetSystemMenu(hwnd, hMenu); } BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y, int Reserved, HWND Wnd, CONST RECT *Rect) { return NtUserTrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL); // LPTPMPARAMS is null } /* * @unimplemented */ LRESULT WINAPI MenuWindowProcA( HWND hWnd, ULONG_PTR Result, UINT Msg, WPARAM wParam, LPARAM lParam ) { if ( Msg < WM_USER) { return PopupMenuWndProcA(hWnd, Msg, wParam, lParam ); } return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE); } /* * @unimplemented */ LRESULT WINAPI MenuWindowProcW( HWND hWnd, ULONG_PTR Result, UINT Msg, WPARAM wParam, LPARAM lParam ) { if ( Msg < WM_USER) { return PopupMenuWndProcW(hWnd, Msg, wParam, lParam ); } return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE); } /* * @implemented */ BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT cmd, LPCWSTR lpszNewItem, UINT cmdInsert, UINT flags) { /* FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert for MF_DELETE. We should check the parameters for all others MF_* actions also (anybody got a doc on ChangeMenu?). */ switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT)) { case MF_APPEND : return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem); case MF_DELETE : return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE); case MF_CHANGE : return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem); case MF_REMOVE : return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert, flags &~ MF_REMOVE); default : /* MF_INSERT */ return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem); }; } /* * @implemented */ BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT cmd, LPCSTR lpszNewItem, UINT cmdInsert, UINT flags) { /* FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert for MF_DELETE. We should check the parameters for all others MF_* actions also (anybody got a doc on ChangeMenu?). */ switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT)) { case MF_APPEND : return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem); case MF_DELETE : return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE); case MF_CHANGE : return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem); case MF_REMOVE : return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert, flags &~ MF_REMOVE); default : /* MF_INSERT */ return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem); }; }