mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
728d5736d9
IS_SYSTEM_MENU(): "moved" to ntuser on6dfa71c
(r68904). IS_SYSTEM_POPUP(): uselessly added on6fc29cc
(r8195). IS_BITMAP_ITEM(): uses removed on07b6ddc
(r23221). uselessly copied to ntuser on6dfa71c
(r68904).
1776 lines
43 KiB
C
1776 lines
43 KiB
C
/*
|
|
* 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 <user32.h>
|
|
|
|
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))
|
|
|
|
/*********************************************************************
|
|
* 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 ? MAKEINTRESOURCEW(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);
|
|
};
|
|
}
|
|
|