reactos/reactos/lib/user32/windows/menu.c

1929 lines
35 KiB
C

/*
* ReactOS kernel
* Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: menu.c,v 1.16 2003/08/05 15:41:03 weiden Exp $
*
* PROJECT: ReactOS user32.dll
* FILE: lib/user32/windows/menu.c
* PURPOSE: Menus
* PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
* UPDATE HISTORY:
* 09-05-2001 CSH Created
*/
/* INCLUDES ******************************************************************/
#include <windows.h>
#include <user32.h>
#include <debug.h>
#include <string.h>
#include <draw.h>
#include <window.h>
/* TYPES *********************************************************************/
typedef struct _MENUITEM
{
UINT Type;
UINT State;
UINT Id;
HMENU SubMenu;
HBITMAP CheckBit;
HBITMAP UnCheckBit;
LPWSTR Text;
ULONG ItemData;
ULONG TypeData;
HBITMAP BmpItem;
RECT Rect;
UINT XTab;
} MENUITEM, *PMENUITEM;
typedef struct _POPUP_MENU
{
ULONG Magic;
MENUITEM* Items;
ULONG NrItems;
ULONG Width;
ULONG Height;
ULONG FocusedItem;
ULONG Flags;
HWND hWnd;
HWND hWndOwner;
HBRUSH hbrBack;
ULONG HelpId;
ULONG cyMax;
ULONG MenuData;
ULONG Style;
} POPUP_MENU, *PPOPUP_MENU;
static HFONT hMenuFont = 0;
#define MENU_TYPE_MASK ((MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
#define MENU_ITEM_TYPE(flags) \
((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
#define MENU_BAR_ITEMS_SPACE (12)
#define SEPARATOR_HEIGHT (5)
#define MENU_TAB_SPACE (8)
static ULONG ArrowBitmapWidth, ArrowBitmapHeight;
static HANDLE hStdMnArrow;
static HANDLE hMenuFontBold;
#define MENU_MAGIC (0x554D)
BOOL EndMenuCalled = FALSE;
HWND hTopPopupWnd = 0;
HANDLE hMenuDefSysPopup = 0;
#define NO_SELECTED_ITEM (0xffff)
#ifndef MF_END
#define MF_END (0x0080)
#endif
#ifndef MIIM_STRING
#define MIIM_STRING (0x00000040)
#endif
/* INTERNAL FUNCTIONS ********************************************************/
/* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
* Of course I didnt copy the ASM code because we want this to be portable
* and it needs to go away.
*/
static inline unsigned int strlenW( const WCHAR *str )
{
const WCHAR *s = str;
while (*s) s++;
return s - str;
}
static inline WCHAR *strncpyW( WCHAR *str1, const WCHAR *str2, int n )
{
WCHAR *ret = str1;
while (n-- > 0) if (!(*str1++ = *str2++)) break;
while (n-- > 0) *str1++ = 0;
return ret;
}
static inline WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
{
WCHAR *p = dst;
while ((*p++ = *src++));
return dst;
}
static inline WCHAR *strcatW( WCHAR *dst, const WCHAR *src )
{
strcpyW( dst + strlenW(dst), src );
return dst;
}
NTSTATUS
STATIC HEAP_strdupA2W ( HANDLE hHeap, LPWSTR* ppszW, LPCSTR lpszA, UINT* NewLen )
{
ULONG len;
NTSTATUS Status;
*ppszW = NULL;
if ( !lpszA )
return STATUS_SUCCESS;
len = lstrlenA(lpszA);
*ppszW = RtlAllocateHeap ( hHeap, 0, (len+1) * sizeof(WCHAR) );
if ( !*ppszW )
return STATUS_NO_MEMORY;
Status = RtlMultiByteToUnicodeN ( *ppszW, len*sizeof(WCHAR), NULL, (PCHAR)lpszA, len );
(*ppszW)[len] = L'\0';
if(NewLen) (*NewLen) = (UINT)len;
return Status;
}
#ifndef GET_WORD
#define GET_WORD(ptr) (*(WORD *)(ptr))
#endif
#ifndef GET_DWORD
#define GET_DWORD(ptr) (*(DWORD *)(ptr))
#endif
/**********************************************************************
* MENUEX_ParseResource
*
* Parse an extended menu resource and add items to the menu.
* Return a pointer to the end of the resource.
*
* FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
*/
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 += (~((int)res - 1)) & 1;
mii.dwTypeData = (LPWSTR) res;
res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
/* Align the following fields on a dword boundary. */
res += (~((int)res - 1)) & 3;
if (resinfo & 1) /* Pop-up? */
{
/* DWORD helpid = GET_DWORD(res); FIXME: use this. */
res += sizeof(DWORD);
mii.hSubMenu = CreatePopupMenu();
if (!mii.hSubMenu)
return NULL;
if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
{
DestroyMenu(mii.hSubMenu);
return NULL;
}
mii.fMask |= MIIM_SUBMENU;
mii.fType |= MF_POPUP;
}
else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
{
DbgPrint("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_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, BOOL unicode )
{
WORD flags, id = 0;
LPCSTR 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 = res;
if (!unicode) res += strlen(str) + 1;
else res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
if (flags & MF_POPUP)
{
HMENU hSubMenu = CreatePopupMenu();
if (!hSubMenu) return NULL;
if (!(res = MENU_ParseResource( res, hSubMenu, unicode )))
return NULL;
if (!unicode) AppendMenuA( hMenu, flags, (UINT)hSubMenu, str );
else AppendMenuW( hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str );
}
else /* Not a popup */
{
if (!unicode) AppendMenuA( hMenu, flags, id, *str ? str : NULL );
else AppendMenuW( hMenu, flags, id,
*(LPCWSTR)str ? (LPCWSTR)str : NULL );
}
} while (!end);
return res;
}
/* FUNCTIONS *****************************************************************/
static BOOL
MenuIsStringItem(ULONG TypeData)
{
return((TypeData & MENU_TYPE_MASK) == MF_STRING);
}
BOOL
MenuInit(VOID)
{
NONCLIENTMETRICSW ncm;
BITMAP bm;
hStdMnArrow = LoadBitmapA(0, MAKEINTRESOURCEA(OBM_MNARROW));
if (hStdMnArrow == NULL)
{
return(FALSE);
}
GetObjectA(hStdMnArrow, sizeof(bm), &bm);
ArrowBitmapWidth = bm.bmWidth;
ArrowBitmapHeight = bm.bmHeight;
if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
{
return(FALSE);
}
hMenuFont = CreateFontIndirect(&ncm.lfMenuFont);
if (hMenuFont == NULL)
{
return(FALSE);
}
ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
hMenuFontBold = CreateFontIndirect(&ncm.lfMenuFont);
if (hMenuFontBold == NULL)
{
return(FALSE);
}
return(TRUE);
}
/*static BOOL
MenuIsMenu(PPOPUP_MENU Menu)
{
return(Menu->Magic == MENU_MAGIC);
}*/
static PPOPUP_MENU
MenuGetMenu(HMENU hMenu)
{
PPOPUP_MENU Menu;
Menu = (PPOPUP_MENU)hMenu;
return(Menu);
}
static MENUITEM*
MenuFindItem(HMENU* hMenu, UINT* nPos, UINT wFlags)
{
POPUP_MENU* Menu;
ULONG i;
if ((ULONG)(*hMenu) == 0xFFFF || (Menu = MenuGetMenu(*hMenu)) == NULL)
{
return(NULL);
}
if (wFlags & MF_BYPOSITION)
{
if ((*nPos) >= Menu->NrItems)
{
return(NULL);
}
return(&Menu->Items[*nPos]);
}
else
{
MENUITEM* Item = Menu->Items;
for (i = 0; i < Menu->NrItems; i++)
{
if (Item->Id == (*nPos))
{
*nPos = i;
return(Item);
}
else if (Item->Type & MF_POPUP)
{
HMENU SubMenu = Item->SubMenu;
MENUITEM* SubItem = MenuFindItem(&SubMenu, nPos, wFlags);
if (SubItem)
{
*hMenu = SubMenu;
return(SubItem);
}
}
}
}
return(NULL);
}
/*
* @implemented
*/
WINBOOL STDCALL
AppendMenuA(HMENU hMenu,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
DPRINT("AppendMenuA(hMenu 0x%X, uFlags 0x%X, uIDNewItem %d, "
"lpNewItem %s\n", hMenu, uFlags, uIDNewItem, lpNewItem);
return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
lpNewItem));
}
/*
* @implemented
*/
WINBOOL STDCALL
AppendMenuW(HMENU hMenu,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
DPRINT("AppendMenuW(hMenu 0x%X, uFlags 0x%X, uIDNewItem %d, "
"lpNewItem %S\n", hMenu, uFlags, uIDNewItem, lpNewItem);
return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
lpNewItem));
}
/*
* @implemented
*/
DWORD STDCALL
CheckMenuItem(HMENU hmenu,
UINT uIDCheckItem,
UINT uCheck)
{
return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
}
/*
* @unimplemented
*/
WINBOOL STDCALL
CheckMenuRadioItem(HMENU hmenu,
UINT idFirst,
UINT idLast,
UINT idCheck,
UINT uFlags)
{
PMENUITEM First, Last, Check;
HMENU FirstMenu = hmenu;
HMENU LastMenu = hmenu;
HMENU CheckMenu = hmenu;
DPRINT("CheckMenuRadioItem(hmenu %X, idFirst %d, idLast %d, idCheck %d "
"uFlags %d", hmenu, idFirst, idLast, idCheck, uFlags);
First = MenuFindItem(&FirstMenu, &idFirst, uFlags);
Last = MenuFindItem(&LastMenu, &idLast, uFlags);
Check = MenuFindItem(&CheckMenu, &idCheck, uFlags);
if (First == NULL || Last == NULL || Check == NULL ||
First > Last || FirstMenu != LastMenu || FirstMenu != CheckMenu ||
Check > Last || Check < Last)
{
return(FALSE);
}
while (First < Last)
{
if (First == Check)
{
First->TypeData |= MFT_RADIOCHECK;
First->State |= MFS_CHECKED;
}
else
{
First->TypeData &= ~MFT_RADIOCHECK;
First->State &= ~MFS_CHECKED;
}
First++;
}
return(TRUE);
}
/*
* @implemented
*/
HMENU STDCALL
CreateMenu(VOID)
{
return NtUserCreateMenu();
}
/*
* @implemented
*/
HMENU STDCALL
CreatePopupMenu(VOID)
{
HMENU hMenu;
PPOPUP_MENU Menu;
hMenu = CreateMenu();
Menu = MenuGetMenu(hMenu);
Menu->Flags |= MF_POPUP;
return (HMENU)Menu;
}
/*
* @implemented
*/
WINBOOL STDCALL
DeleteMenu(HMENU hMenu,
UINT uPosition,
UINT uFlags)
{
PMENUITEM Item;
Item = MenuFindItem(&hMenu, &uPosition, uFlags);
if (Item == NULL)
{
return(FALSE);
}
if (Item->TypeData & MF_POPUP)
{
DestroyMenu(Item->SubMenu);
}
RemoveMenu(hMenu, uPosition, uFlags | MF_BYPOSITION);
return(TRUE);
}
VOID
MenuFreeItemData(PMENUITEM Item)
{
if (MenuIsStringItem(Item->TypeData) && Item->Text != NULL)
{
HeapFree(GetProcessHeap(), 0, Item->Text);
}
}
/*
* @implemented
*/
WINBOOL STDCALL
DestroyMenu(HMENU hMenu)
{
return NtUserDestroyMenu(hMenu);
}
/*
* @implemented
*/
WINBOOL STDCALL
DrawMenuBar(HWND hWnd)
{
ULONG Style;
ULONG MenuId;
PPOPUP_MENU Menu;
Style = GetWindowLong(hWnd, GWL_STYLE);
MenuId = GetWindowLong(hWnd, GWL_ID);
if (!(Style & WS_CHILD) && MenuId != 0)
{
Menu = MenuGetMenu((HMENU)MenuId);
Menu->Height = 0;
Menu->hWndOwner = hWnd;
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
return(TRUE);
}
return(FALSE);
}
/*
* @implemented
*/
UINT STDCALL
EnableMenuItem(HMENU hMenu,
UINT uIDEnableItem,
UINT uEnable)
{
return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
}
BOOL STATIC
MenuIsMenuActive(VOID)
{
return(hTopPopupWnd != NULL && IsWindowVisible(hTopPopupWnd));
}
/*
* @implemented
*/
WINBOOL STDCALL
EndMenu(VOID)
{
if (!EndMenuCalled && MenuIsMenuActive())
{
EndMenuCalled = TRUE;
PostMessageA(hTopPopupWnd, WM_CANCELMODE, 0, 0);
}
return(TRUE);
}
/*
* @implemented
*/
HMENU STDCALL
GetMenu(HWND hWnd)
{
return((HMENU)GetWindowLong(hWnd, GWL_ID));
}
/*
* @unimplemented
*/
WINBOOL STDCALL
GetMenuBarInfo(HWND hwnd,
LONG idObject,
LONG idItem,
PMENUBARINFO pmbi)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
LONG STDCALL
GetMenuCheckMarkDimensions(VOID)
{
return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
GetSystemMetrics(SM_CYMENUCHECK)));
}
/*
* @implemented
*/
UINT STDCALL
GetMenuDefaultItem(HMENU hMenu,
UINT fByPos,
UINT gmdiFlags)
{
PPOPUP_MENU Menu;
PMENUITEM Item;
ULONG i;
Menu = MenuGetMenu(hMenu);
if (Menu->Items == NULL)
{
return(-1);
}
Item = Menu->Items;
while (!(Item->State & MFS_DEFAULT))
{
i++;
Item++;
if (i >= Menu->NrItems)
{
return(-1);
}
}
if (!(gmdiFlags & GMDI_USEDISABLED) && (Item->State & MFS_DISABLED))
{
return(-1);
}
if ((Item->TypeData & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS))
{
UINT Ret;
Ret = GetMenuDefaultItem(Item->SubMenu, fByPos, gmdiFlags);
if (Ret != -1)
{
return(Ret);
}
}
return(fByPos ? i : Item->Id);
}
/*
* @unimplemented
*/
WINBOOL STDCALL
GetMenuInfo(HMENU hmenu,
LPMENUINFO lpcmi)
{
PPOPUP_MENU Menu;
Menu = MenuGetMenu(hmenu);
if (lpcmi->fMask & MIM_BACKGROUND)
{
lpcmi->hbrBack = Menu->hbrBack;
}
if (lpcmi->fMask & MIM_HELPID)
{
lpcmi->dwContextHelpID = Menu->HelpId;
}
if (lpcmi->fMask & MIM_MAXHEIGHT)
{
lpcmi->cyMax = Menu->cyMax;
}
if (lpcmi->fMask & MIM_MENUDATA)
{
lpcmi->dwMenuData = Menu->MenuData;
}
if (lpcmi->fMask & MIM_STYLE)
{
lpcmi->dwStyle = Menu->Style;
}
return(TRUE);
}
/*
* @implemented
*/
int STDCALL
GetMenuItemCount(HMENU hMenu)
{
return NtUserBuildMenuItemList(hMenu, NULL, 0, 0);
}
/*
* @implemented
*/
UINT STDCALL
GetMenuItemID(HMENU hMenu,
int nPos)
{
PMENUITEM Item;
Item = MenuFindItem(&hMenu, &nPos, MF_BYPOSITION);
if (Item == NULL)
{
return(0);
}
if (Item->TypeData & MF_POPUP)
{
return(-1);
}
return(Item->Id);
}
/*
* @unimplemented
*/
WINBOOL STDCALL
GetMenuItemInfoA(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPMENUITEMINFOA lpmii)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
GetMenuItemInfoW(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPMENUITEMINFOW lpmii)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL STDCALL
GetMenuItemRect(HWND hWnd,
HMENU hMenu,
UINT uItem,
LPRECT lprcItem)
{
UNIMPLEMENTED;
return(FALSE);
}
/*
* @unimplemented
*/
UINT
STDCALL
GetMenuState(
HMENU hMenu,
UINT uId,
UINT uFlags)
{
UNIMPLEMENTED;
return 0;
}
/*
* @unimplemented
*/
int
STDCALL
GetMenuStringA(
HMENU hMenu,
UINT uIDItem,
LPSTR lpString,
int nMaxCount,
UINT uFlag)
{
UNIMPLEMENTED;
return 0;
}
/*
* @unimplemented
*/
int
STDCALL
GetMenuStringW(
HMENU hMenu,
UINT uIDItem,
LPWSTR lpString,
int nMaxCount,
UINT uFlag)
{
UNIMPLEMENTED;
return 0;
}
/*
* @unimplemented
*/
HMENU
STDCALL
GetSubMenu(
HMENU hMenu,
int nPos)
{
UNIMPLEMENTED;
return (HMENU)0;
}
/*
* @implemented
*/
WINBOOL
STDCALL
HiliteMenuItem(
HWND hwnd,
HMENU hmenu,
UINT uItemHilite,
UINT uHilite)
{
return NtUserHiliteMenuItem(hwnd, hmenu, uItemHilite, uHilite);
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
InsertMenuA(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
InsertMenuItemA(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPCMENUITEMINFOA lpmii)
{
MENUITEMINFOW mi;
WINBOOL res = FALSE;
BOOL CleanHeap = FALSE;
NTSTATUS Status;
HANDLE hHeap = RtlGetProcessHeap();
if((lpmii->cbSize == sizeof(MENUITEMINFO)) ||
(lpmii->cbSize == sizeof(MENUITEMINFO) - sizeof(HBITMAP)))
{
memcpy(&mi, lpmii, lpmii->cbSize);
/* copy the text string */
if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
(MENU_ITEM_TYPE(mi.fType) == MF_STRING) && mi.dwTypeData)
{
Status = HEAP_strdupA2W (hHeap, &mi.dwTypeData, (LPCSTR)mi.dwTypeData, &mi.cch);
if (!NT_SUCCESS (Status))
{
SetLastError (RtlNtStatusToDosError(Status));
return FALSE;
}
CleanHeap = TRUE;
}
res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
if(CleanHeap) RtlFreeHeap (hHeap, 0, mi.dwTypeData);
}
return res;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
InsertMenuItemW(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPCMENUITEMINFOW lpmii)
{
MENUITEMINFOW mi;
WINBOOL res = FALSE;
BOOL CleanHeap = FALSE;
ULONG len = 0;
NTSTATUS Status;
HANDLE hHeap = RtlGetProcessHeap();
if((lpmii->cbSize == sizeof(MENUITEMINFO)) ||
(lpmii->cbSize == sizeof(MENUITEMINFO) - sizeof(HBITMAP)))
{
memcpy(&mi, lpmii, lpmii->cbSize);
/* copy the text string */
if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
(MENU_ITEM_TYPE(mi.fType) == MF_STRING) && mi.dwTypeData)
{
len = lstrlenW(lpmii->dwTypeData);
DbgPrint("InsertMenuItemW() len = %d\n", len);
mi.dwTypeData = RtlAllocateHeap(hHeap, 0, (len + 1) * sizeof(WCHAR));
if(!mi.dwTypeData)
{
SetLastError (RtlNtStatusToDosError(Status));
return FALSE;
}
memcpy(&mi.dwTypeData, &lpmii->dwTypeData, len);
CleanHeap = TRUE;
mi.cch = len;
DbgPrint("InsertMenuItemW() Text = %ws\n", (PWSTR)mi.dwTypeData);
}
else
{
DbgPrint("InsertMenuItemW() No Text\n");
}
res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
if(CleanHeap) RtlFreeHeap (hHeap, 0, mi.dwTypeData);
}
return res;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
InsertMenuW(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
IsMenu(
HMENU hMenu)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
HMENU STDCALL
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 STDCALL
LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
{
return(LoadMenuIndirectW(lpMenuTemplate));
}
/*
* @implemented
*/
HMENU STDCALL
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, TRUE ))
{
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:
DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
return 0;
}
}
/*
* @implemented
*/
HMENU STDCALL
LoadMenuW(HINSTANCE hInstance,
LPCWSTR lpMenuName)
{
HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
if (Resource == NULL)
{
return(NULL);
}
return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
}
/*
* @unimplemented
*/
int
STDCALL
MenuItemFromPoint(
HWND hWnd,
HMENU hMenu,
POINT ptScreen)
{
UNIMPLEMENTED;
return 0;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
ModifyMenuA(
HMENU hMnu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCSTR lpNewItem)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
ModifyMenuW(
HMENU hMnu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
RemoveMenu(
HMENU hMenu,
UINT uPosition,
UINT uFlags)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @implemented
*/
WINBOOL STDCALL
SetMenu(HWND hWnd,
HMENU hMenu)
{
ULONG Style = GetWindowLong(hWnd, GWL_STYLE);
if (Style & WS_CHILD)
{
return(FALSE);
}
if (GetCapture() == hWnd)
{
ReleaseCapture();
}
SetWindowLong(hWnd, GWL_ID, (LONG)hMenu);
if (hMenu != 0)
{
PPOPUP_MENU Menu;
Menu = MenuGetMenu(hMenu);
Menu->hWnd = hWnd;
Menu->Height = 0;
}
if (IsWindowVisible(hWnd))
{
SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
return(TRUE);
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuDefaultItem(
HMENU hMenu,
UINT uItem,
UINT fByPos)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuInfo(
HMENU hmenu,
LPCMENUINFO lpcmi)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuItemBitmaps(
HMENU hMenu,
UINT uPosition,
UINT uFlags,
HBITMAP hBitmapUnchecked,
HBITMAP hBitmapChecked)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuItemInfoA(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPMENUITEMINFOA lpmii)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuItemInfoW(
HMENU hMenu,
UINT uItem,
WINBOOL fByPosition,
LPMENUITEMINFOW lpmii)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
TrackPopupMenu(
HMENU hMenu,
UINT uFlags,
int x,
int y,
int nReserved,
HWND hWnd,
CONST RECT *prcRect)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
TrackPopupMenuEx(
HMENU hmenu,
UINT fuFlags,
int x,
int y,
HWND hwnd,
LPTPMPARAMS lptpm)
{
UNIMPLEMENTED;
return FALSE;
}
/*
* @unimplemented
*/
WINBOOL
STDCALL
SetMenuContextHelpId(HMENU hmenu,
DWORD dwContextHelpId)
{
return NtUserSetMenuContextHelpId(hmenu, dwContextHelpId);
}
/*
* @unimplemented
*/
DWORD
STDCALL
GetMenuContextHelpId(HMENU hmenu)
{
UNIMPLEMENTED;
return(0);
}
static BOOL
MenuIsBitmapItem(ULONG TypeData)
{
return((TypeData & MENU_TYPE_MASK) == MF_BITMAP);
}
static BOOL
MenuIsMagicItem(LPWSTR Text)
{
return(LOWORD((ULONG)Text) < 12);
}
static HBITMAP
MenuLoadMagicItem(ULONG Id, ULONG Hilite, ULONG ItemData)
{
UNIMPLEMENTED;
return(NULL);
}
static VOID
MenuCalcItemSize(HDC hDC, PMENUITEM Item, HWND hWndOwner, LONG orgX,
LONG orgY, BOOL MenuBar)
{
ULONG CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
SetRect(&Item->Rect, orgX, orgY, orgX, orgY);
if (Item->TypeData & MF_OWNERDRAW)
{
MEASUREITEMSTRUCT mis;
mis.CtlType = ODT_MENU;
mis.CtlID = 0;
mis.itemID = Item->Id;
mis.itemData = Item->ItemData;
mis.itemHeight = 0;
mis.itemWidth = 0;
SendMessageA(hWndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis);
Item->Rect.right += mis.itemWidth;
if (MenuBar)
{
Item->Rect.right += MENU_BAR_ITEMS_SPACE;
Item->Rect.bottom += GetSystemMetrics(SM_CYMENU);
}
else
{
Item->Rect.bottom += mis.itemHeight;
}
}
if (Item->ItemData & MF_SEPARATOR)
{
Item->Rect.bottom += SEPARATOR_HEIGHT;
return;
}
if (!MenuBar)
{
Item->Rect.right += 2 * CheckBitmapWidth;
if (Item->TypeData & MF_POPUP)
{
Item->Rect.right += ArrowBitmapWidth;
}
}
if (Item->TypeData & MF_OWNERDRAW)
{
return;
}
if (MenuIsBitmapItem(Item->TypeData))
{
HBITMAP Bmp;
BITMAP Bm;
if (MenuIsMagicItem(Item->Text))
{
Bmp = MenuLoadMagicItem((LONG)Item->Text,
Item->TypeData & MF_HILITE,
Item->ItemData);
}
else
{
Bmp = (HBITMAP)Item->Text;
}
if (GetObjectA(Bmp, sizeof(Bm), &Bm))
{
Item->Rect.right += Bm.bmWidth;
Item->Rect.bottom += Bm.bmHeight;
}
}
if (MenuIsStringItem(Item->TypeData))
{
SIZE size;
LPWSTR p;
GetTextExtentPoint32W(hDC, Item->Text, wcslen(Item->Text), &size);
Item->Rect.right += size.cx;
Item->Rect.bottom += max(size.cy, GetSystemMetrics(SM_CYMENU));
Item->XTab = 0;
if (MenuBar)
{
Item->Rect.right += MENU_BAR_ITEMS_SPACE;
}
else if ((p = wcschr(Item->Text, '\t')) != NULL)
{
GetTextExtentPoint32W(hDC, Item->Text, (LONG)(p - Item->Text),
&size);
Item->XTab = CheckBitmapWidth + MENU_TAB_SPACE + size.cx;
Item->Rect.right += MENU_TAB_SPACE;
}
else
{
if (wcschr(Item->Text, '\b') != NULL)
{
Item->Rect.right += MENU_TAB_SPACE;
}
Item->XTab = Item->Rect.right - CheckBitmapWidth - ArrowBitmapWidth;
}
}
}
static VOID
MenuMenuBarCalcSize(HDC hDC, LPRECT Rect, PPOPUP_MENU Menu, HWND hWndOwner)
{
LONG maxY, start, helpPos, orgX, orgY, i;
PMENUITEM Item;
if (Rect == NULL || Menu == NULL)
{
return;
}
if (Menu->NrItems == 0)
{
return;
}
Menu->Width = Rect->right - Rect->left;
Menu->Height = 0;
maxY = Rect->top + 1;
start = 0;
helpPos = -1;
while (start < Menu->NrItems)
{
Item = Menu->Items + start;
orgX = Rect->left;
orgY = maxY;
for (i = start; i < Menu->NrItems; i++, Item++)
{
if (helpPos == -1 && Item->TypeData & MF_RIGHTJUSTIFY)
{
helpPos = i;
}
if (i != start && Item->TypeData & (MF_MENUBREAK | MF_MENUBARBREAK))
{
break;
}
MenuCalcItemSize(hDC, Item, hWndOwner, orgX, orgY, TRUE);
if (Item->Rect.right > Rect->right)
{
if (i != start)
{
break;
}
else
{
Item->Rect.right = Rect->right;
}
}
maxY = max(maxY, Item->Rect.bottom);
orgX = Item->Rect.right;
}
for (;start < i; start++)
{
Menu->Items[start].Rect.bottom = maxY;
}
}
Rect->bottom = maxY;
Menu->Height = Rect->bottom - Rect->top;
Item = Menu->Items + Menu->NrItems - 1;
orgY = Item->Rect.top;
orgX = Rect->right;
for (i = Menu->NrItems; i >= helpPos; i--, Item--)
{
if (helpPos == -1 || helpPos > i)
{
break;
}
if (Item->Rect.top != orgY)
{
break;
}
if (Item->Rect.right >= orgX)
{
break;
}
Item->Rect.left += orgX - Item->Rect.right;
Item->Rect.right = orgX;
orgX = Item->Rect.left;
}
}
VOID STATIC
MenuDrawMenuItem(HWND hWnd, HMENU hMenu, HWND hWndOwner, HDC hDC,
PMENUITEM Item, ULONG Height, BOOL MenuBar, ULONG OdAction)
{
RECT Rect;
if (Item->ItemData & MF_SYSMENU)
{
if (!IsIconic(hWnd))
{
UserDrawSysMenuButton(hWnd, hDC,
Item->State & (MF_HILITE | MF_MOUSESELECT));
}
return;
}
if (Item->ItemData & MF_OWNERDRAW)
{
DRAWITEMSTRUCT dis;
dis.CtlType = ODT_MENU;
dis.CtlID = 0;
dis.itemID = Item->Id;
dis.itemData = (ULONG)Item->ItemData;
dis.itemState = 0;
if (Item->State & MF_CHECKED)
{
dis.itemState |= ODS_CHECKED;
}
if (Item->State & MF_GRAYED)
{
dis.itemState |= ODS_GRAYED;
}
if (Item->State & MF_HILITE)
{
dis.itemState |= ODS_SELECTED;
}
dis.itemAction = OdAction;
dis.hDC = hDC;
dis.rcItem = Item->Rect;
SendMessageA(hWndOwner, WM_DRAWITEM, 0, (LPARAM)&dis);
}
if (MenuBar && (Item->TypeData & MF_SEPARATOR))
{
return;
}
Rect = Item->Rect;
if (!(Item->TypeData & MF_OWNERDRAW))
{
if (Item->State & MF_HILITE)
{
if (!MenuIsBitmapItem(Item->TypeData))
{
FillRect(hDC, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT));
}
}
else
{
FillRect(hDC, &Rect, GetSysColorBrush(COLOR_MENU));
}
}
SetBkMode(hDC, TRANSPARENT);
if (!(Item->TypeData & MF_OWNERDRAW))
{
if (!MenuBar && (Item->TypeData & MF_MENUBARBREAK))
{
SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
MoveToEx(hDC, Rect.left, 0, NULL);
LineTo(hDC, Rect.left, Height);
}
if (Item->TypeData & MF_SEPARATOR)
{
SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
MoveToEx(hDC, Rect.left, Rect.right + (SEPARATOR_HEIGHT / 2), NULL);
LineTo(hDC, Rect.right, Rect.top + (SEPARATOR_HEIGHT / 2));
return;
}
}
if (Item->State & MF_HILITE)
{
SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
if (!MenuIsBitmapItem(Item->Type))
{
SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT));
}
}
else
{
if (Item->State & MF_GRAYED)
{
SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
}
else
{
SetTextColor(hDC, GetSysColor(COLOR_MENUTEXT));
}
SetBkColor(hDC, GetSysColor(COLOR_MENU));
}
if (!MenuBar)
{
LONG y = Rect.top + Rect.bottom;
ULONG CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
ULONG CheckBitmapHeight = GetSystemMetrics(SM_CYMENUCHECK);
if (!(Item->TypeData & MF_OWNERDRAW))
{
HBITMAP bm;
if (Item->TypeData & MF_CHECKED)
{
bm = Item->CheckBit;
}
else
{
bm = Item->UnCheckBit;
}
if (bm)
{
HDC hDCMem = CreateCompatibleDC(hDC);
SelectObject(hDCMem, bm);
BitBlt(hDC,
Rect.left,
(y - CheckBitmapHeight) / 2,
CheckBitmapWidth,
CheckBitmapHeight,
hDCMem,
0,
0,
SRCCOPY);
DeleteDC(hDCMem);
}
else if (Item->TypeData & MF_CHECKED)
{
RECT r;
ULONG Type;
HBITMAP bm = CreateBitmap(CheckBitmapWidth, CheckBitmapHeight,
1, 1, NULL);
HDC hDCMem = CreateCompatibleDC(hDC);
SelectObject(hDCMem, bm);
SetRect(&r, 0, 0, CheckBitmapWidth, CheckBitmapHeight);
if (Item->TypeData & MFT_RADIOCHECK)
{
Type = DFCS_MENUBULLET;
}
else
{
Type = DFCS_MENUCHECK;
}
DrawFrameControl(hDCMem, &r, DFC_MENU, Type);
BitBlt(hDC, Rect.left, (y - r.bottom) / 2, r.right, r.bottom,
hDCMem, 0, 0, SRCCOPY);
DeleteDC(hDCMem);
DeleteObject(bm);
}
}
if (Item->TypeData & MF_POPUP)
{
HDC hDCMem = CreateCompatibleDC(hDC);
SelectObject(hDCMem, hStdMnArrow);
BitBlt(hDC,
Rect.right - ArrowBitmapWidth - 1,
(y - ArrowBitmapHeight) / 2,
ArrowBitmapWidth,
ArrowBitmapHeight,
hDCMem,
0,
0,
SRCCOPY);
DeleteDC(hDCMem);
}
Rect.left += CheckBitmapWidth;
Rect.right -= ArrowBitmapWidth;
}
if (Item->TypeData & MF_OWNERDRAW)
{
return;
}
if (MenuIsBitmapItem(Item->TypeData))
{
LONG left, top, w, h;
ULONG Rop;
HBITMAP ResBmp = 0;
HDC hDCMem = CreateCompatibleDC(hDC);
if (MenuIsMagicItem(Item->Text))
{
ResBmp = MenuLoadMagicItem((LONG)Item->Text, Item->State & MF_HILITE,
Item->ItemData);
}
else
{
ResBmp = (HBITMAP)Item->Text;
}
if (ResBmp)
{
BITMAP Bm;
GetObjectA(ResBmp, sizeof(Bm), &Bm);
SelectObject(hDCMem, ResBmp);
h = Rect.bottom - Rect.top;
if (h > Bm.bmHeight)
{
top = Rect.top + (h - Bm.bmHeight) / 2;
}
else
{
top = Rect.top;
}
w = Rect.right - Rect.left;
left = Rect.left;
left++;
w -= 2;
if ((Item->State & MF_HILITE) && !MenuIsMagicItem(Item->Text) &&
!MenuBar)
{
Rop = MERGEPAINT;
}
else
{
Rop = SRCCOPY;
}
BitBlt(hDC, left, top, w, h, hDCMem, 0, 0, Rop);
}
DeleteDC(hDCMem);
return;
}
else if (MenuIsStringItem(Item->Type))
{
LONG i;
HFONT hFontOld = 0;
UINT Format = (MenuBar) ? DT_CENTER | DT_VCENTER | DT_SINGLELINE :
DT_LEFT | DT_VCENTER | DT_SINGLELINE;
if (Item->State & MFS_DEFAULT)
{
hFontOld = SelectObject(hDC, hMenuFontBold);
}
if (MenuBar)
{
Rect.left += MENU_BAR_ITEMS_SPACE / 2;
Rect.right += MENU_BAR_ITEMS_SPACE / 2;
i = wcslen(Item->Text);
}
else
{
for (i = 0; i < wcslen(Item->Text); i++)
{
if (Item->Text[i] == '\t' || Item->Text[i] == '\b')
{
break;
}
}
}
DrawTextW(hDC, Item->Text, i, &Rect, Format);
if (Item->Text[i] != '\0')
{
if (Item->Text[i] == '\t')
{
Rect.left = Item->XTab;
Format = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
}
else
{
Format = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
}
DrawTextW(hDC, Item->Text + i + 1, -1, &Rect, Format);
}
if (hFontOld != 0)
{
SelectObject(hDC, hFontOld);
}
}
}
UINT
MenuDrawMenuBar(HDC hDC, LPRECT Rect, HWND hWnd, BOOL Draw)
{
ULONG MenuID;
PPOPUP_MENU Menu;
HFONT hFontOld;
ULONG i;
MenuID = GetWindowLong(hWnd, GWL_ID);
Menu = MenuGetMenu((HMENU)MenuID);
if (Menu == NULL || Rect == NULL)
{
return(GetSystemMetrics(SM_CYMENU));
}
hFontOld = SelectObject(hDC, hMenuFont);
if (Menu->Height == 0)
{
MenuMenuBarCalcSize(hDC, Rect, Menu, hWnd);
}
Rect->bottom = Rect->top + Menu->Height;
if (!Draw)
{
SelectObject(hDC, hFontOld);
return(Menu->Height);
}
FillRect(hDC, Rect, GetSysColorBrush(COLOR_MENU));
SelectObject(hDC, GetSysColorPen(COLOR_WINDOWFRAME));
MoveToEx(hDC, Rect->left, Rect->bottom, NULL);
LineTo(hDC, Rect->right, Rect->bottom);
if (Menu->NrItems == 0)
{
SelectObject(hDC, hFontOld);
return(GetSystemMetrics(SM_CYMENU));
}
for (i = 0; i < Menu->NrItems; i++)
{
MenuDrawMenuItem(hWnd, (HMENU)MenuID, hWnd, hDC,
Menu->Items + i, Menu->Height, TRUE, ODA_DRAWENTIRE);
}
SelectObject(hDC, hFontOld);
return(Menu->Height);
}
ULONG
MenuGetMenuBarHeight(HWND hWnd, ULONG MenuBarWidth, LONG OrgX, LONG OrgY)
{
ULONG MenuId;
PPOPUP_MENU Menu;
RECT Rect;
HDC hDC;
MenuId = GetWindowLong(hWnd, GWL_ID);
Menu = MenuGetMenu((HMENU)MenuId);
if (Menu == NULL)
{
return(0);
}
hDC = GetDCEx(hWnd, 0, DCX_CACHE | DCX_WINDOW);
SelectObject(hDC, hMenuFont);
SetRect(&Rect, OrgX, OrgY, OrgX + MenuBarWidth,
OrgY + GetSystemMetrics(SM_CYMENU));
MenuMenuBarCalcSize(hDC, &Rect, Menu, hWnd);
ReleaseDC(hWnd, hDC);
return(Menu->Height);
}
VOID
MenuTrackMouseMenuBar(HWND hWnd, ULONG Ht, POINT Pt)
{
}
VOID
MenuTrackKbdMenuBar(HWND hWnd, ULONG wParam, ULONG Key)
{
}