X-Git-Url: http://git.jankratochvil.net/?p=reactos.git;a=blobdiff_plain;f=lib%2Fuser32%2Fwindows%2Fmenu.c;fp=lib%2Fuser32%2Fwindows%2Fmenu.c;h=c028e2c28595ba97987ff60733e08dbcc8990de9;hp=586d8272bda56260af06ddf13603e5501b2c1726;hb=a3df8bf1429570e0bd6c6428f6ed80073578cf4b;hpb=7c0db166f81fbe8c8b913d7f26048e337d383605 diff --git a/lib/user32/windows/menu.c b/lib/user32/windows/menu.c index 586d827..c028e2c 100644 --- a/lib/user32/windows/menu.c +++ b/lib/user32/windows/menu.c @@ -31,291 +31,755 @@ #include #include #include +#include +#include +#include +#include + +#include +#include "user32/regcontrol.h" +#include "../controls/controls.h" /* TYPES *********************************************************************/ -typedef struct _MENUITEM -{ - UINT Type; - UINT State; - UINT Id; - HMENU SubMenu; - HBITMAP CheckBit; - HBITMAP UnCheckBit; - LPWSTR Text; - DWORD ItemData; - DWORD TypeData; - HBITMAP BmpItem; - RECT Rect; - UINT XTab; -} MENUITEM, *PMENUITEM; +#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) + +#ifndef MF_END +#define MF_END (0x0080) +#endif -typedef struct _POPUP_MENU +#ifndef MIIM_STRING +#define MIIM_STRING (0x00000040) +#endif + +#define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom)))) +#define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom)))) +#define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */ +#define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */ + +/********************************************************************* + * PopupMenu class descriptor + */ +const struct builtin_class_descr POPUPMENU_builtin_class = { - MENUITEM* Items; - WORD NrItems; -} POPUP_MENU, *PPOPUP_MENU; + POPUPMENU_CLASS_ATOMW, /* name */ + CS_GLOBALCLASS | CS_SAVEBITS | CS_DBLCLKS, /* style */ + (WNDPROC) NULL, /* FIXME - procW */ + sizeof(MENUINFO *), /* extra */ + (LPCWSTR) IDC_ARROW, /* cursor */ + (HBRUSH)COLOR_MENU /* brush */ +}; -/* FUNCTIONS *****************************************************************/ -static PPOPUP_MENU -MenuGetMenu(HMENU hMenu) +/* 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 ) { - PPOPUP_MENU Menu; - Menu = (PPOPUP_MENU)hMenu; - return(Menu); + 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 MENUITEM* -MenuFindItem(HMENU* hMenu, UINT* nPos, UINT wFlags) +static inline WCHAR *strcpyW( WCHAR *dst, const WCHAR *src ) { - POPUP_MENU* Menu; - ULONG i; + WCHAR *p = dst; + while ((*p++ = *src++)); + return dst; +} - if ((ULONG)(*hMenu) == 0xFFFF || (Menu = MenuGetMenu(*hMenu)) == NULL) - { - return(NULL); - } - if (wFlags & MF_BYPOSITION) +static inline WCHAR *strcatW( WCHAR *dst, const WCHAR *src ) +{ + strcpyW( dst + strlenW(dst), src ); + return dst; +} + +#ifndef GET_WORD +#define GET_WORD(ptr) (*(WORD *)(ptr)) +#endif +#ifndef GET_DWORD +#define GET_DWORD(ptr) (*(DWORD *)(ptr)) +#endif + +HFONT hMenuFont = NULL; +HFONT hMenuFontBold = NULL; + +/********************************************************************** + * 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 { - if ((*nPos) >= Menu->NrItems) + 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? */ { - return(NULL); + /* 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; } - return(&Menu->Items[*nPos]); + 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; + HMENU hSubMenu; + 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) + { + 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; +} + + +NTSTATUS STDCALL +User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength) +{ + LRESULT Result; + HMODULE hUser32; + hUser32 = GetModuleHandleW(L"USER32"); + Result = (LRESULT)LoadMenuW(hUser32, L"SYSMENU"); + return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS)); +} + + +BOOL +MenuInit(VOID) +{ + NONCLIENTMETRICSW ncm; + + /* get the menu font */ + if(!hMenuFont || !hMenuFontBold) + { + ncm.cbSize = sizeof(ncm); + if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) + { + DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n"); + return FALSE; + } + + hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont); + if(hMenuFont == NULL) + { + DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n"); + return FALSE; + } + + ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000); + hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont); + if(hMenuFontBold == NULL) + { + DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n"); + return FALSE; + } + } + + return TRUE; +} + + +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(GetSystemMetrics(SM_CYMENU)); +} + +static BOOL +MeasureMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str) +{ + BOOL res = FALSE; + MEASUREITEMSTRUCT mis; + SIZE sz; + + if(mii->fType & MFT_OWNERDRAW) + { + /* send WM_MEASUREITEM message to window */ + mis.CtlType = ODT_MENU; + mis.CtlID = 0; + mis.itemID = mii->wID; + mis.itemWidth = 0; + mis.itemHeight = 0; + mis.itemData = mii->dwItemData; + res = (BOOL)SendMessageW(hWnd, WM_MEASUREITEM, 0, (LPARAM)&mis); + if(res) + { + mir->right = mir->left + mis.itemWidth; + mir->bottom = mir->top + mis.itemHeight; + } + else + { + /* FIXME calculate size internally assuming the menu item is empty */ + mir->right = mir->left + 1; + mir->bottom = mir->top + 1; + } + return res; + } else + { + GetTextExtentPoint32W(hDC, str, mii->cch, &sz); + /* FIXME calculate the size of the menu item */ + mir->right = mir->left + sz.cx + 6; + mir->bottom = mir->top + max(sz.cy, GetSystemMetrics(SM_CYMENU)); + return TRUE; + } +} + +static BOOL +DrawMenuItem(HWND hWnd, HMENU mnu, HDC hDC, MENUITEMINFOW *mii, RECT *mir, LPWSTR str) +{ + BOOL res = FALSE; + DRAWITEMSTRUCT dis; + + if(mii->fType & MFT_OWNERDRAW) + { + /* send WM_DRAWITEM message to window */ + dis.CtlType = ODT_MENU; + dis.CtlID = 0; + dis.itemID = mii->wID; + dis.itemAction = ODA_DRAWENTIRE; /* FIXME */ + dis.itemState = 0; /* FIXME */ + dis.hwndItem = (HWND)mnu; + dis.hDC = hDC; + RtlCopyMemory(&dis.rcItem, mir, sizeof(RECT)); + dis.itemData = mii->dwItemData; + res = (BOOL)SendMessageW(hWnd, WM_DRAWITEM, 0, (LPARAM)&dis); + return res; + } + else + { + /* FIXME draw the menu item */ + SetTextColor(hDC, COLOR_MENUTEXT); + DrawTextW(hDC, str, mii->cch, mir, DT_SINGLELINE | DT_VCENTER | DT_CENTER); + } + return res; +} + + +UINT +MenuDrawMenuBar(HDC hDC, LPRECT Rect, HWND hWnd, BOOL Draw) +{ + UINT height; + HMENU mnu; + HANDLE hHeap; + PVOID Buf, hBuf; + DWORD BufSize, Items, Items2; + MENUITEMINFOW *mii; + RECT *omir, *mir = NULL; + LPWSTR str; + + height = Rect->bottom - Rect->top; + mnu = GetMenu(hWnd); /* Fixme - pass menu handle as parameter */ + /* get menu item list size */ + BufSize = NtUserBuildMenuItemList(mnu, (VOID*)1, 0, 0); + if(BufSize) + { + /* FIXME cache menu bar items using NtUserDrawMenuBarTemp() + instead of allocating and deallocating memory everytime */ + + hHeap = GetProcessHeap(); + hBuf = HeapAlloc(hHeap, 0, BufSize); + if(!hBuf) + return(Rect->bottom - Rect->top); + Buf = hBuf; + /* copy menu items into buffer */ + Items = Items2 = NtUserBuildMenuItemList(mnu, Buf, BufSize, 0); + + /* calculate menu item rectangles */ + while(Items > 0) { - 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); - } - } - } + omir = mir; + mii = (LPMENUITEMINFOW)Buf; + Buf += sizeof(MENUITEMINFOW); + mir = (LPRECT)Buf; + Buf += sizeof(RECT); + if(mii->cch) + { + str = (LPWSTR)Buf; + Buf += (mii->cch + 1) * sizeof(WCHAR); + } + else + str = NULL; + if(omir) + { + mir->left = omir->right + 1; + mir->top = omir->top; + mir->right += mir->left; + mir->bottom += mir->top; + } + else + { + mir->left = Rect->left; + mir->top = Rect->top; + } + MeasureMenuItem(hWnd, mnu, hDC, mii, mir, str); + + height = max(height, mir->top + mir->bottom); + /* DbgPrint("Measure menu item %ws: (%d, %d, %d, %d)\n", str, mir->left, mir->top, mir->right, mir->bottom); */ + Items--; } - return(NULL); + height = max(height, GetSystemMetrics(SM_CYMENU)); + + Buf = hBuf; + /* draw menu items */ + while (Items2 > 0) + { + mii = (LPMENUITEMINFOW)Buf; + Buf += sizeof(MENUITEMINFOW); + mir = (LPRECT)Buf; + Buf += sizeof(RECT); + if(mii->cch) + { + str = (LPWSTR)Buf; + Buf += (mii->cch + 1) * sizeof(WCHAR); + } + else + str = NULL; + /* DbgPrint("Draw menu item %ws at (%d, %d, %d, %d)\n", str, mir->left, mir->top, mir->right, mir->bottom); */ + DrawMenuItem(hWnd, mnu, hDC, mii, mir, str); + Items2--; + } + + HeapFree(hHeap, 0, hBuf); + } + + return height; +} + + +VOID +MenuTrackMouseMenuBar(HWND hWnd, ULONG Ht, POINT Pt) +{ +} + + +VOID +MenuTrackKbdMenuBar(HWND hWnd, ULONG wParam, ULONG Key) +{ } +/* FUNCTIONS *****************************************************************/ + +/*static BOOL +MenuIsStringItem(ULONG TypeData) +{ + return((TypeData & MENU_TYPE_MASK) == MF_STRING); +}*/ + + +/* + * @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) { - MENUITEM* Item; - DWORD Ret; - - DPRINT("CheckMenuItem(hmenu 0x%X, uIDCheckItem %d, uCheck %d", - hmenu, uIDCheckItem, uCheck); - if ((Item = MenuFindItem(&hmenu, &uIDCheckItem, uCheck)) == NULL) - { - return(-1); - } - Ret = Item->State & MF_CHECKED; - if (uCheck & MF_CHECKED) - { - Item->State |= MF_CHECKED; - } - else - { - Item->State &= ~MF_CHECKED; - } - return(Ret); + return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck); } -WINBOOL -STDCALL -CheckMenuRadioItem( - HMENU hmenu, - UINT idFirst, - UINT idLast, - UINT idCheck, - UINT uFlags) + +/* + * @unimplemented + */ +WINBOOL STDCALL +CheckMenuRadioItem(HMENU hmenu, + UINT idFirst, + UINT idLast, + UINT idCheck, + UINT uFlags) { + UNIMPLEMENTED; return FALSE; } -HMENU -STDCALL + + +/* + * @implemented + */ +HMENU STDCALL CreateMenu(VOID) { - return (HMENU)0; + return NtUserCreateMenu(); } -HMENU -STDCALL + +/* + * @implemented + */ +HMENU STDCALL CreatePopupMenu(VOID) { - return (HMENU)0; + /* FIXME - add MF_POPUP style? */ + return NtUserCreateMenu(); } -WINBOOL -STDCALL -DeleteMenu( - HMENU hMenu, - UINT uPosition, - UINT uFlags) + + +/* + * @implemented + */ +WINBOOL STDCALL +DeleteMenu(HMENU hMenu, + UINT uPosition, + UINT uFlags) { - return FALSE; + return NtUserDeleteMenu(hMenu, uPosition, uFlags); } -WINBOOL -STDCALL -DestroyMenu( - HMENU hMenu) + + +/* + * @implemented + */ +WINBOOL STDCALL +DestroyMenu(HMENU hMenu) { - return FALSE; + return NtUserDestroyMenu(hMenu); } -WINBOOL -STDCALL -DrawMenuBar( - HWND hWnd) + + +/* + * @unimplemented + */ +WINBOOL STDCALL +DrawMenuBar(HWND hWnd) { + UNIMPLEMENTED + /* FIXME - return NtUserCallHwndLock(hWnd, 0x55); */ return FALSE; } -WINBOOL -STDCALL -EnableMenuItem( - HMENU hMenu, - UINT uIDEnableItem, - UINT uEnable) + + +/* + * @implemented + */ +UINT STDCALL +EnableMenuItem(HMENU hMenu, + UINT uIDEnableItem, + UINT uEnable) { - return FALSE; + return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable); } -WINBOOL -STDCALL + +/* + * @unimplemented + */ +WINBOOL STDCALL EndMenu(VOID) { + UNIMPLEMENTED; + /* FIXME - return NtUserEndMenu(); */ return FALSE; } -HMENU -STDCALL -GetMenu( - HWND hWnd) + + +/* + * @implemented + */ +HMENU STDCALL +GetMenu(HWND hWnd) { - return (HMENU)0; + return (HMENU)NtUserCallOneParam((DWORD)hWnd, ONEPARAM_ROUTINE_GETMENU); } -WINBOOL -STDCALL -GetMenuBarInfo( - HWND hwnd, - LONG idObject, - LONG idItem, - PMENUBARINFO pmbi) + +/* + * @unimplemented + */ +WINBOOL STDCALL +GetMenuBarInfo(HWND hwnd, + LONG idObject, + LONG idItem, + PMENUBARINFO pmbi) { + UNIMPLEMENTED; return FALSE; } -LONG -STDCALL + +/* + * @implemented + */ +LONG STDCALL GetMenuCheckMarkDimensions(VOID) { - return 0; + return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK), + GetSystemMetrics(SM_CYMENUCHECK))); } -UINT -STDCALL -GetMenuDefaultItem( - HMENU hMenu, - UINT fByPos, - UINT gmdiFlags) + +/* + * @implemented + */ +UINT STDCALL +GetMenuDefaultItem(HMENU hMenu, + UINT fByPos, + UINT gmdiFlags) { - return 0; + return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags); } -WINBOOL -STDCALL -GetMenuInfo( - HMENU hmenu, - LPCMENUINFO lpcmi) + +/* + * @implemented + */ +WINBOOL STDCALL +GetMenuInfo(HMENU hmenu, + LPMENUINFO lpcmi) { - return FALSE; + MENUINFO mi; + BOOL res = FALSE; + + if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) + return FALSE; + + RtlZeroMemory(&mi, sizeof(MENUINFO)); + mi.cbSize = sizeof(MENUINFO); + mi.fMask = lpcmi->fMask; + + res = NtUserMenuInfo(hmenu, &mi, FALSE); + + memcpy(lpcmi, &mi, sizeof(MENUINFO)); + return res; } -int -STDCALL -GetMenuItemCount( - HMENU hMenu) + +/* + * @implemented + */ +int STDCALL +GetMenuItemCount(HMENU hMenu) { - return 0; + return NtUserBuildMenuItemList(hMenu, NULL, 0, 0); } -UINT -STDCALL -GetMenuItemID( - HMENU hMenu, - int nPos) + +/* + * @implemented + */ +UINT STDCALL +GetMenuItemID(HMENU hMenu, + int nPos) { - return 0; + MENUITEMINFOW mii; + + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_ID | MIIM_SUBMENU; + + if(!NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE)) + { + return -1; + } + + if(mii.hSubMenu) return -1; + if(mii.wID == 0) return -1; + + return mii.wID; } -WINBOOL -STDCALL + +/* + * @unimplemented + */ +WINBOOL STDCALL GetMenuItemInfoA( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPMENUITEMINFO lpmii) + LPMENUITEMINFOA lpmii) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL GetMenuItemInfoW( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPMENUITEMINFO lpmii) + LPMENUITEMINFOW lpmii) { + UNIMPLEMENTED; return FALSE; } -WINBOOL -STDCALL -GetMenuItemRect( - HWND hWnd, - HMENU hMenu, - UINT uItem, - LPRECT lprcItem) + +/* + * @unimplemented + */ +WINBOOL STDCALL +GetMenuItemRect(HWND hWnd, + HMENU hMenu, + UINT uItem, + LPRECT lprcItem) { - return FALSE; + UNIMPLEMENTED; + return(FALSE); } + +/* + * @implemented + */ UINT STDCALL GetMenuState( @@ -323,9 +787,36 @@ GetMenuState( UINT uId, UINT uFlags) { - return 0; + MENUITEMINFOW mii; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU; + + SetLastError(0); + if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE)) + { + UINT nSubItems = 0; + if(mii.hSubMenu) + { + nSubItems = (UINT)NtUserBuildMenuItemList(mii.hSubMenu, NULL, 0, 0); + + /* FIXME - ported from wine, does that work (0xff)? */ + if(GetLastError() != ERROR_INVALID_MENU_HANDLE) + return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff); + + return (UINT)-1; /* Invalid submenu */ + } + + /* FIXME - ported from wine, does that work? */ + return (mii.fType | mii.fState); + } + + return (UINT)-1; } + +/* + * @unimplemented + */ int STDCALL GetMenuStringA( @@ -335,9 +826,14 @@ GetMenuStringA( int nMaxCount, UINT uFlag) { + UNIMPLEMENTED; return 0; } + +/* + * @unimplemented + */ int STDCALL GetMenuStringW( @@ -347,16 +843,34 @@ GetMenuStringW( int nMaxCount, UINT uFlag) { + UNIMPLEMENTED; return 0; } + + +/* + * @implemented + */ HMENU STDCALL GetSubMenu( HMENU hMenu, int nPos) { + MENUITEMINFOW mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_SUBMENU; + if(NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE)) + { + return mi.hSubMenu; + } return (HMENU)0; } + + +/* + * @implemented + */ WINBOOL STDCALL HiliteMenuItem( @@ -365,8 +879,13 @@ HiliteMenuItem( UINT uItemHilite, UINT uHilite) { - return FALSE; + return NtUserHiliteMenuItem(hwnd, hmenu, uItemHilite, uHilite); } + + +/* + * @implemented + */ WINBOOL STDCALL InsertMenuA( @@ -376,31 +895,134 @@ InsertMenuA( UINT_PTR uIDNewItem, LPCSTR lpNewItem) { - return FALSE; + MENUITEMINFOA mii; + mii.cbSize = sizeof(MENUITEMINFOA); + mii.fMask = MIIM_FTYPE | MIIM_STRING; + mii.fType = 0; + + if(uFlags & MF_BITMAP) + { + mii.fType |= MFT_BITMAP; + } + else if(uFlags & MF_OWNERDRAW) + { + mii.fType |= MFT_OWNERDRAW; + } + mii.dwTypeData = (LPSTR)lpNewItem; + if(uFlags & MF_POPUP) + { + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = (HMENU)uIDNewItem; + } + else + { + mii.fMask |= MIIM_ID; + mii.wID = (UINT)uIDNewItem; + } + return InsertMenuItemA(hMenu, uPosition, (WINBOOL)!(MF_BYPOSITION & uFlags), &mii); } + +/* + * @implemented + */ WINBOOL STDCALL InsertMenuItemA( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPCMENUITEMINFO lpmii) + LPCMENUITEMINFOA lpmii) { - return FALSE; + MENUITEMINFOW mi; + UNICODE_STRING MenuText; + WINBOOL res = FALSE; + BOOL CleanHeap = FALSE; + NTSTATUS Status; + + if((lpmii->cbSize == sizeof(MENUITEMINFOA)) || + (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP))) + { + RtlMoveMemory ( &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_strdupAtoW ( &mi.dwTypeData, (LPCSTR)mi.dwTypeData, &mi.cch ); + if (!NT_SUCCESS (Status)) + { + SetLastError (RtlNtStatusToDosError(Status)); + return FALSE; + } + RtlInitUnicodeString(&MenuText, (PWSTR)mi.dwTypeData); + mi.dwTypeData = (LPWSTR)&MenuText; + CleanHeap = TRUE; + } + + res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi); + + if ( CleanHeap ) HEAP_free ( mi.dwTypeData ); + } + return res; } + +/* + * @implemented + */ WINBOOL STDCALL InsertMenuItemW( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPCMENUITEMINFO lpmii) + LPCMENUITEMINFOW lpmii) { - return FALSE; + MENUITEMINFOW mi; + UNICODE_STRING MenuText; + WINBOOL res = FALSE; + BOOL CleanHeap = FALSE; + HANDLE hHeap = RtlGetProcessHeap(); + mi.hbmpItem = (HBITMAP)0; + + // 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 + + if((lpmii->cbSize == sizeof(MENUITEMINFOW)) || + (lpmii->cbSize == sizeof(MENUITEMINFOW) - 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) + { + if(lpmii->cch > 0) + { + if(!RtlCreateUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData)) + { + SetLastError (RtlNtStatusToDosError(STATUS_NO_MEMORY)); + return FALSE; + } + mi.dwTypeData = (LPWSTR)&MenuText; + mi.cch = MenuText.Length / sizeof(WCHAR); + CleanHeap = TRUE; + } + }; + + res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi); + + if(CleanHeap) RtlFreeHeap (hHeap, 0, mi.dwTypeData); + } + return res; } + +/* + * @implemented + */ WINBOOL STDCALL InsertMenuW( @@ -410,48 +1032,136 @@ InsertMenuW( UINT_PTR uIDNewItem, LPCWSTR lpNewItem) { - return FALSE; + MENUITEMINFOW mii; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_FTYPE | MIIM_STRING; + mii.fType = 0; + + if(uFlags & MF_BITMAP) + { + mii.fType |= MFT_BITMAP; + } + else if(uFlags & MF_OWNERDRAW) + { + mii.fType |= MFT_OWNERDRAW; + } + mii.dwTypeData = (LPWSTR)lpNewItem; + if(uFlags & MF_POPUP) + { + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = (HMENU)uIDNewItem; + } + else + { + mii.fMask |= MIIM_ID; + mii.wID = (UINT)uIDNewItem; + } + return InsertMenuItemW(hMenu, uPosition, (WINBOOL)!(MF_BYPOSITION & uFlags), &mii); } + + +/* + * @implemented + */ WINBOOL STDCALL IsMenu( HMENU hMenu) { - return FALSE; + DWORD ret; + SetLastError(ERROR_SUCCESS); + ret = NtUserBuildMenuItemList(hMenu, NULL, 0, 0); + return ((ret == (DWORD)-1) || (GetLastError() == ERROR_INVALID_MENU_HANDLE)); } -HMENU -STDCALL -LoadMenuA( - HINSTANCE hInstance, - LPCSTR lpMenuName) + + +/* + * @implemented + */ +HMENU STDCALL +LoadMenuA(HINSTANCE hInstance, + LPCSTR lpMenuName) { - return (HMENU)0; + HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4)); + if (Resource == NULL) + { + return(NULL); + } + return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource))); } -HMENU -STDCALL -LoadMenuIndirectA( - CONST MENUTEMPLATE *lpMenuTemplate) + +/* + * @implemented + */ +HMENU STDCALL +LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate) { - return (HMENU)0; + return(LoadMenuIndirectW(lpMenuTemplate)); } -HMENU -STDCALL -LoadMenuIndirectW( - CONST MENUTEMPLATE *lpMenuTemplate) + +/* + * @implemented + */ +HMENU STDCALL +LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate) { - return (HMENU)0; + 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; + } } -HMENU -STDCALL -LoadMenuW( - HINSTANCE hInstance, - LPCWSTR lpMenuName) + +/* + * @implemented + */ +HMENU STDCALL +LoadMenuW(HINSTANCE hInstance, + LPCWSTR lpMenuName) { - return (HMENU)0; + HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU); + if (Resource == NULL) + { + return(NULL); + } + return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource))); } + + +/* + * @unimplemented + */ int STDCALL MenuItemFromPoint( @@ -459,8 +1169,14 @@ MenuItemFromPoint( HMENU hMenu, POINT ptScreen) { + UNIMPLEMENTED; return 0; } + + +/* + * @unimplemented + */ WINBOOL STDCALL ModifyMenuA( @@ -470,9 +1186,14 @@ ModifyMenuA( UINT_PTR uIDNewItem, LPCSTR lpNewItem) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL ModifyMenuW( @@ -482,8 +1203,14 @@ ModifyMenuW( UINT_PTR uIDNewItem, LPCWSTR lpNewItem) { + UNIMPLEMENTED; return FALSE; } + + +/* + * @implemented + */ WINBOOL STDCALL RemoveMenu( @@ -491,17 +1218,24 @@ RemoveMenu( UINT uPosition, UINT uFlags) { - return FALSE; + return NtUserRemoveMenu(hMenu, uPosition, uFlags); } -WINBOOL -STDCALL -SetMenu( - HWND hWnd, - HMENU hMenu) + + +/* + * @implemented + */ +WINBOOL STDCALL +SetMenu(HWND hWnd, + HMENU hMenu) { - return FALSE; + return NtUserSetMenu(hWnd, hMenu, TRUE); } + +/* + * @implemented + */ WINBOOL STDCALL SetMenuDefaultItem( @@ -509,18 +1243,32 @@ SetMenuDefaultItem( UINT uItem, UINT fByPos) { - return FALSE; + return NtUserSetMenuDefaultItem(hMenu, uItem, fByPos); } + +/* + * @implemented + */ WINBOOL STDCALL SetMenuInfo( HMENU hmenu, LPCMENUINFO lpcmi) { - return FALSE; + MENUINFO mi; + BOOL res = FALSE; + if(lpcmi->cbSize != sizeof(MENUINFO)) + return res; + + memcpy(&mi, lpcmi, sizeof(MENUINFO)); + return NtUserMenuInfo(hmenu, &mi, TRUE); } + +/* + * @unimplemented + */ WINBOOL STDCALL SetMenuItemBitmaps( @@ -530,31 +1278,46 @@ SetMenuItemBitmaps( HBITMAP hBitmapUnchecked, HBITMAP hBitmapChecked) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL SetMenuItemInfoA( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPMENUITEMINFO lpmii) + LPMENUITEMINFOA lpmii) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL SetMenuItemInfoW( HMENU hMenu, UINT uItem, WINBOOL fByPosition, - LPMENUITEMINFO lpmii) + LPMENUITEMINFOW lpmii) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL TrackPopupMenu( @@ -566,9 +1329,14 @@ TrackPopupMenu( HWND hWnd, CONST RECT *prcRect) { + UNIMPLEMENTED; return FALSE; } + +/* + * @unimplemented + */ WINBOOL STDCALL TrackPopupMenuEx( @@ -579,5 +1347,38 @@ TrackPopupMenuEx( HWND hwnd, LPTPMPARAMS lptpm) { + UNIMPLEMENTED; return FALSE; } + + +/* + * @implemented + */ +WINBOOL +STDCALL +SetMenuContextHelpId(HMENU hmenu, + DWORD dwContextHelpId) +{ + return NtUserSetMenuContextHelpId(hmenu, dwContextHelpId); +} + + +/* + * @implemented + */ +DWORD +STDCALL +GetMenuContextHelpId(HMENU hmenu) +{ + MENUINFO mi; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_HELPID; + + if(NtUserMenuInfo(hmenu, &mi, FALSE)) + { + return mi.dwContextHelpID; + } + return 0; +} +