update for HEAD-2003091401
[reactos.git] / lib / user32 / windows / menu.c
index 586d827..c028e2c 100644 (file)
 #include <windows.h>
 #include <user32.h>
 #include <debug.h>
+#include <string.h>
+#include <draw.h>
+#include <window.h>
+#include <strpool.h>
+
+#include <user32/callback.h>
+#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;
+}
+