[PATCH 1/2] win32u: Move menu tracking implementation from user32.

Jacek Caban wine at gitlab.winehq.org
Thu Jun 16 06:15:43 CDT 2022


From: Jacek Caban <jacek at codeweavers.com>

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
---
 dlls/user32/controls.h       |    3 -
 dlls/user32/defwnd.c         |    3 +-
 dlls/user32/input.c          |   10 -
 dlls/user32/menu.c           | 3216 ++--------------------------------
 dlls/user32/nonclient.c      |   59 -
 dlls/user32/user32.spec      |    8 +-
 dlls/user32/user_main.c      |   16 +-
 dlls/user32/user_private.h   |    1 -
 dlls/win32u/defwnd.c         |   30 +-
 dlls/win32u/gdiobj.c         |    3 +
 dlls/win32u/menu.c           | 1816 ++++++++++++++++++-
 dlls/win32u/ntuser_private.h |    4 +-
 dlls/win32u/syscall.c        |    1 +
 dlls/win32u/sysparams.c      |    2 +-
 dlls/win32u/win32u.spec      |    8 +-
 dlls/win32u/win32u_private.h |   11 +
 dlls/win32u/window.c         |    6 +-
 dlls/win32u/wrappers.c       |   19 +
 dlls/wow64win/syscall.h      |    1 +
 dlls/wow64win/user.c         |    5 +
 include/ntuser.h             |    5 +-
 21 files changed, 2081 insertions(+), 3146 deletions(-)

diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h
index d4e8ccd7b08..4fb21c9f0e3 100644
--- a/dlls/user32/controls.h
+++ b/dlls/user32/controls.h
@@ -113,7 +113,6 @@ extern HBRUSH DEFWND_ControlColor( HDC hDC, UINT ctlType ) DECLSPEC_HIDDEN;
 extern BOOL update_wallpaper( const WCHAR *wallpaper, const WCHAR *pattern ) DECLSPEC_HIDDEN;
 
 /* menu controls */
-extern HWND MENU_IsMenuActive(void) DECLSPEC_HIDDEN;
 extern void MENU_TrackMouseMenuBar( HWND hwnd, INT ht, POINT pt ) DECLSPEC_HIDDEN;
 extern void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar ) DECLSPEC_HIDDEN;
 extern void MENU_EndMenu(HWND) DECLSPEC_HIDDEN;
@@ -126,8 +125,6 @@ extern LRESULT NC_HandleNCMouseLeave( HWND hwnd ) DECLSPEC_HIDDEN;
 extern LRESULT NC_HandleNCLButtonDblClk( HWND hwnd, WPARAM wParam, LPARAM lParam) DECLSPEC_HIDDEN;
 extern LRESULT NC_HandleSysCommand( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
 extern LRESULT NC_HandleSetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN;
-extern BOOL NC_DrawSysButton( HWND hwnd, HDC hdc, BOOL down ) DECLSPEC_HIDDEN;
-extern void NC_GetSysPopupPos( HWND hwnd, RECT* rect ) DECLSPEC_HIDDEN;
 
 /* scrollbar */
 
diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c
index a0d6ca44ecb..4c8748f10cb 100644
--- a/dlls/user32/defwnd.c
+++ b/dlls/user32/defwnd.c
@@ -359,8 +359,7 @@ static LRESULT DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
 
     case WM_CANCELMODE:
         iMenuSysKey = 0;
-        MENU_EndMenu( hwnd );
-        if (GetCapture() == hwnd) ReleaseCapture();
+        NtUserMessageCall( hwnd, msg, wParam, lParam, 0, NtUserDefWindowProc, FALSE );
         break;
 
     case WM_VKEYTOITEM:
diff --git a/dlls/user32/input.c b/dlls/user32/input.c
index 5fd5fe6d044..52d4414b673 100644
--- a/dlls/user32/input.c
+++ b/dlls/user32/input.c
@@ -86,16 +86,6 @@ static HKL get_locale_kbd_layout(void)
 }
 
 
-/**********************************************************************
- *		set_capture_window
- */
-BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret )
-{
-    /* FIXME: move callers to win32u or use NtUserSetCapture */
-    return NtUserCallHwndParam( hwnd, gui_flags, NtUserSetCaptureWindow );
-}
-
-
 /***********************************************************************
  *		keybd_event (USER32.@)
  */
diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c
index d0637a61809..74a43eaab1a 100644
--- a/dlls/user32/menu.c
+++ b/dlls/user32/menu.c
@@ -57,29 +57,6 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(menu);
 
-/* internal flags for menu tracking */
-
-#define TF_ENDMENU              0x10000
-#define TF_SUSPENDPOPUP         0x20000
-#define TF_SKIPREMOVE           0x40000
-#define TF_RCVD_BTN_UP          0x80000
-
-typedef struct
-{
-    UINT	trackFlags;
-    HMENU	hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
-    HMENU	hTopMenu;     /* initial menu */
-    HWND	hOwnerWnd;    /* where notifications are sent */
-    POINT	pt;
-} MTRACKER;
-
-#define ITEM_PREV		-1
-#define ITEM_NEXT		 1
-
-  /* Internal MENU_TrackMenu() flags */
-#define TPM_INTERNAL		0xF0000000
-#define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
-#define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
 
   /* Space between 2 columns */
 #define MENU_COL_SPACE 4
@@ -97,9 +74,6 @@ typedef struct
 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
 #define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
 
-#define IS_SYSTEM_MENU(menu)  \
-	(!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
-
 #define MENUITEMINFO_TYPE_MASK \
 		(MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
 		MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
@@ -108,22 +82,6 @@ typedef struct
 #define STATE_MASK (~TYPE_MASK)
 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
 
-static SIZE     menucharsize;
-static UINT     ODitemheight; /* default owner drawn item height */      
-
-/* Use global popup window because there's no way 2 menus can
- * be tracked at the same time.  */
-static HWND top_popup;
-static HMENU top_popup_hmenu;
-
-  /* Flag set by EndMenu() to force an exit from menu tracking */
-static BOOL fEndMenu = FALSE;
-
-static BOOL is_win_menu_disallowed(HWND hwnd)
-{
-    return (GetWindowLongW(hwnd, GWL_STYLE) & (WS_CHILD | WS_POPUP)) == WS_CHILD;
-}
-
 /*********************************************************************
  * menu class descriptor
  */
@@ -268,74 +226,6 @@ static void release_menu_ptr(POPUPMENU *menu)
     }
 }
 
-/***********************************************************************
- *           get_win_sys_menu
- *
- * Get the system menu of a window
- */
-static HMENU get_win_sys_menu( HWND hwnd )
-{
-    HMENU ret = 0;
-    WND *win = WIN_GetPtr( hwnd );
-    if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
-    {
-        ret = win->hSysMenu;
-        WIN_ReleasePtr( win );
-    }
-    return ret;
-}
-
-/***********************************************************************
- *           get_menu_font
- */
-static HFONT get_menu_font( BOOL bold )
-{
-    static HFONT hMenuFont, hMenuFontBold;
-
-    HFONT ret = bold ? hMenuFontBold : hMenuFont;
-
-    if (!ret)
-    {
-        NONCLIENTMETRICSW ncm;
-        HFONT prev;
-
-        ncm.cbSize = sizeof(NONCLIENTMETRICSW);
-        SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
-
-        if (bold)
-        {
-            ncm.lfMenuFont.lfWeight += 300;
-            if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
-        }
-        if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
-        prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
-                                                  ret, NULL );
-        if (prev)
-        {
-            /* another thread beat us to it */
-            DeleteObject( ret );
-            ret = prev;
-        }
-    }
-    return ret;
-}
-
-/***********************************************************************
- *           get_arrow_bitmap
- */
-static HBITMAP get_arrow_bitmap(void)
-{
-    static HBITMAP arrow_bitmap;
-
-    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
-    return arrow_bitmap;
-}
-
-static inline UINT get_scroll_arrow_height(const POPUPMENU *menu)
-{
-    return menucharsize.cy + 4;
-}
-
 /***********************************************************************
  *           MENU_CopySysPopup
  *
@@ -385,2832 +275,209 @@ static HMENU MENU_CopySysPopup(BOOL mdi)
  * However, the real system menu handle is sometimes seen in the
  * WM_MENUSELECT parameters (and Word 6 likes it this way).
  */
-HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
-{
-    HMENU hMenu;
-
-    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
-    if ((hMenu = CreateMenu()))
-    {
-	POPUPMENU *menu = MENU_GetMenu(hMenu);
-	menu->wFlags = MF_SYSMENU;
-	menu->hWnd = WIN_GetFullHandle( hWnd );
-	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
-
-	if (!hPopupMenu)
-        {
-            if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
-	        hPopupMenu = MENU_CopySysPopup(TRUE);
-            else
-	        hPopupMenu = MENU_CopySysPopup(FALSE);
-        }
-
-	if (hPopupMenu)
-	{
-            if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
-                NtUserDeleteMenu( hPopupMenu, SC_CLOSE, MF_BYCOMMAND );
-
-	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
-                         (UINT_PTR)hPopupMenu, NULL );
-
-            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
-            menu->items[0].fState = 0;
-            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
-
-	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
-	    return hMenu;
-	}
-	NtUserDestroyMenu( hMenu );
-    }
-    ERR("failed to load system menu!\n");
-    return 0;
-}
-
-
-/***********************************************************************
- *           MENU_InitSysMenuPopup
- *
- * Grey the appropriate items in System menu.
- */
-static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
-{
-    BOOL gray;
-
-    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
-    NtUserEnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
-    gray = ((style & WS_MAXIMIZE) != 0);
-    NtUserEnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
-    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
-    NtUserEnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
-    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
-    NtUserEnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
-    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
-    NtUserEnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
-    gray = (clsStyle & CS_NOCLOSE) != 0;
-
-    /* The menu item must keep its state if it's disabled */
-    if(gray)
-	NtUserEnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
-}
-
-
-/******************************************************************************
- *
- *   UINT  MENU_GetStartOfNextColumn(
- *     HMENU  hMenu )
- *
- *****************************************************************************/
-
-static UINT  MENU_GetStartOfNextColumn(
-    HMENU  hMenu )
-{
-    POPUPMENU *menu = MENU_GetMenu(hMenu);
-    UINT i;
-
-    if(!menu)
-	return NO_SELECTED_ITEM;
-
-    i = menu->FocusedItem + 1;
-    if( i == NO_SELECTED_ITEM )
-	return i;
-
-    for( ; i < menu->nItems; ++i ) {
-	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
-	    return i;
-    }
-
-    return NO_SELECTED_ITEM;
-}
-
-
-/******************************************************************************
- *
- *   UINT  MENU_GetStartOfPrevColumn(
- *     HMENU  hMenu )
- *
- *****************************************************************************/
-
-static UINT  MENU_GetStartOfPrevColumn(
-    HMENU  hMenu )
-{
-    POPUPMENU *menu = MENU_GetMenu(hMenu);
-    UINT  i;
-
-    if( !menu )
-	return NO_SELECTED_ITEM;
-
-    if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
-	return NO_SELECTED_ITEM;
-
-    /* Find the start of the column */
-
-    for(i = menu->FocusedItem; i != 0 &&
-	 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
-	--i); /* empty */
-
-    if(i == 0)
-	return NO_SELECTED_ITEM;
-
-    for(--i; i != 0; --i) {
-	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
-	    break;
-    }
-
-    TRACE("ret %d.\n", i );
-
-    return i;
-}
-
-static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
-{
-    UINT fallback_pos = ~0u, i;
-    POPUPMENU *menu;
-
-    menu = grab_menu_ptr(hmenu);
-    if (!menu)
-        return NULL;
-
-    if (flags & MF_BYPOSITION)
-    {
-        if (id >= menu->nItems)
-        {
-            release_menu_ptr(menu);
-            return NULL;
-        }
-
-        if (pos) *pos = id;
-        return menu;
-    }
-    else
-    {
-        MENUITEM *item = menu->items;
-	for (i = 0; i < menu->nItems; i++, item++)
-	{
-	    if (item->fType & MF_POPUP)
-	    {
-                POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
-
-                if (submenu)
-                {
-                    release_menu_ptr(menu);
-                    return submenu;
-                }
-                else if (item->wID == id)
-		{
-		    /* fallback to this item if nothing else found */
-		    fallback_pos = i;
-		}
-	    }
-	    else if (item->wID == id)
-	    {
-                if (pos) *pos = i;
-                return menu;
-	    }
-	}
-    }
-
-    if (fallback_pos != ~0u)
-        *pos = fallback_pos;
-    else
-    {
-        release_menu_ptr(menu);
-        menu = NULL;
-    }
-
-    return menu;
-}
-
-/***********************************************************************
- *           MENU_FindSubMenu
- *
- * Find a Sub menu. Return the position of the submenu, and modifies
- * *hmenu in case it is found in another sub-menu.
- * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
- */
-static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
-{
-    POPUPMENU *menu;
-    UINT i;
-    MENUITEM *item;
-    if (((*hmenu)==(HMENU)0xffff) ||
-            (!(menu = MENU_GetMenu(*hmenu))))
-        return NO_SELECTED_ITEM;
-    item = menu->items;
-    for (i = 0; i < menu->nItems; i++, item++) {
-        if(!(item->fType & MF_POPUP)) continue;
-        if (item->hSubMenu == hSubTarget) {
-            return i;
-        }
-        else  {
-            HMENU hsubmenu = item->hSubMenu;
-            UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
-            if (pos != NO_SELECTED_ITEM) {
-                *hmenu = hsubmenu;
-                return pos;
-            }
-        }
-    }
-    return NO_SELECTED_ITEM;
-}
-
-/***********************************************************************
- *           MENU_AdjustMenuItemRect
- *
- * Adjust menu item rectangle according to scrolling state.
- */
-static void
-MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
-{
-    INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
-
-    OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
-}
-
-enum hittest
-{
-    ht_nowhere,     /* outside the menu */
-    ht_border,      /* anywhere that's not an item or a scroll arrow */
-    ht_item,        /* a menu item */
-    ht_scroll_up,   /* scroll up arrow */
-    ht_scroll_down  /* scroll down arrow */
-};
-
-/***********************************************************************
- *           MENU_FindItemByCoords
- *
- * Find the item at the specified coordinates (screen coords). Does
- * not work for child windows and therefore should not be called for
- * an arbitrary system menu.
- *
- * Returns a hittest code.  *pos will contain the position of the
- * item or NO_SELECTED_ITEM.  If the hittest code is ht_scroll_up
- * or ht_scroll_down then *pos will contain the position of the
- * item that's just outside the items_rect - ie, the one that would
- * be scrolled completely into view.
- */
-static enum hittest MENU_FindItemByCoords( const POPUPMENU *menu, POINT pt, UINT *pos )
-{
-    MENUITEM *item;
-    UINT i;
-    RECT rect;
-    enum hittest ht = ht_border;
-
-    *pos = NO_SELECTED_ITEM;
-
-    if (!GetWindowRect(menu->hWnd, &rect) || !PtInRect(&rect, pt))
-        return ht_nowhere;
-
-    if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
-    else pt.x -= rect.left;
-    pt.y -= rect.top;
-
-    if (!PtInRect(&menu->items_rect, pt))
-    {
-        if (!menu->bScrolling || pt.x < menu->items_rect.left || pt.x >= menu->items_rect.right)
-            return ht_border;
-
-        /* On a scroll arrow.  Update pt so that it points to the item just outside items_rect */
-        if (pt.y < menu->items_rect.top)
-        {
-            ht = ht_scroll_up;
-            pt.y = menu->items_rect.top - 1;
-        }
-        else
-        {
-            ht = ht_scroll_down;
-            pt.y = menu->items_rect.bottom;
-        }
-    }
-
-    item = menu->items;
-    for (i = 0; i < menu->nItems; i++, item++)
-    {
-        rect = item->rect;
-        MENU_AdjustMenuItemRect(menu, &rect);
-        if (PtInRect(&rect, pt))
-        {
-            *pos = i;
-            if (ht != ht_scroll_up && ht != ht_scroll_down) ht = ht_item;
-            break;
-        }
-    }
-
-    return ht;
-}
-
-
-/***********************************************************************
- *           MENU_FindItemByKey
- *
- * Find the menu item selected by a key press.
- * Return item id, -1 if none, -2 if we should close the menu.
- */
-static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
-                                WCHAR key, BOOL forceMenuChar )
-{
-    TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
-
-    if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
-
-    if (hmenu)
-    {
-	POPUPMENU *menu = MENU_GetMenu( hmenu );
-	MENUITEM *item = menu->items;
-	LRESULT menuchar;
-
-	if( !forceMenuChar )
-	{
-	     UINT i;
-	     BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
-
-	     for (i = 0; i < menu->nItems; i++, item++)
-	     {
-		if( item->text)
-		{
-		    const WCHAR *p = item->text - 2;
-		    do
-		    {
-			const WCHAR *q = p + 2;
-			p = wcschr (q, '&');
-			if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
-		    }
-		    while (p != NULL && p [1] == '&');
-		    if (p && (towupper(p[1]) == towupper(key))) return i;
-		}
-	     }
-	}
-	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
-                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
-	if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
-	if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
-    }
-    return (UINT)(-1);
-}
-
-
-/***********************************************************************
- *           MENU_GetBitmapItemSize
- *
- * Get the size of a bitmap item.
- */
-static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
-                                    HWND hwndOwner)
-{
-    BITMAP bm;
-    HBITMAP bmp = lpitem->hbmpItem;
-
-    size->cx = size->cy = 0;
-
-    /* check if there is a magic menu item associated with this item */
-    switch( (INT_PTR) bmp )
-    {
-    case (INT_PTR)HBMMENU_CALLBACK:
-        {
-            MEASUREITEMSTRUCT measItem;
-            measItem.CtlType = ODT_MENU;
-            measItem.CtlID = 0;
-            measItem.itemID = lpitem->wID;
-            measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
-            measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
-            measItem.itemData = lpitem->dwItemData;
-            SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
-            size->cx = measItem.itemWidth;
-            size->cy = measItem.itemHeight;
-            return;
-        }
-        break;
-    case (INT_PTR)HBMMENU_SYSTEM:
-        if (lpitem->dwItemData)
-        {
-            bmp = (HBITMAP)lpitem->dwItemData;
-            break;
-        }
-        /* fall through */
-    case (INT_PTR)HBMMENU_MBAR_RESTORE:
-    case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
-    case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
-    case (INT_PTR)HBMMENU_MBAR_CLOSE:
-    case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
-        size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
-        size->cy = size->cx;
-        return;
-    case (INT_PTR)HBMMENU_POPUP_CLOSE:
-    case (INT_PTR)HBMMENU_POPUP_RESTORE:
-    case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
-    case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
-        size->cx = GetSystemMetrics( SM_CXMENUSIZE);
-        size->cy = GetSystemMetrics( SM_CYMENUSIZE);
-        return;
-    }
-    if (GetObjectW(bmp, sizeof(bm), &bm ))
-    {
-        size->cx = bm.bmWidth;
-        size->cy = bm.bmHeight;
-    }
-}
-
-/***********************************************************************
- *           MENU_DrawBitmapItem
- *
- * Draw a bitmap item.
- */
-static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
-                                 POPUPMENU *menu, HWND hwndOwner, UINT odaction )
-{
-    BITMAP bm;
-    DWORD rop;
-    HDC hdcMem;
-    HBITMAP bmp;
-    int w = rect->right - rect->left;
-    int h = rect->bottom - rect->top;
-    int bmp_xoffset = 0;
-    int left, top;
-    HBITMAP hbmToDraw = lpitem->hbmpItem;
-    bmp = hbmToDraw;
-
-    /* Check if there is a magic menu item associated with this item */
-    if (IS_MAGIC_BITMAP(hbmToDraw))
-    {
-        UINT flags = 0;
-        WCHAR bmchr = 0;
-        RECT r;
-
-	switch((INT_PTR)hbmToDraw)
-        {
-        case (INT_PTR)HBMMENU_SYSTEM:
-            if (lpitem->dwItemData)
-            {
-                bmp = (HBITMAP)lpitem->dwItemData;
-                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
-            }
-            else
-            {
-                static HBITMAP hBmpSysMenu;
-
-                if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
-                bmp = hBmpSysMenu;
-                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
-                /* only use right half of the bitmap */
-                bmp_xoffset = bm.bmWidth / 2;
-                bm.bmWidth -= bmp_xoffset;
-            }
-            goto got_bitmap;
-        case (INT_PTR)HBMMENU_MBAR_RESTORE:
-            flags = DFCS_CAPTIONRESTORE;
-            break;
-        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
-            flags = DFCS_CAPTIONMIN;
-            break;
-        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
-            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
-            break;
-        case (INT_PTR)HBMMENU_MBAR_CLOSE:
-            flags = DFCS_CAPTIONCLOSE;
-            break;
-        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
-            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
-            break;
-        case (INT_PTR)HBMMENU_CALLBACK:
-            {
-                DRAWITEMSTRUCT drawItem;
-                drawItem.CtlType = ODT_MENU;
-                drawItem.CtlID = 0;
-                drawItem.itemID = lpitem->wID;
-                drawItem.itemAction = odaction;
-                drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
-                drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
-                drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
-                drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
-                drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
-                drawItem.hwndItem = (HWND)menu->obj.handle;
-                drawItem.hDC = hdc;
-                drawItem.itemData = lpitem->dwItemData;
-                drawItem.rcItem = *rect;
-                SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
-                return;
-            }
-            break;
-        case (INT_PTR)HBMMENU_POPUP_CLOSE:
-            bmchr = 0x72;
-            break;
-        case (INT_PTR)HBMMENU_POPUP_RESTORE:
-            bmchr = 0x32;
-            break;
-        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
-            bmchr = 0x31;
-            break;
-        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
-            bmchr = 0x30;
-            break;
-        default:
-            FIXME("Magic %p not implemented\n", hbmToDraw);
-            return;
-        }
-        if (bmchr)
-        {
-            /* draw the magic bitmaps using marlett font characters */
-            /* FIXME: fontsize and the position (x,y) could probably be better */
-            HFONT hfont, hfontsav;
-            LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, L"Marlett" };
-            logfont.lfHeight =  min( h, w) - 5 ;
-            TRACE(" height %ld rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
-            hfont = CreateFontIndirectW( &logfont);
-            hfontsav = SelectObject(hdc, hfont);
-            TextOutW( hdc,  rect->left, rect->top + 2, &bmchr, 1);
-            SelectObject(hdc, hfontsav);
-            DeleteObject( hfont);
-        }
-        else
-        {
-            r = *rect;
-            InflateRect( &r, -1, -1 );
-            if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
-            DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
-        }
-        return;
-    }
-
-    if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
-
- got_bitmap:
-    hdcMem = CreateCompatibleDC( hdc );
-    SelectObject( hdcMem, bmp );
-
-    /* handle fontsize > bitmap_height */
-    top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
-    left=rect->left;
-    rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
-    if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
-        SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
-    BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
-    DeleteDC( hdcMem );
-}
-
-
-/***********************************************************************
- *           MENU_CalcItemSize
- *
- * Calculate the size of the menu item and store it in lpitem->rect.
- */
-static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
-			       INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
-{
-    WCHAR *p;
-    UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
-    UINT arrow_bitmap_width;
-    BITMAP bm;
-    INT itemheight;
-
-    TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
-    debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
-			 (menuBar ? " (MenuBar)" : ""));
-
-    GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
-    arrow_bitmap_width = bm.bmWidth;
-
-    /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
-    if( !menucharsize.cx ) {
-        menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
-        /* Win95/98/ME will use menucharsize.cy here. Testing is possible
-         * but it is unlikely an application will depend on that */
-        ODitemheight = HIWORD( GetDialogBaseUnits());
-    }
-
-    SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
-
-    if (lpitem->fType & MF_OWNERDRAW)
-    {
-        MEASUREITEMSTRUCT mis;
-        mis.CtlType    = ODT_MENU;
-        mis.CtlID      = 0;
-        mis.itemID     = lpitem->wID;
-        mis.itemData   = lpitem->dwItemData;
-        mis.itemHeight = ODitemheight;
-        mis.itemWidth  = 0;
-        SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
-        /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
-         * width of a menufont character to the width of an owner-drawn menu. 
-         */
-        lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
-        if (menuBar) {
-            /* under at least win95 you seem to be given a standard
-               height for the menu and the height value is ignored */
-            lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
-        } else
-            lpitem->rect.bottom += mis.itemHeight;
-
-        TRACE("id=%04Ix size=%ldx%ld\n",
-                lpitem->wID, lpitem->rect.right-lpitem->rect.left,
-                lpitem->rect.bottom-lpitem->rect.top);
-        return;
-    }
-
-    if (lpitem->fType & MF_SEPARATOR)
-    {
-        lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
-        if( !menuBar)
-            lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
-        return;
-    }
-
-    itemheight = 0;
-    lpitem->xTab = 0;
-
-    if (!menuBar) {
-        if (lpitem->hbmpItem) {
-            SIZE size;
-
-            MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
-            /* Keep the size of the bitmap in callback mode to be able
-             * to draw it correctly */
-            lpitem->bmpsize = size;
-            lppop->textOffset = max( lppop->textOffset, size.cx);
-            lpitem->rect.right += size.cx + 2;
-            itemheight = size.cy + 2;
-        }
-        if( !(lppop->dwStyle & MNS_NOCHECK))
-            lpitem->rect.right += check_bitmap_width; 
-        lpitem->rect.right += 4 + menucharsize.cx;
-        lpitem->xTab = lpitem->rect.right;
-        lpitem->rect.right += arrow_bitmap_width;
-    } else if (lpitem->hbmpItem) { /* menuBar */
-        SIZE size;
-
-        MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
-        lpitem->bmpsize = size;
-        lpitem->rect.right  += size.cx;
-        if( lpitem->text) lpitem->rect.right  += 2;
-        itemheight = size.cy;
-    }
-
-    /* it must be a text item - unless it's the system menu */
-    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
-        HFONT hfontOld = NULL;
-        RECT rc = lpitem->rect;
-        LONG txtheight, txtwidth;
-
-	if ( lpitem->fState & MFS_DEFAULT ) {
-	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
-	}
-        if (menuBar) {
-            txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
-                    DT_SINGLELINE|DT_CALCRECT); 
-            lpitem->rect.right  += rc.right - rc.left;
-            itemheight = max( max( itemheight, txtheight),
-                    GetSystemMetrics( SM_CYMENU) - 1);
-            lpitem->rect.right +=  2 * menucharsize.cx;
-        } else {
-            if ((p = wcschr( lpitem->text, '\t' )) != NULL) {
-                RECT tmprc = rc;
-                LONG tmpheight;
-                int n = (int)( p - lpitem->text);
-                /* Item contains a tab (only meaningful in popup menus) */
-                /* get text size before the tab */
-                txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
-                        DT_SINGLELINE|DT_CALCRECT);
-                txtwidth = rc.right - rc.left;
-                p += 1; /* advance past the Tab */
-                /* get text size after the tab */
-                tmpheight = DrawTextW( hdc, p, -1, &tmprc,
-                        DT_SINGLELINE|DT_CALCRECT);
-                lpitem->xTab += txtwidth;
-                txtheight = max( txtheight, tmpheight);
-                txtwidth += menucharsize.cx + /* space for the tab */
-                    tmprc.right - tmprc.left; /* space for the short cut */
-            } else {
-                txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
-                        DT_SINGLELINE|DT_CALCRECT);
-                txtwidth = rc.right - rc.left;
-                lpitem->xTab += txtwidth;
-            }
-            lpitem->rect.right  += 2 + txtwidth;
-            itemheight = max( itemheight,
-                    max( txtheight + 2, menucharsize.cy + 4));
-        }
-	if (hfontOld) SelectObject (hdc, hfontOld);
-    } else if( menuBar) {
-        itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
-    }
-    lpitem->rect.bottom += itemheight;
-    TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
-}
-
-/***********************************************************************
- *           MENU_PopupMenuCalcSize
- *
- * Calculate the size of a popup menu.
- */
-static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
-{
-    MENUITEM *lpitem;
-    HDC hdc;
-    UINT start, i;
-    BOOL textandbmp = FALSE, multi_col = FALSE;
-    int orgX, orgY, maxTab, maxTabWidth;
-
-    lppop->Width = lppop->Height = 0;
-    SetRectEmpty(&lppop->items_rect);
-
-    if (lppop->nItems == 0) return;
-    hdc = GetDC( 0 );
-
-    SelectObject( hdc, get_menu_font(FALSE));
-
-    start = 0;
-
-    lppop->textOffset = 0;
-
-    while (start < lppop->nItems)
-    {
-	lpitem = &lppop->items[start];
-	orgX = lppop->items_rect.right;
-        if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
-            orgX += MENU_COL_SPACE; 
-	orgY = lppop->items_rect.top;
-
-	maxTab = maxTabWidth = 0;
-	  /* Parse items until column break or end of menu */
-	for (i = start; i < lppop->nItems; i++, lpitem++)
-	{
-            if (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
-            {
-                multi_col = TRUE;
-                if (i != start) break;
-            }
-
-	    MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
-	    lppop->items_rect.right = max( lppop->items_rect.right, lpitem->rect.right );
-	    orgY = lpitem->rect.bottom;
-	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
-	    {
-		maxTab = max( maxTab, lpitem->xTab );
-		maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
-	    }
-            if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
-	}
-
-	  /* Finish the column (set all items to the largest width found) */
-	lppop->items_rect.right = max( lppop->items_rect.right, maxTab + maxTabWidth );
-	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
-	{
-	    lpitem->rect.right = lppop->items_rect.right;
-	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
-		lpitem->xTab = maxTab;
-
-	}
-	lppop->items_rect.bottom = max( lppop->items_rect.bottom, orgY );
-    }
-
-    /* if none of the items have both text and bitmap then
-     * the text and bitmaps are all aligned on the left. If there is at
-     * least one item with both text and bitmap then bitmaps are
-     * on the left and texts left aligned with the right hand side
-     * of the bitmaps */
-    if( !textandbmp) lppop->textOffset = 0;
-
-    lppop->nTotalHeight = lppop->items_rect.bottom;
-
-    /* space for the border */
-    OffsetRect(&lppop->items_rect, MENU_MARGIN, MENU_MARGIN);
-    lppop->Height = lppop->items_rect.bottom + MENU_MARGIN;
-    lppop->Width = lppop->items_rect.right + MENU_MARGIN;
-
-    /* Adjust popup height if it exceeds maximum */
-    if (lppop->Height >= max_height)
-    {
-        lppop->Height = max_height;
-        lppop->bScrolling = !multi_col;
-        /* When the scroll arrows are present, don't add the top/bottom margin as well */
-        if (lppop->bScrolling)
-        {
-            lppop->items_rect.top = get_scroll_arrow_height(lppop);
-            lppop->items_rect.bottom = lppop->Height - get_scroll_arrow_height(lppop);
-        }
-    }
-    else
-    {
-        lppop->bScrolling = FALSE;
-    }
-
-    NtUserReleaseDC( 0, hdc );
-}
-
-
-/***********************************************************************
- *           draw_popup_arrow
- *
- * Draws the popup-menu arrow.
- */
-static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
-        UINT arrow_bitmap_height)
-{
-    HDC hdcMem = CreateCompatibleDC( hdc );
-    HBITMAP hOrigBitmap;
-
-    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
-    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
-            (rect.top + rect.bottom - arrow_bitmap_height) / 2,
-            arrow_bitmap_width, arrow_bitmap_height,
-            hdcMem, 0, 0, SRCCOPY );
-    SelectObject( hdcMem, hOrigBitmap );
-    DeleteDC( hdcMem );
-}
-/***********************************************************************
- *           MENU_DrawMenuItem
- *
- * Draw a single menu item.
- */
-static void MENU_DrawMenuItem( HWND hwnd, POPUPMENU *menu, HWND hwndOwner, HDC hdc,
-                               MENUITEM *lpitem, BOOL menuBar, UINT odaction )
-{
-    RECT rect, bmprc;
-    BOOL flat_menu = FALSE;
-    int bkgnd;
-    UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
-    HRGN old_clip = NULL, clip;
-
-    debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
-
-    if (!menuBar) {
-        BITMAP bmp;
-        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
-        arrow_bitmap_width = bmp.bmWidth;
-        arrow_bitmap_height = bmp.bmHeight;
-    }
-
-    if (lpitem->fType & MF_SYSMENU)
-    {
-	if( !IsIconic(hwnd) )
-	    NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
-	return;
-    }
-
-    TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem->rect ) );
-    rect = lpitem->rect;
-    MENU_AdjustMenuItemRect( menu, &rect );
-    if (!IntersectRect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
-        return;
-
-    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
-    bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
-  
-      /* Setup colors */
-
-    if (lpitem->fState & MF_HILITE)
-    {
-        if(menuBar && !flat_menu) {
-	    SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
-            SetBkColor(hdc, GetSysColor(COLOR_MENU));
-	} else {
-	    if(lpitem->fState & MF_GRAYED)
-		SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
-	    else
-		SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
-            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
-	}
-    }
-    else
-    {
-	if (lpitem->fState & MF_GRAYED)
-	    SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
-	else
-	    SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
-	SetBkColor( hdc, GetSysColor( bkgnd ) );
-    }
-
-    old_clip = CreateRectRgn( 0, 0, 0, 0 );
-    if (GetClipRgn( hdc, old_clip ) <= 0)
-    {
-        DeleteObject( old_clip );
-        old_clip = NULL;
-    }
-    clip = CreateRectRgnIndirect( &menu->items_rect );
-    ExtSelectClipRgn( hdc, clip, RGN_AND );
-    DeleteObject( clip );
-
-    if (lpitem->fType & MF_OWNERDRAW)
-    {
-        /*
-        ** Experimentation under Windows reveals that an owner-drawn
-        ** menu is given the rectangle which includes the space it requested
-        ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
-        ** and a popup-menu arrow.  This is the value of lpitem->rect.
-        ** Windows will leave all drawing to the application except for
-        ** the popup-menu arrow.  Windows always draws that itself, after
-        ** the menu owner has finished drawing.
-        */
-        DRAWITEMSTRUCT dis;
-        COLORREF old_bk, old_text;
-
-        dis.CtlType   = ODT_MENU;
-	dis.CtlID     = 0;
-        dis.itemID    = lpitem->wID;
-        dis.itemData  = lpitem->dwItemData;
-        dis.itemState = 0;
-        if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
-        if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
-        if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
-        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
-        dis.hwndItem   = (HWND)menu->obj.handle;
-        dis.hDC        = hdc;
-        dis.rcItem     = rect;
-        TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
-	      "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
-	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
-	      dis.hDC, wine_dbgstr_rect( &dis.rcItem));
-        old_bk = GetBkColor( hdc );
-        old_text = GetTextColor( hdc );
-        SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
-        /* Draw the popup-menu arrow */
-        SetBkColor( hdc, old_bk );
-        SetTextColor( hdc, old_text );
-        if (lpitem->fType & MF_POPUP)
-            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
-                    arrow_bitmap_height);
-        goto done;
-    }
-
-    if (menuBar && (lpitem->fType & MF_SEPARATOR)) goto done;
-
-    if (lpitem->fState & MF_HILITE)
-    {
-        if (flat_menu)
-        {
-            InflateRect (&rect, -1, -1);
-            FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
-            InflateRect (&rect, 1, 1);
-            FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
-        }
-        else
-        {
-            if(menuBar)
-                DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
-            else
-                FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
-        }
-    }
-    else
-        FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
-
-    SetBkMode( hdc, TRANSPARENT );
-
-    /* vertical separator */
-    if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
-    {
-        HPEN oldPen;
-        RECT rc = rect;
-
-        rc.left -= MENU_COL_SPACE / 2 + 1;
-        rc.top = 3;
-        rc.bottom = menu->Height - 3;
-        if (flat_menu)
-        {
-            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
-            MoveToEx( hdc, rc.left, rc.top, NULL );
-            LineTo( hdc, rc.left, rc.bottom );
-            SelectObject( hdc, oldPen );
-        }
-        else
-            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
-    }
-
-    /* horizontal separator */
-    if (lpitem->fType & MF_SEPARATOR)
-    {
-        HPEN oldPen;
-        RECT rc = rect;
-
-        InflateRect( &rc, -1, 0 );
-        rc.top = ( rc.top + rc.bottom) / 2;
-        if (flat_menu)
-        {
-            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
-            MoveToEx( hdc, rc.left, rc.top, NULL );
-            LineTo( hdc, rc.right, rc.top );
-            SelectObject( hdc, oldPen );
-        }
-        else
-            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
-        goto done;
-    }
-
-	/* helper lines for debugging */
-/*	FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
-	SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
-	MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
-	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
-*/
-
-    if (lpitem->hbmpItem) {
-        /* calculate the bitmap rectangle in coordinates relative
-         * to the item rectangle */
-        if( menuBar) {
-            if( lpitem->hbmpItem == HBMMENU_CALLBACK)
-                bmprc.left = 3;
-            else 
-                bmprc.left = lpitem->text ? menucharsize.cx : 0;          
-        }
-        else if (menu->dwStyle & MNS_NOCHECK)
-            bmprc.left = 4;
-        else if (menu->dwStyle & MNS_CHECKORBMP)
-            bmprc.left = 2;
-        else
-            bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
-        bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
-        if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
-            bmprc.top = 0;
-        else
-            bmprc.top = (rect.bottom - rect.top -
-                    lpitem->bmpsize.cy) / 2; 
-        bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
-    }
-
-    if (!menuBar)
-    {
-        HBITMAP bm;
-        INT y = rect.top + rect.bottom;
-        BOOL checked = FALSE;
-        UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
-        UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
-        /* Draw the check mark
-         *
-         * FIXME:
-         * Custom checkmark bitmaps are monochrome but not always 1bpp.
-         */
-        if (!(menu->dwStyle & MNS_NOCHECK))
-        {
-            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
-                lpitem->hUnCheckBit;
-            if (bm)  /* we have a custom bitmap */
-            {
-                HDC hdcMem = CreateCompatibleDC( hdc );
-
-                SelectObject( hdcMem, bm );
-                BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
-                        check_bitmap_width, check_bitmap_height,
-                        hdcMem, 0, 0, SRCCOPY );
-                DeleteDC( hdcMem );
-                checked = TRUE;
-            }
-            else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
-            {
-                RECT r;
-                HBITMAP bm = CreateBitmap( check_bitmap_width,
-                        check_bitmap_height, 1, 1, NULL );
-                HDC hdcMem = CreateCompatibleDC( hdc );
-
-                SelectObject( hdcMem, bm );
-                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
-                DrawFrameControl( hdcMem, &r, DFC_MENU,
-                        (lpitem->fType & MFT_RADIOCHECK) ?
-                        DFCS_MENUBULLET : DFCS_MENUCHECK );
-                BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
-                        hdcMem, 0, 0, SRCCOPY );
-                DeleteDC( hdcMem );
-                DeleteObject( bm );
-                checked = TRUE;
-            }
-        }
-        if (lpitem->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
-        {
-            POINT origorg;
-            /* some applications make this assumption on the DC's origin */
-            SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
-            MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
-            SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
-        }
-	/* Draw the popup-menu arrow */
-        if (lpitem->fType & MF_POPUP)
-            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
-                    arrow_bitmap_height);
-	rect.left += 4;
-        if( !(menu->dwStyle & MNS_NOCHECK))
-            rect.left += check_bitmap_width;
-	rect.right -= arrow_bitmap_width;
-    }
-    else if (lpitem->hbmpItem)
-    {   /* Draw the bitmap */
-        POINT origorg;
-
-        SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
-        MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
-        SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
-    }
-    /* process text if present */
-    if (lpitem->text)
-    {
-	int i;
-	HFONT hfontOld = 0;
-
-	UINT uFormat = (menuBar) ?
-			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
-			DT_LEFT | DT_VCENTER | DT_SINGLELINE;
-
-        if( !(menu->dwStyle & MNS_CHECKORBMP))
-            rect.left += menu->textOffset;
-
-	if ( lpitem->fState & MFS_DEFAULT )
-	{
-	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
-	}
-
-	if (menuBar) {
-            if( lpitem->hbmpItem)
-                rect.left += lpitem->bmpsize.cx;
-            if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
-                rect.left += menucharsize.cx;
-            rect.right -= menucharsize.cx;
-	}
-
-	for (i = 0; lpitem->text[i]; i++)
-	    if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
-	        break;
-
-	if(lpitem->fState & MF_GRAYED)
-	{
-	    if (!(lpitem->fState & MF_HILITE) )
-	    {
-		++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
-		SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
-		DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
-		--rect.left; --rect.top; --rect.right; --rect.bottom;
-	    }
-	    SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
-	}
-
-	DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
-
-	/* paint the shortcut text */
-	if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
-	{
-	    if (lpitem->text[i] == '\t')
-	    {
-		rect.left = lpitem->xTab;
-		uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
-	    }
-	    else
-	    {
-		rect.right = lpitem->xTab;
-		uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
-	    }
-
-	    if(lpitem->fState & MF_GRAYED)
-	    {
-		if (!(lpitem->fState & MF_HILITE) )
-		{
-		    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
-		    SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
-		    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
-		    --rect.left; --rect.top; --rect.right; --rect.bottom;
-		}
-		SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
-	    }
-	    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
-	}
-
-	if (hfontOld)
-	    SelectObject (hdc, hfontOld);
-    }
-
-done:
-    ExtSelectClipRgn( hdc, old_clip, RGN_COPY );
-    if (old_clip) DeleteObject( old_clip );
-}
-
-
-/***********************************************************************
- *           MENU_InitPopup
- *
- * Popup menu initialization before WM_ENTERMENULOOP.
- */
-static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
-{
-    POPUPMENU *menu;
-    DWORD ex_style = 0;
-
-    TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
-
-    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
-
-    /* store the owner for DrawItem */
-    if (!IsWindow( hwndOwner ))
-    {
-        SetLastError( ERROR_INVALID_WINDOW_HANDLE );
-        return FALSE;
-    }
-    menu->hwndOwner = hwndOwner;
-
-    if (flags & TPM_LAYOUTRTL)
-        ex_style = WS_EX_LAYOUTRTL;
-
-    /* NOTE: In Windows, top menu popup is not owned. */
-    menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
-                                WS_POPUP, 0, 0, 0, 0,
-                                hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
-                                (LPVOID)hmenu );
-    if( !menu->hWnd ) return FALSE;
-    return TRUE;
-}
-
-
-/***********************************************************************
- *           MENU_ShowPopup
- *
- * Display a popup menu.
- */
-static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
-                              INT x, INT y, INT xanchor, INT yanchor )
-{
-    POPUPMENU *menu;
-    POINT pt;
-    HMONITOR monitor;
-    MONITORINFO info;
-    UINT max_height;
-
-    TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
-          hwndOwner, hmenu, id, x, y, xanchor, yanchor);
-
-    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
-    if (menu->FocusedItem != NO_SELECTED_ITEM)
-    {
-	menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
-	menu->FocusedItem = NO_SELECTED_ITEM;
-    }
-
-    menu->nScrollPos = 0;
-
-    /* FIXME: should use item rect */
-    pt.x = x;
-    pt.y = y;
-    monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
-    info.cbSize = sizeof(info);
-    GetMonitorInfoW( monitor, &info );
-
-    max_height = info.rcWork.bottom - info.rcWork.top;
-    if (menu->cyMax)
-        max_height = min( max_height, menu->cyMax );
-
-    MENU_PopupMenuCalcSize( menu, max_height );
-
-    /* adjust popup menu pos so that it fits within the desktop */
-
-    if (flags & TPM_LAYOUTRTL)
-        flags ^= TPM_RIGHTALIGN;
-
-    if( flags & TPM_RIGHTALIGN ) x -= menu->Width;
-    if( flags & TPM_CENTERALIGN ) x -= menu->Width / 2;
-
-    if( flags & TPM_BOTTOMALIGN ) y -= menu->Height;
-    if( flags & TPM_VCENTERALIGN ) y -= menu->Height / 2;
-
-    if( x + menu->Width > info.rcWork.right)
-    {
-        if( xanchor && x >= menu->Width - xanchor )
-            x -= menu->Width - xanchor;
-
-        if( x + menu->Width > info.rcWork.right)
-            x = info.rcWork.right - menu->Width;
-    }
-    if( x < info.rcWork.left ) x = info.rcWork.left;
-
-    if( y + menu->Height > info.rcWork.bottom)
-    {
-        if( yanchor && y >= menu->Height + yanchor )
-            y -= menu->Height + yanchor;
-
-        if( y + menu->Height > info.rcWork.bottom)
-            y = info.rcWork.bottom - menu->Height;
-    }
-    if( y < info.rcWork.top ) y = info.rcWork.top;
-
-    if (!top_popup) {
-        top_popup = menu->hWnd;
-        top_popup_hmenu = hmenu;
-    }
-    /* Display the window */
-
-    NtUserSetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
-                        SWP_SHOWWINDOW | SWP_NOACTIVATE );
-    UpdateWindow( menu->hWnd );
-    return TRUE;
-}
-
-
-/***********************************************************************
- *           MENU_EnsureMenuItemVisible
- */
-static void
-MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
-{
-    if (lppop->bScrolling)
-    {
-        MENUITEM *item = &lppop->items[wIndex];
-        UINT nOldPos = lppop->nScrollPos;
-        const RECT *rc = &lppop->items_rect;
-        UINT scroll_height = rc->bottom - rc->top;
-
-        if (item->rect.bottom > lppop->nScrollPos + scroll_height)
-        {
-            lppop->nScrollPos = item->rect.bottom - scroll_height;
-            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
-        }
-        else if (item->rect.top < lppop->nScrollPos)
-        {
-            lppop->nScrollPos = item->rect.top;
-            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
-        }
-
-        /* Invalidate the scroll arrows if necessary */
-        if (nOldPos != lppop->nScrollPos)
-        {
-            RECT arrow_rect = lppop->items_rect;
-            if (nOldPos == 0 || lppop->nScrollPos == 0)
-            {
-                arrow_rect.top = 0;
-                arrow_rect.bottom = lppop->items_rect.top;
-                NtUserInvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
-            }
-            if (nOldPos + scroll_height == lppop->nTotalHeight ||
-                lppop->nScrollPos + scroll_height == lppop->nTotalHeight)
-            {
-                arrow_rect.top = lppop->items_rect.bottom;
-                arrow_rect.bottom = lppop->Height;
-                NtUserInvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
-            }
-        }
-    }
-}
-
-
-/***********************************************************************
- *           MENU_SelectItem
- */
-static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
-                             BOOL sendMenuSelect, HMENU topmenu )
-{
-    LPPOPUPMENU lppop;
-    HDC hdc;
-
-    TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
-
-    lppop = MENU_GetMenu( hmenu );
-    if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
-
-    if (lppop->FocusedItem == wIndex) return;
-    if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
-    else hdc = NtUserGetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
-    if (!top_popup) {
-        top_popup = lppop->hWnd;
-        top_popup_hmenu = hmenu;
-    }
-
-    SelectObject( hdc, get_menu_font(FALSE));
-
-      /* Clear previous highlighted item */
-    if (lppop->FocusedItem != NO_SELECTED_ITEM)
-    {
-        lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
-        MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[lppop->FocusedItem],
-                           !(lppop->wFlags & MF_POPUP), ODA_SELECT );
-    }
-
-      /* Highlight new item (if any) */
-    lppop->FocusedItem = wIndex;
-    if (lppop->FocusedItem != NO_SELECTED_ITEM)
-    {
-        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
-            lppop->items[wIndex].fState |= MF_HILITE;
-            MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
-            MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[wIndex],
-                               !(lppop->wFlags & MF_POPUP), ODA_SELECT );
-        }
-        if (sendMenuSelect)
-        {
-            MENUITEM *ip = &lppop->items[lppop->FocusedItem];
-	    SendMessageW( hwndOwner, WM_MENUSELECT,
-                     MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
-                     ip->fType | ip->fState |
-                     (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
-        }
-    }
-    else if (sendMenuSelect) {
-        if(topmenu){
-            int pos;
-            if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
-                POPUPMENU *ptm = MENU_GetMenu( topmenu );
-                MENUITEM *ip = &ptm->items[pos];
-                SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
-                         ip->fType | ip->fState |
-                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
-            }
-        }
-    }
-    NtUserReleaseDC( lppop->hWnd, hdc );
-}
-
-
-/***********************************************************************
- *           MENU_MoveSelection
- *
- * Moves currently selected item according to the offset parameter.
- * If there is no selection then it should select the last item if
- * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
- */
-static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
-{
-    INT i;
-    POPUPMENU *menu;
-
-    TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
-
-    menu = MENU_GetMenu( hmenu );
-    if ((!menu) || (!menu->items)) return;
-
-    if ( menu->FocusedItem != NO_SELECTED_ITEM )
-    {
-	if( menu->nItems == 1 ) return; else
-	for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
-					    ; i += offset)
-	    if (!(menu->items[i].fType & MF_SEPARATOR))
-	    {
-		MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
-		return;
-	    }
-    }
-
-    for ( i = (offset > 0) ? 0 : menu->nItems - 1;
-		  i >= 0 && i < menu->nItems ; i += offset)
-	if (!(menu->items[i].fType & MF_SEPARATOR))
-	{
-	    MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
-	    return;
-	}
-}
-
-
-/**********************************************************************
- *         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;
-    LPCWSTR str;
-    BOOL end_flag;
-
-    do
-    {
-        flags = GET_WORD(res);
-        end_flag = flags & MF_END;
-        /* Remove MF_END because it has the same value as MF_HILITE */
-        flags &= ~MF_END;
-        res += sizeof(WORD);
-        if (!(flags & MF_POPUP))
-        {
-            id = GET_WORD(res);
-            res += sizeof(WORD);
-        }
-        str = (LPCWSTR)res;
-        res += (lstrlenW(str) + 1) * sizeof(WCHAR);
-        if (flags & MF_POPUP)
-        {
-            HMENU hSubMenu = CreatePopupMenu();
-            if (!hSubMenu) return NULL;
-            if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
-            AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
-        }
-        else  /* Not a popup */
-        {
-            AppendMenuW( hMenu, flags, id, *str ? str : NULL );
-        }
-    } while (!end_flag);
-    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); /* FIXME: for 16-bit apps this is a byte.  */
-        res += sizeof(WORD);
-	/* Align the text on a word boundary.  */
-	res += (~((UINT_PTR)res - 1)) & 1;
-	mii.dwTypeData = (LPWSTR) res;
-	res += (1 + lstrlenW(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, debugstr_w(mii.dwTypeData));
-
-	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))) {
-		NtUserDestroyMenu( mii.hSubMenu );
-                return NULL;
-	    }
-	    mii.fMask |= MIIM_SUBMENU;
-	    mii.fType |= MF_POPUP;
-        }
-	else if(!*mii.dwTypeData && !(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_GetSubPopup
- *
- * Return the handle of the selected sub-popup menu (if any).
- */
-static HMENU MENU_GetSubPopup( HMENU hmenu )
-{
-    POPUPMENU *menu;
-    MENUITEM *item;
-
-    menu = MENU_GetMenu( hmenu );
-
-    if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
-
-    item = &menu->items[menu->FocusedItem];
-    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
-        return item->hSubMenu;
-    return 0;
-}
-
-
-/***********************************************************************
- *           MENU_HideSubPopups
- *
- * Hide the sub-popup menus of this menu.
- */
-static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
-                                BOOL sendMenuSelect, UINT wFlags )
-{
-    POPUPMENU *menu = MENU_GetMenu( hmenu );
-
-    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
-
-    if (menu && top_popup)
-    {
-	HMENU hsubmenu;
-	POPUPMENU *submenu;
-	MENUITEM *item;
-
-	if (menu->FocusedItem != NO_SELECTED_ITEM)
-	{
-	    item = &menu->items[menu->FocusedItem];
-	    if (!(item->fType & MF_POPUP) ||
-		!(item->fState & MF_MOUSESELECT)) return;
-	    item->fState &= ~MF_MOUSESELECT;
-	    hsubmenu = item->hSubMenu;
-	} else return;
-
-	if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
-	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
-	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
-        NtUserDestroyWindow( submenu->hWnd );
-        submenu->hWnd = 0;
-
-        if (!(wFlags & TPM_NONOTIFY))
-           SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
-                         MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
-    }
-}
-
-
-/***********************************************************************
- *           MENU_ShowSubPopup
- *
- * Display the sub-menu of the selected item of this menu.
- * Return the handle of the submenu, or hmenu if no submenu to display.
- */
-static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
-                                  BOOL selectFirst, UINT wFlags )
-{
-    RECT rect;
-    POPUPMENU *menu;
-    MENUITEM *item;
-    HDC hdc;
-
-    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
-
-    if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
-
-    if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
-
-    item = &menu->items[menu->FocusedItem];
-    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
-        return hmenu;
-
-    /* message must be sent before using item,
-       because nearly everything may be changed by the application ! */
-
-    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
-    if (!(wFlags & TPM_NONOTIFY))
-       SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
-                     MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
-
-    item = &menu->items[menu->FocusedItem];
-    rect = item->rect;
-
-    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
-    if (!(item->fState & MF_HILITE))
-    {
-        if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
-        else hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
-
-        SelectObject( hdc, get_menu_font(FALSE));
-
-        item->fState |= MF_HILITE;
-        MENU_DrawMenuItem( menu->hWnd, menu, hwndOwner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
-        NtUserReleaseDC( menu->hWnd, hdc );
-    }
-    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
-        item->rect = rect;
-
-    item->fState |= MF_MOUSESELECT;
-
-    if (IS_SYSTEM_MENU(menu))
-    {
-	MENU_InitSysMenuPopup(item->hSubMenu,
-                              GetWindowLongW( menu->hWnd, GWL_STYLE ),
-                              GetClassLongW( menu->hWnd, GCL_STYLE));
-
-	NC_GetSysPopupPos( menu->hWnd, &rect );
-	if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
-	rect.top = rect.bottom;
-	rect.right = GetSystemMetrics(SM_CXSIZE);
-        rect.bottom = GetSystemMetrics(SM_CYSIZE);
-    }
-    else
-    {
-        RECT item_rect = item->rect;
-
-        MENU_AdjustMenuItemRect(menu, &item_rect);
-        GetWindowRect( menu->hWnd, &rect );
-
-	if (menu->wFlags & MF_POPUP)
-	{
-	    /* The first item in the popup menu has to be at the
-	       same y position as the focused menu item */
-            if (wFlags & TPM_LAYOUTRTL)
-                rect.left += GetSystemMetrics(SM_CXBORDER);
-	    else
-                rect.left += item_rect.right - GetSystemMetrics(SM_CXBORDER);
-	    rect.top += item_rect.top - MENU_MARGIN;
-	    rect.right = item_rect.left - item_rect.right + GetSystemMetrics(SM_CXBORDER);
-	    rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
-	}
-	else
-	{
-            if (wFlags & TPM_LAYOUTRTL)
-                rect.left = rect.right - item_rect.left;
-	    else
-                rect.left += item_rect.left;
-	    rect.top += item_rect.bottom;
-	    rect.right = item_rect.right - item_rect.left;
-	    rect.bottom = item_rect.bottom - item_rect.top;
-	}
-    }
-
-    /* use default alignment for submenus */
-    wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
-
-    MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );
-
-    MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
-		    rect.left, rect.top, rect.right, rect.bottom );
-    if (selectFirst)
-        MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
-    return item->hSubMenu;
-}
-
-
-
-/**********************************************************************
- *         MENU_IsMenuActive
- */
-HWND MENU_IsMenuActive(void)
-{
-    return top_popup;
-}
-
-/**********************************************************************
- *         MENU_EndMenu
- *
- * Calls EndMenu() if the hwnd parameter belongs to the menu owner
- *
- * Does the (menu stuff) of the default window handling of WM_CANCELMODE
- */
-void MENU_EndMenu( HWND hwnd )
-{
-    POPUPMENU *menu;
-    menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
-    if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
-}
-
-/***********************************************************************
- *           MENU_PtMenu
- *
- * Walks menu chain trying to find a menu pt maps to.
- */
-static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
-{
-   POPUPMENU *menu = MENU_GetMenu( hMenu );
-   UINT item = menu->FocusedItem;
-   HMENU ret;
-
-   /* try subpopup first (if any) */
-   ret = (item != NO_SELECTED_ITEM &&
-          (menu->items[item].fType & MF_POPUP) &&
-          (menu->items[item].fState & MF_MOUSESELECT))
-        ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
-
-   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
-   {
-       INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
-       if( menu->wFlags & MF_POPUP )
-       {
-           if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
-       }
-       else if (ht == HTSYSMENU)
-           ret = get_win_sys_menu( menu->hWnd );
-       else if (ht == HTMENU)
-           ret = GetMenu( menu->hWnd );
-   }
-   return ret;
-}
-
-/***********************************************************************
- *           MENU_ExecFocusedItem
- *
- * Execute a menu item (for instance when user pressed Enter).
- * Return the wID of the executed item. Otherwise, -1 indicating
- * that no menu item was executed, -2 if a popup is shown;
- * Have to receive the flags for the TrackPopupMenu options to avoid
- * sending unwanted message.
- *
- */
-static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
-{
-    MENUITEM *item;
-    POPUPMENU *menu = MENU_GetMenu( hMenu );
-
-    TRACE("%p hmenu=%p\n", pmt, hMenu);
-
-    if (!menu || !menu->nItems ||
-	(menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
-
-    item = &menu->items[menu->FocusedItem];
-
-    TRACE("hMenu %p wID %08Ix hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
-
-    if (!(item->fType & MF_POPUP))
-    {
-	if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
-	{
-	    /* If TPM_RETURNCMD is set you return the id, but
-	       do not send a message to the owner */
-	    if(!(wFlags & TPM_RETURNCMD))
-	    {
-		if( menu->wFlags & MF_SYSMENU )
-		    PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
-				  MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
-		else
-		{
-                    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
-                    DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
-
-                    if (dwStyle & MNS_NOTIFYBYPOS)
-                        PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
-                                      (LPARAM)hMenu);
-                    else
-                        PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
-		}
-	    }
-	    return item->wID;
-	}
-    }
-    else
-    {
-	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
-	return -2;
-    }
-
-    return -1;
-}
-
-/***********************************************************************
- *           MENU_SwitchTracking
- *
- * Helper function for menu navigation routines.
- */
-static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
-{
-    POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
-    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
-
-    TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
-
-    if( pmt->hTopMenu != hPtMenu &&
-	!((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
-    {
-	/* both are top level menus (system and menu-bar) */
-	MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
-	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
-        pmt->hTopMenu = hPtMenu;
-    }
-    else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
-    MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
-}
-
-
-/***********************************************************************
- *           MENU_ButtonDown
- *
- * Return TRUE if we can go on with menu tracking.
- */
-static BOOL MENU_ButtonDown( MTRACKER* pmt, UINT message, HMENU hPtMenu, UINT wFlags )
-{
-    TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
-
-    if (hPtMenu)
-    {
-        UINT pos;
-        POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
-        enum hittest ht = ht_item;
-
-        if( IS_SYSTEM_MENU(ptmenu) )
-        {
-            if (message == WM_LBUTTONDBLCLK) return FALSE;
-            pos = 0;
-        }
-        else
-            ht = MENU_FindItemByCoords( ptmenu, pmt->pt, &pos );
-
-        if (pos != NO_SELECTED_ITEM)
-        {
-            if (ptmenu->FocusedItem != pos)
-                MENU_SwitchTracking( pmt, hPtMenu, pos, wFlags );
-
-            /* If the popup menu is not already "popped" */
-            if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
-                pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
-        }
-
-        /* A click on an item or anywhere on a popup keeps tracking going */
-        if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
-            return TRUE;
-    }
-    return FALSE;
-}
-
-/***********************************************************************
- *           MENU_ButtonUp
- *
- * Return the value of MENU_ExecFocusedItem if
- * the selected item was not a popup. Else open the popup.
- * A -1 return value indicates that we go on with menu tracking.
- *
- */
-static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
-{
-    TRACE("%p hmenu=%p\n", pmt, hPtMenu);
-
-    if (hPtMenu)
-    {
-	UINT pos;
-	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
-
-        if( IS_SYSTEM_MENU(ptmenu) )
-            pos = 0;
-        else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &pos ) != ht_item)
-            pos = NO_SELECTED_ITEM;
-
-        if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
-	{
-            debug_print_menuitem ("FocusedItem: ", &ptmenu->items[pos], "");
-
-	    if (!(ptmenu->items[pos].fType & MF_POPUP))
-	    {
-	        INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
-                if (executedMenuId == -1 || executedMenuId == -2) return -1;
-                return executedMenuId;
-	    }
-
-	    /* If we are dealing with the menu bar                  */
-	    /* and this is a click on an already "popped" item:     */
-	    /* Stop the menu tracking and close the opened submenus */
-            if(((pmt->hTopMenu == hPtMenu) || IS_SYSTEM_MENU(ptmenu)) && (pmt->trackFlags & TF_RCVD_BTN_UP))
-		return 0;
-	}
-        if( GetMenu(ptmenu->hWnd) == hPtMenu || IS_SYSTEM_MENU(ptmenu) )
-        {
-            if (pos == NO_SELECTED_ITEM) return 0;
-            pmt->trackFlags |= TF_RCVD_BTN_UP;
-        }
-    }
-    return -1;
-}
-
-
-/***********************************************************************
- *           MENU_MouseMove
- *
- * Return TRUE if we can go on with menu tracking.
- */
-static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
-{
-    UINT id = NO_SELECTED_ITEM;
-    POPUPMENU *ptmenu = NULL;
-
-    if( hPtMenu )
-    {
-	ptmenu = MENU_GetMenu( hPtMenu );
-        if( IS_SYSTEM_MENU(ptmenu) )
-	    id = 0;
-        else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &id ) != ht_item)
-            id = NO_SELECTED_ITEM;
-    }
-
-    if( id == NO_SELECTED_ITEM )
-    {
-	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
-			 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
-
-    }
-    else if( ptmenu->FocusedItem != id )
-    {
-        MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
-        pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
-    }
-    return TRUE;
-}
-
-
-/***********************************************************************
- *           MENU_DoNextMenu
- *
- * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
- */
-static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
-{
-    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
-    BOOL atEnd = FALSE;
-
-    /* When skipping left, we need to do something special after the
-       first menu.                                                  */
-    if (vk == VK_LEFT && menu->FocusedItem == 0)
-    {
-        atEnd = TRUE;
-    }
-    /* When skipping right, for the non-system menu, we need to
-       handle the last non-special menu item (ie skip any window
-       icons such as MDI maximize, restore or close)             */
-    else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
-    {
-        UINT i = menu->FocusedItem + 1;
-        while (i < menu->nItems) {
-            if ((menu->items[i].wID >= SC_SIZE &&
-                 menu->items[i].wID <= SC_RESTORE)) {
-                i++;
-            } else break;
-        }
-        if (i == menu->nItems) {
-            atEnd = TRUE;
-        }
-    }
-    /* When skipping right, we need to cater for the system menu */
-    else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
-    {
-        if (menu->FocusedItem == (menu->nItems - 1)) {
-            atEnd = TRUE;
-        }
-    }
-
-    if( atEnd )
-    {
-        MDINEXTMENU next_menu;
-	HMENU hNewMenu;
-	HWND  hNewWnd;
-	UINT  id = 0;
-
-        next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
-        next_menu.hmenuNext = 0;
-        next_menu.hwndNext = 0;
-        SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
-
-        TRACE("%p [%p] -> %p [%p]\n",
-              pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
-
-	if (!next_menu.hmenuNext || !next_menu.hwndNext)
-	{
-            DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
-	    hNewWnd = pmt->hOwnerWnd;
-	    if( IS_SYSTEM_MENU(menu) )
-	    {
-		/* switch to the menu bar */
-
-                if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
-
-	        if( vk == VK_LEFT )
-	        {
-		    menu = MENU_GetMenu( hNewMenu );
-		    id = menu->nItems - 1;
-
-                    /* Skip backwards over any system predefined icons,
-                       eg. MDI close, restore etc icons                 */
-                    while ((id > 0) &&
-                           (menu->items[id].wID >= SC_SIZE &&
-                            menu->items[id].wID <= SC_RESTORE)) id--;
-	        }
-	    }
-	    else if (style & WS_SYSMENU )
-	    {
-		/* switch to the system menu */
-	        hNewMenu = get_win_sys_menu( hNewWnd );
-	    }
-            else return FALSE;
-	}
-	else    /* application returned a new menu to switch to */
-	{
-            hNewMenu = next_menu.hmenuNext;
-            hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
-
-	    if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
-	    {
-                DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
-
-		if (style & WS_SYSMENU &&
-		    GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
-		{
-	            /* get the real system menu */
-		    hNewMenu =  get_win_sys_menu(hNewWnd);
-		}
-	        else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
-		{
-		    /* FIXME: Not sure what to do here;
-		     * perhaps try to track hNewMenu as a popup? */
-
-		    TRACE(" -- got confused.\n");
-		    return FALSE;
-		}
-	    }
-	    else return FALSE;
-	}
-
-	if( hNewMenu != pmt->hTopMenu )
-	{
-	    MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
-                    FALSE, 0 );
-	    if( pmt->hCurrentMenu != pmt->hTopMenu )
-		MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
-	}
-
-	if( hNewWnd != pmt->hOwnerWnd )
-	{
-	    pmt->hOwnerWnd = hNewWnd;
-            set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
-	}
-
-	pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
-	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
-
-	return TRUE;
-    }
-    return FALSE;
-}
-
-/***********************************************************************
- *           MENU_SuspendPopup
- *
- * The idea is not to show the popup if the next input message is
- * going to hide it anyway.
- */
-static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT uMsg )
-{
-    MSG msg;
-
-    msg.hwnd = pmt->hOwnerWnd;
-
-    PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
-    pmt->trackFlags |= TF_SKIPREMOVE;
-
-    switch( uMsg )
-    {
-	case WM_KEYDOWN:
-	     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
-	     if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
-	     {
-		 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
-	         PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
-	         if( msg.message == WM_KEYDOWN &&
-		    (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
-	         {
-		     pmt->trackFlags |= TF_SUSPENDPOPUP;
-		     return TRUE;
-	         }
-	     }
-	     break;
-    }
-
-    /* failures go through this */
-    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
-    return FALSE;
-}
-
-/***********************************************************************
- *           MENU_KeyEscape
- *
- * Handle a VK_ESCAPE key event in a menu.
- */
-static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
-{
-    BOOL bEndMenu = TRUE;
-
-    if (pmt->hCurrentMenu != pmt->hTopMenu)
-    {
-        POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
-
-        if (menu->wFlags & MF_POPUP)
-        {
-            HMENU hmenutmp, hmenuprev;
-
-            hmenuprev = hmenutmp = pmt->hTopMenu;
-
-            /* close topmost popup */
-            while (hmenutmp != pmt->hCurrentMenu)
-            {
-                hmenuprev = hmenutmp;
-                hmenutmp = MENU_GetSubPopup( hmenuprev );
-            }
-
-            MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
-            pmt->hCurrentMenu = hmenuprev;
-            bEndMenu = FALSE;
-        }
-    }
-
-    return bEndMenu;
-}
-
-/***********************************************************************
- *           MENU_KeyLeft
- *
- * Handle a VK_LEFT key event in a menu.
- */
-static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags, UINT msg )
-{
-    POPUPMENU *menu;
-    HMENU hmenutmp, hmenuprev;
-    UINT  prevcol;
-
-    hmenuprev = hmenutmp = pmt->hTopMenu;
-    menu = MENU_GetMenu( hmenutmp );
-
-    /* Try to move 1 column left (if possible) */
-    if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
-	NO_SELECTED_ITEM ) {
-
-	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
-			 prevcol, TRUE, 0 );
-	return;
-    }
-
-    /* close topmost popup */
-    while (hmenutmp != pmt->hCurrentMenu)
-    {
-	hmenuprev = hmenutmp;
-	hmenutmp = MENU_GetSubPopup( hmenuprev );
-    }
-
-    MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
-    pmt->hCurrentMenu = hmenuprev;
-
-    if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
-    {
-	/* move menu bar selection if no more popups are left */
-
-	if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
-	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
-
-	if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
-	{
-	   /* A sublevel menu was displayed - display the next one
-	    * unless there is another displacement coming up */
-
-	    if( !MENU_SuspendPopup( pmt, msg ) )
-		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
-						pmt->hTopMenu, TRUE, wFlags);
-	}
-    }
-}
-
-
-/***********************************************************************
- *           MENU_KeyRight
- *
- * Handle a VK_RIGHT key event in a menu.
- */
-static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags, UINT msg )
-{
-    HMENU hmenutmp;
-    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
-    UINT  nextcol;
-
-    TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
-          pmt->hCurrentMenu,
-          debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
-          pmt->hTopMenu, debugstr_w(menu->items[0].text) );
-
-    if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
-    {
-	/* If already displaying a popup, try to display sub-popup */
-
-	hmenutmp = pmt->hCurrentMenu;
-	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
-
-	/* if subpopup was displayed then we are done */
-	if (hmenutmp != pmt->hCurrentMenu) return;
-    }
-
-    /* Check to see if there's another column */
-    if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
-	NO_SELECTED_ITEM ) {
-	TRACE("Going to %d.\n", nextcol );
-	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
-			 nextcol, TRUE, 0 );
-	return;
-    }
-
-    if (!(menu->wFlags & MF_POPUP))	/* menu bar tracking */
-    {
-	if( pmt->hCurrentMenu != pmt->hTopMenu )
-	{
-	    MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
-	    hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
-	} else hmenutmp = 0;
-
-	/* try to move to the next item */
-	if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
-	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
-
-	if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
-	    if( !MENU_SuspendPopup( pmt, msg ) )
-		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
-						       pmt->hTopMenu, TRUE, wFlags);
-    }
-}
-
-static void CALLBACK release_capture( BOOL __normal )
-{
-    set_capture_window( 0, GUI_INMENUMODE, NULL );
-}
-
-/***********************************************************************
- *           MENU_TrackMenu
- *
- * Menu tracking code.
- */
-static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
-                            HWND hwnd, const RECT *lprect )
-{
-    MSG msg;
-    POPUPMENU *menu;
-    BOOL fRemove;
-    INT executedMenuId = -1;
-    MTRACKER mt;
-    BOOL enterIdleSent = FALSE;
-    HWND capture_win;
-
-    mt.trackFlags = 0;
-    mt.hCurrentMenu = hmenu;
-    mt.hTopMenu = hmenu;
-    mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
-    mt.pt.x = x;
-    mt.pt.y = y;
-
-    TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
-          hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
-
-    if (!(menu = MENU_GetMenu( hmenu )))
-    {
-        WARN("Invalid menu handle %p\n", hmenu);
-        SetLastError(ERROR_INVALID_MENU_HANDLE);
-        return FALSE;
-    }
-
-    if (wFlags & TPM_BUTTONDOWN)
-    {
-	/* Get the result in order to start the tracking or not */
-	fRemove = MENU_ButtonDown( &mt, WM_LBUTTONDOWN, hmenu, wFlags );
-	fEndMenu = !fRemove;
-    }
-
-    if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
-
-    /* owner may not be visible when tracking a popup, so use the menu itself */
-    capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
-    set_capture_window( capture_win, GUI_INMENUMODE, NULL );
-
-    if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
-        return FALSE;
-
-    __TRY while (!fEndMenu)
-    {
-	menu = MENU_GetMenu( mt.hCurrentMenu );
-	if (!menu) /* sometimes happens if I do a window manager close */
-	    break;
-
-	/* we have to keep the message in the queue until it's
-	 * clear that menu loop is not over yet. */
-
-        for (;;)
-        {
-            if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
-            {
-                if (!NtUserCallMsgFilter( &msg, MSGF_MENU )) break;
-                /* remove the message from the queue */
-                PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
-            }
-            else
-            {
-                if (!enterIdleSent)
-                {
-                    HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
-                    enterIdleSent = TRUE;
-                    SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
-                }
-                WaitMessage();
-            }
-        }
-
-	/* check if EndMenu() tried to cancel us, by posting this message */
-        if(msg.message == WM_CANCELMODE)
-	{
-	    /* we are now out of the loop */
-    	    fEndMenu = TRUE;
-
-	    /* remove the message from the queue */
-	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
-
-	    /* break out of internal loop, ala ESCAPE */
-	    break;
-	}
-
-        mt.pt = msg.pt;
-
-	if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
-	  enterIdleSent=FALSE;
-
-        fRemove = FALSE;
-	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
-	{
-            /*
-             * Use the mouse coordinates in lParam instead of those in the MSG
-             * struct to properly handle synthetic messages. They are already
-             * in screen coordinates.
-             */
-            mt.pt.x = (short)LOWORD(msg.lParam);
-            mt.pt.y = (short)HIWORD(msg.lParam);
-
-	    /* Find a menu for this mouse event */
-	    hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
-
-	    switch(msg.message)
-	    {
-		/* no WM_NC... messages in captured state */
-
-		case WM_RBUTTONDBLCLK:
-		case WM_RBUTTONDOWN:
-		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
-		    /* fall through */
-		case WM_LBUTTONDBLCLK:
-		case WM_LBUTTONDOWN:
-		    /* If the message belongs to the menu, removes it from the queue */
-		    /* Else, end menu tracking */
-		    fRemove = MENU_ButtonDown( &mt, msg.message, hmenu, wFlags );
-		    fEndMenu = !fRemove;
-		    break;
-
-		case WM_RBUTTONUP:
-		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
-		    /* fall through */
-		case WM_LBUTTONUP:
-		    /* Check if a menu was selected by the mouse */
-		    if (hmenu)
-		    {
-                        executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
-                        TRACE("executedMenuId %d\n", executedMenuId);
-
-			/* End the loop if executedMenuId is an item ID */
-			/* or if the job was done (executedMenuId = 0). */
-                        fEndMenu = fRemove = (executedMenuId != -1);
-		    }
-                    /* No menu was selected by the mouse */
-                    /* if the function was called by TrackPopupMenu, continue
-                       with the menu tracking. If not, stop it */
-                    else
-                        fEndMenu = !(wFlags & TPM_POPUPMENU);
-
-		    break;
-
-		case WM_MOUSEMOVE:
-                    /* the selected menu item must be changed every time */
-		     /* the mouse moves. */
-
-                    if (hmenu)
-			fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
-
-	    } /* switch(msg.message) - mouse */
-	}
-	else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
-	{
-            fRemove = TRUE;  /* Keyboard messages are always removed */
-	    switch(msg.message)
-	    {
-	    case WM_KEYDOWN:
-	    case WM_SYSKEYDOWN:
-		switch(msg.wParam)
-		{
-		case VK_MENU:
-		case VK_F10:
-		    fEndMenu = TRUE;
-		    break;
-
-		case VK_HOME:
-		case VK_END:
-		    MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
-				     NO_SELECTED_ITEM, FALSE, 0 );
-		    MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
-				       (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
-		    break;
-
-		case VK_UP:
-		case VK_DOWN: /* If on menu bar, pull-down the menu */
-
-		    menu = MENU_GetMenu( mt.hCurrentMenu );
-		    if (!(menu->wFlags & MF_POPUP))
-			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
-		    else      /* otherwise try to move selection */
-			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
-                                       (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
-		    break;
-
-		case VK_LEFT:
-		    MENU_KeyLeft( &mt, wFlags, msg.message );
-		    break;
-
-		case VK_RIGHT:
-		    MENU_KeyRight( &mt, wFlags, msg.message );
-		    break;
-
-		case VK_ESCAPE:
-                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
-		    break;
-
-		case VK_F1:
-		    {
-			HELPINFO hi;
-			hi.cbSize = sizeof(HELPINFO);
-			hi.iContextType = HELPINFO_MENUITEM;
-			if (menu->FocusedItem == NO_SELECTED_ITEM)
-			    hi.iCtrlId = 0;
-		        else
-			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
-			hi.hItemHandle = hmenu;
-			hi.dwContextId = menu->dwContextHelpID;
-			hi.MousePos = msg.pt;
-			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
-			break;
-		    }
-
-		default:
-		    TranslateMessage( &msg );
-		    break;
-		}
-		break;  /* WM_KEYDOWN */
-
-	    case WM_CHAR:
-	    case WM_SYSCHAR:
-		{
-		    UINT	pos;
-
-		    if (msg.wParam == '\r' || msg.wParam == ' ')
-		    {
-                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
-                        fEndMenu = (executedMenuId != -2);
-
-			break;
-		    }
-
-		      /* Hack to avoid control chars. */
-		      /* We will find a better way real soon... */
-		    if (msg.wParam < 32) break;
-
-		    pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
-                                              LOWORD(msg.wParam), FALSE );
-		    if (pos == (UINT)-2) fEndMenu = TRUE;
-		    else if (pos == (UINT)-1) MessageBeep(0);
-		    else
-		    {
-			MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
-                                TRUE, 0 );
-                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
-                        fEndMenu = (executedMenuId != -2);
-		    }
-		}
-		break;
-	    }  /* switch(msg.message) - kbd */
-	}
-	else
-	{
-	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
-	    DispatchMessageW( &msg );
-	    continue;
-	}
-
-	if (!fEndMenu) fRemove = TRUE;
-
-	/* finally remove message from the queue */
-
-        if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
-	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
-	else mt.trackFlags &= ~TF_SKIPREMOVE;
-    }
-    __FINALLY( release_capture )
-
-    /* If dropdown is still painted and the close box is clicked on
-       then the menu will be destroyed as part of the DispatchMessage above.
-       This will then invalidate the menu handle in mt.hTopMenu. We should
-       check for this first.  */
-    if( IsMenu( mt.hTopMenu ) )
-    {
-	menu = MENU_GetMenu( mt.hTopMenu );
-
-        if( IsWindow( mt.hOwnerWnd ) )
-        {
-	    MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
-
-	    if (menu && (menu->wFlags & MF_POPUP))
-	    {
-                NtUserDestroyWindow( menu->hWnd );
-                menu->hWnd = 0;
-
-                if (!(wFlags & TPM_NONOTIFY))
-                   SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
-                                 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
-	    }
-	    MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
-	    SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
-        }
-    }
-
-    SetLastError( ERROR_SUCCESS );
-    /* The return value is only used by TrackPopupMenu */
-    if (!(wFlags & TPM_RETURNCMD)) return TRUE;
-    if (executedMenuId == -1) executedMenuId = 0;
-    return executedMenuId;
-}
-
-/***********************************************************************
- *           MENU_InitTracking
- */
-static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
-{
-    POPUPMENU *menu;
-    
-    TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
-
-    NtUserHideCaret( 0 );
-
-    if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;
-
-    /* This makes the menus of applications built with Delphi work.
-     * It also enables menus to be displayed in more than one window,
-     * but there are some bugs left that need to be fixed in this case.
-     */
-    if (!bPopup) menu->hWnd = hWnd;
-    if (!top_popup)
-    {
-        top_popup = menu->hWnd;
-        top_popup_hmenu = hMenu;
-    }
-
-    fEndMenu = FALSE;
-
-    /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
-    if (!(wFlags & TPM_NONOTIFY))
-       SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
-
-    SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
-
-    if (!(wFlags & TPM_NONOTIFY))
-    {
-       SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
-       /* If an app changed/recreated menu bar entries in WM_INITMENU
-        * menu sizes will be recalculated once the menu created/shown.
-        */
-    }
-
-    return TRUE;
-}
-
-/***********************************************************************
- *           MENU_ExitTracking
- */
-static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
-{
-    TRACE("hwnd=%p\n", hWnd);
-
-    SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
-    NtUserShowCaret( 0 );
-    top_popup = 0;
-    top_popup_hmenu = NULL;
-    return TRUE;
-}
-
-/***********************************************************************
- *           MENU_TrackMouseMenuBar
- *
- * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
- */
-void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
-{
-    HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
-    UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
-
-    TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
-
-    if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
-    if (IsMenu(hMenu))
-    {
-	MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
-
-        /* fetch the window menu again, it may have changed */
-        hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
-	MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
-	MENU_ExitTracking(hWnd, FALSE);
-    }
-}
-
-
-/***********************************************************************
- *           MENU_TrackKbdMenuBar
- *
- * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
- */
-void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
+HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
 {
-    UINT uItem = NO_SELECTED_ITEM;
-    HMENU hTrackMenu;
-    UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
+    HMENU hMenu;
+
+    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
+    if ((hMenu = CreateMenu()))
+    {
+	POPUPMENU *menu = MENU_GetMenu(hMenu);
+	menu->wFlags = MF_SYSMENU;
+	menu->hWnd = WIN_GetFullHandle( hWnd );
+	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
 
-    TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
+	if (!hPopupMenu)
+        {
+            if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
+	        hPopupMenu = MENU_CopySysPopup(TRUE);
+            else
+	        hPopupMenu = MENU_CopySysPopup(FALSE);
+        }
 
-    /* find window that has a menu */
+	if (hPopupMenu)
+	{
+            if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
+                NtUserDeleteMenu( hPopupMenu, SC_CLOSE, MF_BYCOMMAND );
 
-    while (is_win_menu_disallowed(hwnd))
-        if (!(hwnd = NtUserGetAncestor( hwnd, GA_PARENT ))) return;
+	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
+                         (UINT_PTR)hPopupMenu, NULL );
 
-    /* check if we have to track a system menu */
+            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
+            menu->items[0].fState = 0;
+            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
 
-    hTrackMenu = GetMenu( hwnd );
-    if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
-    {
-        if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
-        hTrackMenu = get_win_sys_menu( hwnd );
-        uItem = 0;
-        wParam |= HTSYSMENU; /* prevent item lookup */
+	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
+	    return hMenu;
+	}
+	NtUserDestroyMenu( hMenu );
     }
-    if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
+    ERR("failed to load system menu!\n");
+    return 0;
+}
 
-    if (!IsMenu( hTrackMenu )) return;
 
-    MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
+static POPUPMENU *find_menu_item(HMENU hmenu, UINT id, UINT flags, UINT *pos)
+{
+    UINT fallback_pos = ~0u, i;
+    POPUPMENU *menu;
 
-    /* fetch the window menu again, it may have changed */
-    hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
+    menu = grab_menu_ptr(hmenu);
+    if (!menu)
+        return NULL;
 
-    if( wChar && wChar != ' ' )
+    if (flags & MF_BYPOSITION)
     {
-        uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
-        if ( uItem >= (UINT)(-2) )
+        if (id >= menu->nItems)
         {
-            if( uItem == (UINT)(-1) ) MessageBeep(0);
-            /* schedule end of menu tracking */
-            wFlags |= TF_ENDMENU;
-            goto track_menu;
+            release_menu_ptr(menu);
+            return NULL;
         }
+
+        if (pos) *pos = id;
+        return menu;
     }
+    else
+    {
+        MENUITEM *item = menu->items;
+	for (i = 0; i < menu->nItems; i++, item++)
+	{
+	    if (item->fType & MF_POPUP)
+	    {
+                POPUPMENU *submenu = find_menu_item(item->hSubMenu, id, flags, pos);
 
-    MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
+                if (submenu)
+                {
+                    release_menu_ptr(menu);
+                    return submenu;
+                }
+                else if (item->wID == id)
+		{
+		    /* fallback to this item if nothing else found */
+		    fallback_pos = i;
+		}
+	    }
+	    else if (item->wID == id)
+	    {
+                if (pos) *pos = i;
+                return menu;
+	    }
+	}
+    }
 
-    if (!(wParam & HTSYSMENU) || wChar == ' ')
+    if (fallback_pos != ~0u)
+        *pos = fallback_pos;
+    else
     {
-        if( uItem == NO_SELECTED_ITEM )
-            MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
-        else
-            PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
+        release_menu_ptr(menu);
+        menu = NULL;
     }
 
-track_menu:
-    MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
-    MENU_ExitTracking( hwnd, FALSE );
+    return menu;
 }
 
+
 /**********************************************************************
- *           TrackPopupMenuEx   (USER32.@)
+ *         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
  */
-BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
-                              HWND hWnd, LPTPMPARAMS lpTpm )
+static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
 {
-    POPUPMENU *menu;
-    BOOL ret = FALSE;
-
-    TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
-            hMenu, wFlags, x, y, hWnd, lpTpm,
-            lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
-
-    /* Parameter check */
-    /* FIXME: this check is performed several times, here and in the called
-       functions. That could be optimized */
-    if (!(menu = MENU_GetMenu( hMenu )))
-    {
-        SetLastError( ERROR_INVALID_MENU_HANDLE );
-        return FALSE;
-    }
-
-    if (IsWindow(menu->hWnd))
-    {
-        SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
-        return FALSE;
-    }
+    WORD flags, id = 0;
+    LPCWSTR str;
+    BOOL end_flag;
 
-    if (MENU_InitPopup( hWnd, hMenu, wFlags ))
+    do
     {
-        MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
+        flags = GET_WORD(res);
+        end_flag = flags & MF_END;
+        /* Remove MF_END because it has the same value as MF_HILITE */
+        flags &= ~MF_END;
+        res += sizeof(WORD);
+        if (!(flags & MF_POPUP))
+        {
+            id = GET_WORD(res);
+            res += sizeof(WORD);
+        }
+        str = (LPCWSTR)res;
+        res += (lstrlenW(str) + 1) * sizeof(WCHAR);
+        if (flags & MF_POPUP)
+        {
+            HMENU hSubMenu = CreatePopupMenu();
+            if (!hSubMenu) return NULL;
+            if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
+            AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
+        }
+        else  /* Not a popup */
+        {
+            AppendMenuW( hMenu, flags, id, *str ? str : NULL );
+        }
+    } while (!end_flag);
+    return res;
+}
 
-        /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
-        if (!(wFlags & TPM_NONOTIFY))
-            SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
 
-        if (menu->wFlags & MF_SYSMENU)
-            MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
-                                   GetClassLongW( hWnd, GCL_STYLE));
+/**********************************************************************
+ *         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;
 
-        if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
-            ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
-                                  lpTpm ? &lpTpm->rcExclude : NULL );
-        MENU_ExitTracking(hWnd, TRUE);
+	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); /* FIXME: for 16-bit apps this is a byte.  */
+        res += sizeof(WORD);
+	/* Align the text on a word boundary.  */
+	res += (~((UINT_PTR)res - 1)) & 1;
+	mii.dwTypeData = (LPWSTR) res;
+	res += (1 + lstrlenW(mii.dwTypeData)) * sizeof(WCHAR);
+	/* Align the following fields on a dword boundary.  */
+	res += (~((UINT_PTR)res - 1)) & 3;
 
-        if (menu->hWnd)
-        {
-            NtUserDestroyWindow( menu->hWnd );
-            menu->hWnd = 0;
+        TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
+              mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
 
-            if (!(wFlags & TPM_NONOTIFY))
-               SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
-                             MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
+	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))) {
+		NtUserDestroyMenu( mii.hSubMenu );
+                return NULL;
+	    }
+	    mii.fMask |= MIIM_SUBMENU;
+	    mii.fType |= MF_POPUP;
         }
-        SetLastError(0);
-    }
-
-    return ret;
+	else if(!*mii.dwTypeData && !(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;
 }
 
+
 /**********************************************************************
  *           TrackPopupMenu   (USER32.@)
  *
@@ -3221,7 +488,7 @@ BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
                             INT nReserved, HWND hWnd, const RECT *lpRect )
 {
-    return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
+    return NtUserTrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL );
 }
 
 /***********************************************************************
@@ -3234,13 +501,6 @@ LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM
     switch(message)
     {
     case WM_DESTROY:
-        /* zero out global pointer in case resident popup window was destroyed. */
-        if (hwnd == top_popup) {
-            top_popup = 0;
-            top_popup_hmenu = NULL;
-        }
-        break;
-
     case WM_CREATE:
     case WM_MOUSEACTIVATE:
     case WM_PAINT:
@@ -3381,35 +641,6 @@ INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
 }
 
 
-/**********************************************************************
- *         HiliteMenuItem    (USER32.@)
- */
-BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
-                                UINT wHilite )
-{
-    POPUPMENU *menu;
-    UINT pos;
-    HMENU handle_menu;
-    UINT focused_item;
-
-    TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
-
-    if (!(menu = find_menu_item(hMenu, wItemID, wHilite, &pos))) return FALSE;
-
-    handle_menu = menu->obj.handle;
-    focused_item = menu->FocusedItem;
-    release_menu_ptr(menu);
-
-    if (focused_item != pos)
-    {
-        MENU_HideSubPopups( hWnd, handle_menu, FALSE, 0 );
-        MENU_SelectItem( hWnd, handle_menu, pos, TRUE, 0 );
-    }
-
-    return TRUE;
-}
-
-
 /**********************************************************************
  *         GetMenuState    (USER32.@)
  */
@@ -3662,93 +893,6 @@ HMENU WINAPI GetMenu( HWND hWnd )
     return retvalue;
 }
 
-/**********************************************************************
- *         GetMenuBarInfo    (USER32.@)
- */
-BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
-{
-    POPUPMENU *menu;
-    HMENU hmenu = NULL;
-    ATOM class_atom;
-
-    TRACE( "(%p,0x%08lx,0x%08lx,%p)\n", hwnd, idObject, idItem, pmbi );
-
-    switch (idObject)
-    {
-    case OBJID_CLIENT:
-        class_atom = GetClassLongW(hwnd, GCW_ATOM);
-        if (!class_atom)
-            return FALSE;
-        if (class_atom != POPUPMENU_CLASS_ATOM)
-        {
-            WARN("called on invalid window: %d\n", class_atom);
-            SetLastError(ERROR_INVALID_MENU_HANDLE);
-            return FALSE;
-        }
-
-        hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
-        break;
-    case OBJID_MENU:
-        hmenu = GetMenu(hwnd);
-        break;
-    case OBJID_SYSMENU:
-        hmenu = NtUserGetSystemMenu( hwnd, FALSE );
-        break;
-    default:
-        return FALSE;
-    }
-
-    if (!hmenu)
-        return FALSE;
-
-    if (pmbi->cbSize != sizeof(MENUBARINFO))
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
-
-    menu = MENU_GetMenu(hmenu);
-    if (!menu)
-        return FALSE;
-    if (idItem < 0 || idItem > menu->nItems)
-        return FALSE;
-
-    if (!menu->Height)
-    {
-        SetRectEmpty(&pmbi->rcBar);
-    }
-    else if (idItem == 0)
-    {
-        NtUserGetMenuItemRect( hwnd, hmenu, 0, &pmbi->rcBar );
-        pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
-        pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
-    }
-    else
-    {
-        NtUserGetMenuItemRect( hwnd, hmenu, idItem - 1, &pmbi->rcBar );
-    }
-
-    pmbi->hMenu = hmenu;
-    pmbi->hwndMenu = NULL;
-    pmbi->fBarFocused = top_popup_hmenu == hmenu;
-    if (idItem)
-    {
-        pmbi->fFocused = menu->FocusedItem == idItem - 1;
-        if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
-        {
-            menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
-            if (menu)
-                pmbi->hwndMenu = menu->hWnd;
-        }
-    }
-    else
-    {
-        pmbi->fFocused = pmbi->fBarFocused;
-    }
-
-    return TRUE;
-}
-
 
 /**********************************************************************
  *         GetSubMenu    (USER32.@)
@@ -3781,28 +925,6 @@ BOOL WINAPI DrawMenuBar( HWND hwnd )
 }
 
 
-/***********************************************************************
- *           EndMenu   (USER.187)
- *           EndMenu   (USER32.@)
- */
-BOOL WINAPI EndMenu(void)
-{
-    /* if we are in the menu code, and it is active */
-    if (!fEndMenu && top_popup)
-    {
-	/* terminate the menu handling code */
-        fEndMenu = TRUE;
-
-	/* needs to be posted to wakeup the internal menu handler */
-	/* which will now terminate the menu, in the event that */
-	/* the main window was minimized, or lost focus, so we */
-	/* don't end up with an orphaned menu */
-        PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
-    }
-    return fEndMenu;
-}
-
-
 /*****************************************************************
  *        LoadMenuA   (USER32.@)
  */
diff --git a/dlls/user32/nonclient.c b/dlls/user32/nonclient.c
index 174f6e46977..6c86c098e97 100644
--- a/dlls/user32/nonclient.c
+++ b/dlls/user32/nonclient.c
@@ -595,35 +595,6 @@ LRESULT NC_HandleNCMouseLeave(HWND hwnd)
     return 0;
 }
 
-/******************************************************************************
- *
- *   NC_DrawSysButton
- *
- *   Draws the system icon.
- *
- *****************************************************************************/
-BOOL NC_DrawSysButton (HWND hwnd, HDC hdc, BOOL down)
-{
-    HICON hIcon = NC_IconForWindow( hwnd );
-
-    if (hIcon)
-    {
-        RECT rect;
-        POINT pt;
-        DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
-        DWORD ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
-
-        NC_GetInsideRect( hwnd, COORDS_WINDOW, &rect, style, ex_style );
-        pt.x = rect.left + 2;
-        pt.y = rect.top + (GetSystemMetrics(SM_CYCAPTION) - GetSystemMetrics(SM_CYSMICON)) / 2;
-        NtUserDrawIconEx( hdc, pt.x, pt.y, hIcon,
-                          GetSystemMetrics(SM_CXSMICON),
-                          GetSystemMetrics(SM_CYSMICON), 0, 0, DI_NORMAL );
-    }
-    return (hIcon != 0);
-}
-
-
 /***********************************************************************
  *           NC_HandleSetCursor
  *
@@ -675,23 +646,6 @@ LRESULT NC_HandleSetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
     return (LRESULT)NtUserSetCursor( LoadCursorA( 0, (LPSTR)IDC_ARROW ) );
 }
 
-/***********************************************************************
- *           NC_GetSysPopupPos
- */
-void NC_GetSysPopupPos( HWND hwnd, RECT* rect )
-{
-    if (IsIconic(hwnd)) GetWindowRect( hwnd, rect );
-    else
-    {
-        DWORD style = GetWindowLongW( hwnd, GWL_STYLE );
-        DWORD ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE );
-
-        NC_GetInsideRect( hwnd, COORDS_CLIENT, rect, style, ex_style );
-        rect->right = rect->left + GetSystemMetrics(SM_CYCAPTION) - 1;
-        rect->bottom = rect->top + GetSystemMetrics(SM_CYCAPTION) - 1;
-        MapWindowPoints( hwnd, 0, (POINT *)rect, 2 );
-    }
-}
 
 
 /***********************************************************************
@@ -795,19 +749,6 @@ LRESULT NC_HandleSysCommand( HWND hwnd, WPARAM wParam, LPARAM lParam )
         }
         break;
 
-    case SC_MOUSEMENU:
-        {
-            POINT pt;
-            pt.x = (short)LOWORD(lParam);
-            pt.y = (short)HIWORD(lParam);
-            MENU_TrackMouseMenuBar( hwnd, wParam & 0x000F, pt );
-        }
-        break;
-
-    case SC_KEYMENU:
-        MENU_TrackKbdMenuBar( hwnd, wParam, (WCHAR)lParam );
-        break;
-
     case SC_TASKLIST:
         WinExec( "taskman.exe", SW_SHOWNORMAL );
         break;
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index d34bff5715e..d0262a004f1 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -210,7 +210,7 @@
 @ stdcall EnableWindow(long long)
 @ stdcall EndDeferWindowPos(long)
 @ stdcall EndDialog(long long)
-@ stdcall EndMenu()
+@ stdcall EndMenu() NtUserEndMenu
 @ stdcall EndPaint(long ptr) NtUserEndPaint
 @ stub EndTask
 # @ stub EnterReaderModeHelper
@@ -328,7 +328,7 @@
 @ stdcall GetLayeredWindowAttributes(long ptr ptr ptr) NtUserGetLayeredWindowAttributes
 @ stdcall GetListBoxInfo(long)
 @ stdcall GetMenu(long)
-@ stdcall GetMenuBarInfo(long long long ptr)
+@ stdcall GetMenuBarInfo(long long long ptr) NtUserGetMenuBarInfo
 @ stdcall GetMenuCheckMarkDimensions()
 @ stdcall GetMenuContextHelpId(long)
 @ stdcall GetMenuDefaultItem(long long long)
@@ -426,7 +426,7 @@
 @ stdcall GrayStringW(long long ptr long long long long long long)
 # @ stub HasSystemSleepStarted
 @ stdcall HideCaret(long) NtUserHideCaret
-@ stdcall HiliteMenuItem(long long long long)
+@ stdcall HiliteMenuItem(long long long long) NtUserHiliteMenuItem
 # @ stub IMPGetIMEA
 # @ stub IMPGetIMEW
 # @ stub IMPQueryIMEA
@@ -764,7 +764,7 @@
 @ stdcall ToUnicodeEx(long long ptr ptr long long long) NtUserToUnicodeEx
 @ stdcall TrackMouseEvent(ptr) NtUserTrackMouseEvent
 @ stdcall TrackPopupMenu(long long long long long long ptr)
-@ stdcall TrackPopupMenuEx(long long long long long ptr)
+@ stdcall TrackPopupMenuEx(long long long long long ptr) NtUserTrackPopupMenuEx
 @ stdcall TranslateAccelerator(long long ptr) TranslateAcceleratorA
 @ stdcall TranslateAcceleratorA(long long ptr)
 @ stdcall TranslateAcceleratorW(long long ptr) NtUserTranslateAccelerator
diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c
index bca56fc4141..3cb42e15b1e 100644
--- a/dlls/user32/user_main.c
+++ b/dlls/user32/user_main.c
@@ -32,6 +32,7 @@
 #include "user_private.h"
 #include "win.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(graphics);
 WINE_DECLARE_DEBUG_CHANNEL(message);
@@ -139,16 +140,26 @@ static void CDECL free_win_ptr( WND *win )
     HeapFree( GetProcessHeap(), 0, win->pScroll );
 }
 
+static NTSTATUS try_finally( NTSTATUS (CDECL *func)( void *), void *arg,
+                             void (CALLBACK *finally_func)( BOOL ))
+{
+    NTSTATUS status;
+    __TRY
+    {
+        status = func( arg );
+    }
+    __FINALLY( finally_func );
+    return status;
+}
+
 static const struct user_callbacks user_funcs =
 {
-    EndMenu,
     ImmProcessKey,
     ImmTranslateMessage,
     NtWaitForMultipleObjects,
     SCROLL_DrawNCScrollBar,
     free_win_ptr,
     MENU_GetSysMenu,
-    MENU_IsMenuActive,
     notify_ime,
     post_dde_message,
     process_rawinput_message,
@@ -157,6 +168,7 @@ static const struct user_callbacks user_funcs =
     unpack_dde_message,
     register_imm,
     unregister_imm,
+    try_finally,
 };
 
 static NTSTATUS WINAPI User32CopyImage( const struct copy_image_params *params, ULONG size )
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 3997e2410f8..a3a3d28c2d9 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -86,7 +86,6 @@ extern void free_cached_data( UINT format, HANDLE handle ) DECLSPEC_HIDDEN;
 extern HANDLE render_synthesized_format( UINT format, UINT from ) DECLSPEC_HIDDEN;
 
 extern void CLIPBOARD_ReleaseOwner( HWND hwnd ) DECLSPEC_HIDDEN;
-extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ) DECLSPEC_HIDDEN;
 extern HDC get_display_dc(void) DECLSPEC_HIDDEN;
 extern void release_display_dc( HDC hdc ) DECLSPEC_HIDDEN;
 extern void wait_graphics_driver_ready(void) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index 7126d492c78..75a8045f9e2 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -850,6 +850,14 @@ static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
         NtUserShowWindow( hwnd, SW_MAXIMIZE );
         break;
 
+    case SC_MOUSEMENU:
+        track_mouse_menu_bar( hwnd, wparam & 0x000F, (short)LOWORD(lparam), (short)HIWORD(lparam) );
+        break;
+
+    case SC_KEYMENU:
+        track_keyboard_menu_bar( hwnd, wparam, lparam );
+        break;
+
     case SC_RESTORE:
         if (is_iconic( hwnd )) show_owned_popups( hwnd, TRUE );
         NtUserShowWindow( hwnd, SW_RESTORE );
@@ -893,6 +901,21 @@ static void get_inside_rect( HWND hwnd, enum coords_relative relative, RECT *rec
     }
 }
 
+void get_sys_popup_pos( HWND hwnd, RECT *rect )
+{
+    if (is_iconic(hwnd)) get_window_rect( hwnd, rect, get_thread_dpi() );
+    else
+    {
+        DWORD style = get_window_long( hwnd, GWL_STYLE );
+        DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
+
+        get_inside_rect( hwnd, COORDS_CLIENT, rect, style, ex_style );
+        rect->right = rect->left + get_system_metrics( SM_CYCAPTION ) - 1;
+        rect->bottom = rect->top + get_system_metrics( SM_CYCAPTION ) - 1;
+        map_window_points( hwnd, 0, (POINT *)rect, 2, get_thread_dpi() );
+    }
+}
+
 /* Draw a window frame inside the given rectangle, and update the rectangle. */
 static void draw_nc_frame( HDC  hdc, RECT  *rect, BOOL  active, DWORD style, DWORD ex_style )
 {
@@ -1621,7 +1644,7 @@ static void handle_nc_calc_size( HWND hwnd, WPARAM wparam, RECT *win_rect )
     }
 }
 
-static LRESULT handle_nc_hit_test( HWND hwnd, POINT pt )
+LRESULT handle_nc_hit_test( HWND hwnd, POINT pt )
 {
     RECT rect, client_rect;
     DWORD style, ex_style;
@@ -2129,6 +2152,11 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
     case WM_GETDLGCODE:
         break;
 
+    case WM_CANCELMODE:
+        end_menu( hwnd );
+        if (get_capture() == hwnd) release_capture();
+        break;
+
     case WM_SETTEXT:
         result = set_window_text( hwnd, (void *)lparam, ansi );
         if (result && (get_window_long( hwnd, GWL_STYLE ) & WS_CAPTION) == WS_CAPTION)
diff --git a/dlls/win32u/gdiobj.c b/dlls/win32u/gdiobj.c
index 3c2ec6bb2e9..da2e78be2dd 100644
--- a/dlls/win32u/gdiobj.c
+++ b/dlls/win32u/gdiobj.c
@@ -1179,6 +1179,7 @@ static struct unix_funcs unix_funcs =
     NtUserGetIconInfo,
     NtUserGetKeyNameText,
     NtUserGetKeyboardLayoutList,
+    NtUserGetMenuBarInfo,
     NtUserGetMessage,
     NtUserGetPriorityClipboardFormat,
     NtUserGetQueueStatus,
@@ -1188,6 +1189,7 @@ static struct unix_funcs unix_funcs =
     NtUserGetUpdatedClipboardFormats,
     NtUserGetWindowPlacement,
     NtUserHideCaret,
+    NtUserHiliteMenuItem,
     NtUserInternalGetWindowIcon,
     NtUserInvalidateRect,
     NtUserInvalidateRgn,
@@ -1239,6 +1241,7 @@ static struct unix_funcs unix_funcs =
     NtUserSystemParametersInfoForDpi,
     NtUserToUnicodeEx,
     NtUserTrackMouseEvent,
+    NtUserTrackPopupMenuEx,
     NtUserTranslateAccelerator,
     NtUserTranslateMessage,
     NtUserUnregisterClass,
diff --git a/dlls/win32u/menu.c b/dlls/win32u/menu.c
index 8ccece38e74..6a464047427 100644
--- a/dlls/win32u/menu.c
+++ b/dlls/win32u/menu.c
@@ -49,6 +49,15 @@ enum hittest
     ht_scroll_down  /* scroll down arrow */
 };
 
+typedef struct
+{
+    UINT   trackFlags;
+    HMENU  hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
+    HMENU  hTopMenu;     /* initial menu */
+    HWND   hOwnerWnd;    /* where notifications are sent */
+    POINT  pt;
+} MTRACKER;
+
 /* maximum allowed depth of any branch in the menu tree.
  * This value is slightly larger than in windows (25) to
  * stay on the safe side. */
@@ -57,12 +66,35 @@ enum hittest
 /* (other menu->FocusedItem values give the position of the focused item) */
 #define NO_SELECTED_ITEM  0xffff
 
+/* internal flags for menu tracking */
+#define TF_ENDMENU       0x10000
+#define TF_SUSPENDPOPUP  0x20000
+#define TF_SKIPREMOVE    0x40000
+#define TF_RCVD_BTN_UP   0x80000
+
+/* Internal track_menu() flags */
+#define TPM_INTERNAL    0xf0000000
+#define TPM_BUTTONDOWN  0x40000000  /* menu was clicked before tracking */
+#define TPM_POPUPMENU   0x20000000  /* menu is a popup menu */
+
 /* Space between 2 columns */
-#define MENU_COL_SPACE 4
+#define MENU_COL_SPACE  4
+
+/* Margins for popup menus */
+#define MENU_MARGIN  3
+
+#define ITEM_PREV  -1
+#define ITEM_NEXT  1
+
+#define MENU_ITEM_TYPE(flags) \
+    ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
 
 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
-#define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
+#define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
+
+#define IS_SYSTEM_MENU(menu)  \
+    (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
 
 #define MENUITEMINFO_TYPE_MASK                                          \
     (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR |          \
@@ -73,6 +105,14 @@ enum hittest
 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
 
 
+/* Use global popup window because there's no way 2 menus can
+ * be tracked at the same time.  */
+static HWND top_popup;
+static HMENU top_popup_hmenu;
+
+/* Flag set by NtUserEndMenu() to force an exit from menu tracking */
+static BOOL exit_menu = FALSE;
+
 static SIZE menucharsize;
 static UINT od_item_hight; /* default owner drawn item height */
 
@@ -2473,6 +2513,12 @@ LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam, LPARAM l
         return 1;
 
     case WM_DESTROY:
+        /* zero out global pointer in case resident popup window was destroyed. */
+        if (hwnd == top_popup)
+        {
+            top_popup = 0;
+            top_popup_hmenu = NULL;
+        }
         break;
 
     case WM_SHOWWINDOW:
@@ -2495,6 +2541,1768 @@ LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam, LPARAM l
 
 HWND is_menu_active(void)
 {
-    if (!user_callbacks) return 0;
-    return user_callbacks->is_menu_active();
+    return top_popup;
+}
+
+/* Calculate the size of a popup menu */
+static void calc_popup_menu_size( POPUPMENU *menu, UINT max_height )
+{
+    BOOL textandbmp = FALSE, multi_col = FALSE;
+    int org_x, org_y, max_tab, max_tab_width;
+    MENUITEM *item;
+    UINT start, i;
+    HDC hdc;
+
+    menu->Width = menu->Height = 0;
+    SetRectEmpty( &menu->items_rect );
+
+    if (menu->nItems == 0) return;
+    hdc = NtUserGetDCEx( 0, 0, DCX_CACHE | DCX_WINDOW );
+
+    NtGdiSelectFont( hdc, get_menu_font( FALSE ));
+
+    start = 0;
+    menu->textOffset = 0;
+
+    while (start < menu->nItems)
+    {
+        item = &menu->items[start];
+        org_x = menu->items_rect.right;
+        if (item->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+            org_x += MENU_COL_SPACE;
+        org_y = menu->items_rect.top;
+
+        max_tab = max_tab_width = 0;
+        /* Parse items until column break or end of menu */
+        for (i = start; i < menu->nItems; i++, item++)
+        {
+            if (item->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+            {
+                multi_col = TRUE;
+                if (i != start) break;
+            }
+
+            calc_menu_item_size( hdc, item, menu->hwndOwner, org_x, org_y, FALSE, menu );
+            menu->items_rect.right = max( menu->items_rect.right, item->rect.right );
+            org_y = item->rect.bottom;
+            if (IS_STRING_ITEM( item->fType ) && item->xTab)
+            {
+                max_tab = max( max_tab, item->xTab );
+                max_tab_width = max( max_tab_width, item->rect.right-item->xTab );
+            }
+            if (item->text && item->hbmpItem) textandbmp = TRUE;
+        }
+
+        /* Finish the column (set all items to the largest width found) */
+        menu->items_rect.right = max( menu->items_rect.right, max_tab + max_tab_width );
+        for (item = &menu->items[start]; start < i; start++, item++)
+        {
+            item->rect.right = menu->items_rect.right;
+            if (IS_STRING_ITEM( item->fType ) && item->xTab)
+                item->xTab = max_tab;
+        }
+        menu->items_rect.bottom = max( menu->items_rect.bottom, org_y );
+    }
+
+    /* If none of the items have both text and bitmap then
+     * the text and bitmaps are all aligned on the left. If there is at
+     * least one item with both text and bitmap then bitmaps are
+     * on the left and texts left aligned with the right hand side
+     * of the bitmaps */
+    if (!textandbmp) menu->textOffset = 0;
+
+    menu->nTotalHeight = menu->items_rect.bottom;
+
+    /* space for the border */
+    OffsetRect( &menu->items_rect, MENU_MARGIN, MENU_MARGIN );
+    menu->Height = menu->items_rect.bottom + MENU_MARGIN;
+    menu->Width = menu->items_rect.right + MENU_MARGIN;
+
+    /* Adjust popup height if it exceeds maximum */
+    if (menu->Height >= max_height)
+    {
+        menu->Height = max_height;
+        menu->bScrolling = !multi_col;
+        /* When the scroll arrows are present, don't add the top/bottom margin as well */
+        if (menu->bScrolling)
+        {
+            menu->items_rect.top = get_scroll_arrow_height( menu );
+            menu->items_rect.bottom = menu->Height - get_scroll_arrow_height( menu );
+        }
+    }
+    else
+    {
+        menu->bScrolling = FALSE;
+    }
+
+    NtUserReleaseDC( 0, hdc );
+}
+
+static BOOL show_popup( HWND owner, HMENU hmenu, UINT id, UINT flags,
+                        int x, int y, INT xanchor, INT yanchor )
+{
+    POPUPMENU *menu;
+    HMONITOR monitor;
+    MONITORINFO info;
+    UINT max_height;
+    POINT pt;
+
+    TRACE( "owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
+           owner, hmenu, id, x, y, xanchor, yanchor );
+
+    if (!(menu = unsafe_menu_ptr( hmenu ))) return FALSE;
+    if (menu->FocusedItem != NO_SELECTED_ITEM)
+    {
+        menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
+        menu->FocusedItem = NO_SELECTED_ITEM;
+    }
+
+    menu->nScrollPos = 0;
+
+    /* FIXME: should use item rect */
+    pt.x = x;
+    pt.y = y;
+    monitor = monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, get_thread_dpi() );
+    info.cbSize = sizeof(info);
+    get_monitor_info( monitor, &info );
+
+    max_height = info.rcWork.bottom - info.rcWork.top;
+    if (menu->cyMax) max_height = min( max_height, menu->cyMax );
+    calc_popup_menu_size( menu, max_height );
+
+    /* adjust popup menu pos so that it fits within the desktop */
+    if (flags & TPM_LAYOUTRTL) flags ^= TPM_RIGHTALIGN;
+
+    if (flags & TPM_RIGHTALIGN) x -= menu->Width;
+    if (flags & TPM_CENTERALIGN) x -= menu->Width / 2;
+
+    if (flags & TPM_BOTTOMALIGN) y -= menu->Height;
+    if (flags & TPM_VCENTERALIGN) y -= menu->Height / 2;
+
+    if (x + menu->Width > info.rcWork.right)
+    {
+        if (xanchor && x >= menu->Width - xanchor) x -= menu->Width - xanchor;
+        if (x + menu->Width > info.rcWork.right) x = info.rcWork.right - menu->Width;
+    }
+    if (x < info.rcWork.left) x = info.rcWork.left;
+
+    if (y + menu->Height > info.rcWork.bottom)
+    {
+        if (yanchor && y >= menu->Height + yanchor) y -= menu->Height + yanchor;
+        if (y + menu->Height > info.rcWork.bottom) y = info.rcWork.bottom - menu->Height;
+    }
+    if (y < info.rcWork.top) y = info.rcWork.top;
+
+    if (!top_popup)
+    {
+        top_popup = menu->hWnd;
+        top_popup_hmenu = hmenu;
+    }
+
+    /* Display the window */
+    NtUserSetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
+                        SWP_SHOWWINDOW | SWP_NOACTIVATE );
+    NtUserRedrawWindow( menu->hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
+    return TRUE;
+}
+
+static void ensure_menu_item_visible( POPUPMENU *menu, UINT index, HDC hdc )
+{
+    if (menu->bScrolling)
+    {
+        MENUITEM *item = &menu->items[index];
+        UINT prev_pos = menu->nScrollPos;
+        const RECT *rc = &menu->items_rect;
+        UINT scroll_height = rc->bottom - rc->top;
+
+        if (item->rect.bottom > menu->nScrollPos + scroll_height)
+        {
+            menu->nScrollPos = item->rect.bottom - scroll_height;
+            NtUserScrollWindowEx( menu->hWnd, 0, prev_pos - menu->nScrollPos, rc, rc, 0, NULL,
+                                  SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN | SW_NODCCACHE );
+        }
+        else if (item->rect.top < menu->nScrollPos)
+        {
+            menu->nScrollPos = item->rect.top;
+            NtUserScrollWindowEx( menu->hWnd, 0, prev_pos - menu->nScrollPos, rc, rc, 0, NULL,
+                                  SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN | SW_NODCCACHE );
+        }
+
+        /* Invalidate the scroll arrows if necessary */
+        if (prev_pos != menu->nScrollPos)
+        {
+            RECT arrow_rect = menu->items_rect;
+            if (prev_pos == 0 || menu->nScrollPos == 0)
+            {
+                arrow_rect.top = 0;
+                arrow_rect.bottom = menu->items_rect.top;
+                NtUserInvalidateRect( menu->hWnd, &arrow_rect, FALSE );
+            }
+            if (prev_pos + scroll_height == menu->nTotalHeight ||
+                menu->nScrollPos + scroll_height == menu->nTotalHeight)
+            {
+                arrow_rect.top = menu->items_rect.bottom;
+                arrow_rect.bottom = menu->Height;
+                NtUserInvalidateRect( menu->hWnd, &arrow_rect, FALSE );
+            }
+        }
+    }
+}
+
+static void select_item( HWND owner, HMENU hmenu, UINT index, BOOL send_select, HMENU topmenu )
+{
+    POPUPMENU *menu;
+    HDC hdc;
+
+    TRACE( "owner %p menu %p index 0x%04x select 0x%04x\n", owner, hmenu, index, send_select );
+
+    menu = unsafe_menu_ptr( hmenu );
+    if (!menu || !menu->nItems || !menu->hWnd) return;
+
+    if (menu->FocusedItem == index) return;
+    if (menu->wFlags & MF_POPUP) hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_USESTYLE );
+    else hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
+    if (!top_popup)
+    {
+        top_popup = menu->hWnd;
+        top_popup_hmenu = hmenu;
+    }
+
+    NtGdiSelectFont( hdc, get_menu_font( FALSE ));
+
+    /* Clear previous highlighted item */
+    if (menu->FocusedItem != NO_SELECTED_ITEM)
+    {
+        menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
+        draw_menu_item( menu->hWnd, menu, owner, hdc, &menu->items[menu->FocusedItem],
+                        !(menu->wFlags & MF_POPUP), ODA_SELECT );
+    }
+
+    /* Highlight new item (if any) */
+    menu->FocusedItem = index;
+    if (menu->FocusedItem != NO_SELECTED_ITEM)
+    {
+        if (!(menu->items[index].fType & MF_SEPARATOR))
+        {
+            menu->items[index].fState |= MF_HILITE;
+            ensure_menu_item_visible( menu, index, hdc );
+            draw_menu_item( menu->hWnd, menu, owner, hdc, &menu->items[index],
+                            !(menu->wFlags & MF_POPUP), ODA_SELECT );
+        }
+        if (send_select)
+        {
+            MENUITEM *ip = &menu->items[menu->FocusedItem];
+            send_message( owner, WM_MENUSELECT,
+                          MAKEWPARAM( ip->fType & MF_POPUP ? index: ip->wID,
+                                      ip->fType | ip->fState | (menu->wFlags & MF_SYSMENU) ),
+                          (LPARAM)hmenu );
+        }
+    }
+    else if (send_select)
+    {
+        if (topmenu)
+        {
+            int pos = find_submenu( &topmenu, hmenu );
+            if (pos != NO_SELECTED_ITEM)
+            {
+                POPUPMENU *ptm = unsafe_menu_ptr( topmenu );
+                MENUITEM *ip = &ptm->items[pos];
+                send_message( owner, WM_MENUSELECT,
+                              MAKEWPARAM( pos, ip->fType | ip->fState | (ptm->wFlags & MF_SYSMENU) ),
+                              (LPARAM)topmenu );
+            }
+        }
+    }
+    NtUserReleaseDC( menu->hWnd, hdc );
+}
+
+/***********************************************************************
+ *           move_selection
+ *
+ * Moves currently selected item according to the offset parameter.
+ * If there is no selection then it should select the last item if
+ * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
+ */
+static void move_selection( HWND owner, HMENU hmenu, INT offset )
+{
+    POPUPMENU *menu;
+    int i;
+
+    TRACE( "hwnd %p hmenu %p off 0x%04x\n", owner, hmenu, offset );
+
+    menu = unsafe_menu_ptr( hmenu );
+    if (!menu || !menu->items) return;
+
+    if (menu->FocusedItem != NO_SELECTED_ITEM)
+    {
+        if (menu->nItems == 1) return;
+        for (i = menu->FocusedItem + offset; i >= 0 && i < menu->nItems; i += offset)
+        {
+            if (menu->items[i].fType & MF_SEPARATOR) continue;
+            select_item( owner, hmenu, i, TRUE, 0 );
+            return;
+        }
+    }
+
+    for (i = (offset > 0) ? 0 : menu->nItems - 1; i >= 0 && i < menu->nItems; i += offset)
+    {
+        if (menu->items[i].fType & MF_SEPARATOR) continue;
+        select_item( owner, hmenu, i, TRUE, 0 );
+        return;
+    }
+}
+
+static void hide_sub_popups( HWND owner, HMENU hmenu, BOOL send_select, UINT flags )
+{
+    POPUPMENU *menu = unsafe_menu_ptr( hmenu );
+
+    TRACE( "owner=%p hmenu=%p 0x%04x\n", owner, hmenu, send_select );
+
+    if (menu && top_popup)
+    {
+        POPUPMENU *submenu;
+        MENUITEM *item;
+        HMENU hsubmenu;
+
+        if (menu->FocusedItem == NO_SELECTED_ITEM) return;
+
+        item = &menu->items[menu->FocusedItem];
+        if (!(item->fType & MF_POPUP) || !(item->fState & MF_MOUSESELECT)) return;
+        item->fState &= ~MF_MOUSESELECT;
+        hsubmenu = item->hSubMenu;
+
+        if (!(submenu = unsafe_menu_ptr( hsubmenu ))) return;
+        hide_sub_popups( owner, hsubmenu, FALSE, flags );
+        select_item( owner, hsubmenu, NO_SELECTED_ITEM, send_select, 0 );
+        NtUserDestroyWindow( submenu->hWnd );
+        submenu->hWnd = 0;
+
+        if (!(flags & TPM_NONOTIFY))
+           send_message( owner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
+                         MAKELPARAM( 0, IS_SYSTEM_MENU( submenu )));
+    }
+}
+
+static void init_sys_menu_popup( HMENU hmenu, DWORD style, DWORD class_style )
+{
+    BOOL gray;
+
+    /* Grey the appropriate items in System menu */
+    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
+    NtUserEnableMenuItem( hmenu, SC_SIZE, gray ? MF_GRAYED : MF_ENABLED );
+    gray = ((style & WS_MAXIMIZE) != 0);
+    NtUserEnableMenuItem( hmenu, SC_MOVE, gray ? MF_GRAYED : MF_ENABLED );
+    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
+    NtUserEnableMenuItem( hmenu, SC_MINIMIZE, gray ? MF_GRAYED : MF_ENABLED );
+    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
+    NtUserEnableMenuItem( hmenu, SC_MAXIMIZE, gray ? MF_GRAYED : MF_ENABLED );
+    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
+    NtUserEnableMenuItem( hmenu, SC_RESTORE, gray ? MF_GRAYED : MF_ENABLED );
+    gray = (class_style & CS_NOCLOSE) != 0;
+
+    /* The menu item must keep its state if it's disabled */
+    if (gray) NtUserEnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED );
+}
+
+static BOOL init_popup( HWND owner, HMENU hmenu, UINT flags )
+{
+    UNICODE_STRING class_name = { .Buffer = MAKEINTRESOURCEW( POPUPMENU_CLASS_ATOM ) };
+    DWORD ex_style = 0;
+    POPUPMENU *menu;
+
+    TRACE( "owner %p hmenu %p\n", owner, hmenu );
+
+    if (!(menu = unsafe_menu_ptr( hmenu ))) return FALSE;
+
+    /* store the owner for DrawItem */
+    if (!is_window( owner ))
+    {
+        SetLastError( ERROR_INVALID_WINDOW_HANDLE );
+        return FALSE;
+    }
+    menu->hwndOwner = owner;
+
+    if (flags & TPM_LAYOUTRTL) ex_style = WS_EX_LAYOUTRTL;
+
+    /* NOTE: In Windows, top menu popup is not owned. */
+    menu->hWnd = NtUserCreateWindowEx( ex_style, &class_name, &class_name, NULL,
+                                       WS_POPUP, 0, 0, 0, 0, owner, 0,
+                                       (HINSTANCE)get_window_long_ptr( owner, GWLP_HINSTANCE, FALSE ),
+                                       (void *)hmenu, 0, NULL, 0, FALSE );
+    return !!menu->hWnd;
+}
+
+
+/***********************************************************************
+ *           show_sub_popup
+ *
+ * Display the sub-menu of the selected item of this menu.
+ * Return the handle of the submenu, or hmenu if no submenu to display.
+ */
+static HMENU show_sub_popup( HWND owner, HMENU hmenu, BOOL select_first, UINT flags )
+{
+    POPUPMENU *menu;
+    MENUITEM *item;
+    RECT rect;
+    HDC hdc;
+
+    TRACE( "owner %p hmenu %p 0x%04x\n", owner, hmenu, select_first );
+
+    if (!(menu = unsafe_menu_ptr( hmenu ))) return hmenu;
+    if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
+
+    item = &menu->items[menu->FocusedItem];
+    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
+        return hmenu;
+
+    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
+    if (!(flags & TPM_NONOTIFY))
+       send_message( owner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
+                     MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU( menu )));
+
+    item = &menu->items[menu->FocusedItem];
+    rect = item->rect;
+
+    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
+    if (!(item->fState & MF_HILITE))
+    {
+        if (menu->wFlags & MF_POPUP) hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_USESTYLE );
+        else hdc = NtUserGetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW );
+
+        NtGdiSelectFont( hdc, get_menu_font( FALSE ));
+
+        item->fState |= MF_HILITE;
+        draw_menu_item( menu->hWnd, menu, owner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
+        NtUserReleaseDC( menu->hWnd, hdc );
+    }
+    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
+        item->rect = rect;
+
+    item->fState |= MF_MOUSESELECT;
+
+    if (IS_SYSTEM_MENU( menu ))
+    {
+        init_sys_menu_popup( item->hSubMenu,
+                             get_window_long( menu->hWnd, GWL_STYLE ),
+                             get_class_long( menu->hWnd, GCL_STYLE, FALSE ));
+
+        get_sys_popup_pos( menu->hWnd, &rect );
+        if (flags & TPM_LAYOUTRTL) rect.left = rect.right;
+        rect.top = rect.bottom;
+        rect.right = get_system_metrics( SM_CXSIZE );
+        rect.bottom = get_system_metrics( SM_CYSIZE );
+    }
+    else
+    {
+        RECT item_rect = item->rect;
+
+        adjust_menu_item_rect( menu, &item_rect );
+        get_window_rect( menu->hWnd, &rect, get_thread_dpi() );
+
+        if (menu->wFlags & MF_POPUP)
+        {
+            /* The first item in the popup menu has to be at the
+               same y position as the focused menu item */
+            if (flags & TPM_LAYOUTRTL)
+                rect.left += get_system_metrics( SM_CXBORDER );
+            else
+                rect.left += item_rect.right - get_system_metrics( SM_CXBORDER );
+            rect.top += item_rect.top - MENU_MARGIN;
+            rect.right = item_rect.left - item_rect.right + get_system_metrics( SM_CXBORDER );
+            rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
+        }
+        else
+        {
+            if (flags & TPM_LAYOUTRTL)
+                rect.left = rect.right - item_rect.left;
+            else
+                rect.left += item_rect.left;
+            rect.top += item_rect.bottom;
+            rect.right = item_rect.right - item_rect.left;
+            rect.bottom = item_rect.bottom - item_rect.top;
+        }
+    }
+
+    /* use default alignment for submenus */
+    flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
+    init_popup( owner, item->hSubMenu, flags );
+    show_popup( owner, item->hSubMenu, menu->FocusedItem, flags,
+                rect.left, rect.top, rect.right, rect.bottom );
+    if (select_first) move_selection( owner, item->hSubMenu, ITEM_NEXT );
+    return item->hSubMenu;
+}
+
+/***********************************************************************
+ *           exec_focused_item
+ *
+ * Execute a menu item (for instance when user pressed Enter).
+ * Return the wID of the executed item. Otherwise, -1 indicating
+ * that no menu item was executed, -2 if a popup is shown;
+ * Have to receive the flags for the NtUserTrackPopupMenuEx options to avoid
+ * sending unwanted message.
+ */
+static INT exec_focused_item( MTRACKER *pmt, HMENU handle, UINT flags )
+{
+    MENUITEM *item;
+    POPUPMENU *menu = unsafe_menu_ptr( handle );
+
+    TRACE( "%p hmenu=%p\n", pmt, handle );
+
+    if (!menu || !menu->nItems || menu->FocusedItem == NO_SELECTED_ITEM) return -1;
+    item = &menu->items[menu->FocusedItem];
+
+    TRACE( "handle %p ID %08lx submenu %p type %04x\n", handle, item->wID,
+           item->hSubMenu, item->fType );
+
+    if ((item->fType & MF_POPUP))
+    {
+        pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, handle, TRUE, flags );
+        return -2;
+    }
+
+    if ((item->fState & (MF_GRAYED | MF_DISABLED)) || (item->fType & MF_SEPARATOR))
+        return -1;
+
+    /* If TPM_RETURNCMD is set you return the id, but
+       do not send a message to the owner */
+    if (!(flags & TPM_RETURNCMD))
+    {
+        if (menu->wFlags & MF_SYSMENU)
+            NtUserPostMessage( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
+                               MAKELPARAM( (INT16)pmt->pt.x, (INT16)pmt->pt.y ));
+        else
+        {
+            POPUPMENU *topmenu = unsafe_menu_ptr( pmt->hTopMenu );
+            DWORD style = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
+
+            if (style & MNS_NOTIFYBYPOS)
+                NtUserPostMessage( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
+                                   (LPARAM)handle);
+            else
+                NtUserPostMessage( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
+        }
+    }
+
+    return item->wID;
+}
+
+/***********************************************************************
+ *           switch_tracking
+ *
+ * Helper function for menu navigation routines.
+ */
+static void switch_tracking( MTRACKER *pmt, HMENU pt_menu, UINT id, UINT flags )
+{
+    POPUPMENU *ptmenu = unsafe_menu_ptr( pt_menu );
+    POPUPMENU *topmenu = unsafe_menu_ptr( pmt->hTopMenu );
+
+    TRACE( "%p hmenu=%p 0x%04x\n", pmt, pt_menu, id );
+
+    if (pmt->hTopMenu != pt_menu && !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP))
+    {
+        /* both are top level menus (system and menu-bar) */
+        hide_sub_popups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, flags );
+        select_item( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
+        pmt->hTopMenu = pt_menu;
+    }
+    else hide_sub_popups( pmt->hOwnerWnd, pt_menu, FALSE, flags );
+    select_item( pmt->hOwnerWnd, pt_menu, id, TRUE, 0 );
+}
+
+/***********************************************************************
+ *           menu_button_down
+ *
+ * Return TRUE if we can go on with menu tracking.
+ */
+static BOOL menu_button_down( MTRACKER *pmt, UINT message, HMENU pt_menu, UINT flags )
+{
+    TRACE( "%p pt_menu=%p\n", pmt, pt_menu );
+
+    if (pt_menu)
+    {
+        POPUPMENU *ptmenu = unsafe_menu_ptr( pt_menu );
+        enum hittest ht = ht_item;
+        UINT pos;
+
+        if (IS_SYSTEM_MENU( ptmenu ))
+        {
+            if (message == WM_LBUTTONDBLCLK) return FALSE;
+            pos = 0;
+        }
+        else
+            ht = find_item_by_coords( ptmenu, pmt->pt, &pos );
+
+        if (pos != NO_SELECTED_ITEM)
+        {
+            if (ptmenu->FocusedItem != pos)
+                switch_tracking( pmt, pt_menu, pos, flags );
+
+            /* If the popup menu is not already "popped" */
+            if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
+                pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, pt_menu, FALSE, flags );
+        }
+
+        /* A click on an item or anywhere on a popup keeps tracking going */
+        if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *           menu_button_up
+ *
+ * Return the value of exec_focused_item if
+ * the selected item was not a popup. Else open the popup.
+ * A -1 return value indicates that we go on with menu tracking.
+ *
+ */
+static INT menu_button_up( MTRACKER *pmt, HMENU pt_menu, UINT flags )
+{
+    TRACE( "%p hmenu=%p\n", pmt, pt_menu );
+
+    if (pt_menu)
+    {
+        POPUPMENU *ptmenu = unsafe_menu_ptr( pt_menu );
+        UINT pos;
+
+        if (IS_SYSTEM_MENU( ptmenu ))
+            pos = 0;
+        else if (find_item_by_coords( ptmenu, pmt->pt, &pos ) != ht_item)
+            pos = NO_SELECTED_ITEM;
+
+        if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
+        {
+            TRACE( "%s\n", debugstr_menuitem( &ptmenu->items[pos] ));
+
+            if (!(ptmenu->items[pos].fType & MF_POPUP))
+            {
+                INT executedMenuId = exec_focused_item( pmt, pt_menu, flags );
+                if (executedMenuId == -1 || executedMenuId == -2) return -1;
+                return executedMenuId;
+            }
+
+            /* If we are dealing with the menu bar and this is a click on an
+             * already "popped" item: Stop the menu tracking and close the
+             * opened submenus */
+            if(((pmt->hTopMenu == pt_menu) || IS_SYSTEM_MENU( ptmenu )) &&
+               (pmt->trackFlags & TF_RCVD_BTN_UP))
+                return 0;
+        }
+
+        if (get_menu( ptmenu->hWnd ) == pt_menu || IS_SYSTEM_MENU( ptmenu ))
+        {
+            if (pos == NO_SELECTED_ITEM) return 0;
+            pmt->trackFlags |= TF_RCVD_BTN_UP;
+        }
+    }
+    return -1;
+}
+
+/***********************************************************************
+ *           end_menu
+ *
+ * Call NtUserEndMenu() if the hwnd parameter belongs to the menu owner.
+ */
+void end_menu( HWND hwnd )
+{
+    POPUPMENU *menu;
+    BOOL call_end = FALSE;
+    if (top_popup_hmenu && (menu = grab_menu_ptr( top_popup_hmenu )))
+    {
+        call_end = hwnd == menu->hWnd || hwnd == menu->hwndOwner;
+        release_menu_ptr( menu );
+    }
+    if (call_end) NtUserEndMenu();
+}
+
+/***********************************************************************
+ *           menu_mouse_move
+ *
+ * Return TRUE if we can go on with menu tracking.
+ */
+static BOOL menu_mouse_move( MTRACKER* pmt, HMENU pt_menu, UINT flags )
+{
+    UINT id = NO_SELECTED_ITEM;
+    POPUPMENU *ptmenu = NULL;
+
+    if (pt_menu)
+    {
+        ptmenu = unsafe_menu_ptr( pt_menu );
+        if (IS_SYSTEM_MENU( ptmenu ))
+            id = 0;
+        else if (find_item_by_coords( ptmenu, pmt->pt, &id ) != ht_item)
+            id = NO_SELECTED_ITEM;
+    }
+
+    if (id == NO_SELECTED_ITEM)
+    {
+        select_item( pmt->hOwnerWnd, pmt->hCurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->hTopMenu );
+    }
+    else if (ptmenu->FocusedItem != id)
+    {
+        switch_tracking( pmt, pt_menu, id, flags );
+        pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, pt_menu, FALSE, flags );
+    }
+    return TRUE;
+}
+
+static LRESULT do_next_menu( MTRACKER *pmt, UINT vk, UINT flags )
+{
+    POPUPMENU *menu = unsafe_menu_ptr( pmt->hTopMenu );
+    BOOL at_end = FALSE;
+
+    if (vk == VK_LEFT && menu->FocusedItem == 0)
+    {
+        /* When skipping left, we need to do something special after the  first menu */
+        at_end = TRUE;
+    }
+    else if (vk == VK_RIGHT && !IS_SYSTEM_MENU( menu ))
+    {
+        /* When skipping right, for the non-system menu, we need to
+         * handle the last non-special menu item (ie skip any window
+         * icons such as MDI maximize, restore or close) */
+        UINT i = menu->FocusedItem + 1;
+        while (i < menu->nItems)
+        {
+            if (menu->items[i].wID < SC_SIZE || menu->items[i].wID > SC_RESTORE) break;
+            i++;
+        }
+        if (i == menu->nItems) at_end = TRUE;
+    }
+    else if (vk == VK_RIGHT && IS_SYSTEM_MENU( menu ))
+    {
+        /* When skipping right, we need to cater for the system menu */
+        if (menu->FocusedItem == menu->nItems - 1) at_end = TRUE;
+    }
+
+    if (at_end)
+    {
+        MDINEXTMENU next_menu;
+        HMENU new_menu;
+        HWND new_hwnd;
+        UINT id = 0;
+
+        next_menu.hmenuIn = (IS_SYSTEM_MENU( menu )) ? get_sub_menu( pmt->hTopMenu, 0 ) : pmt->hTopMenu;
+        next_menu.hmenuNext = 0;
+        next_menu.hwndNext = 0;
+        send_message( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
+
+        TRACE( "%p [%p] -> %p [%p]\n", pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext,
+               next_menu.hwndNext );
+
+        if (!next_menu.hmenuNext || !next_menu.hwndNext)
+        {
+            DWORD style = get_window_long( pmt->hOwnerWnd, GWL_STYLE );
+            new_hwnd = pmt->hOwnerWnd;
+            if (IS_SYSTEM_MENU( menu ))
+            {
+                /* switch to the menu bar */
+                if ((style & WS_CHILD) || !(new_menu = get_menu( new_hwnd ))) return FALSE;
+
+                if (vk == VK_LEFT)
+                {
+                    menu = unsafe_menu_ptr( new_menu );
+                    id = menu->nItems - 1;
+
+                    /* Skip backwards over any system predefined icons,
+                     * eg. MDI close, restore etc icons */
+                    while (id > 0 &&
+                           menu->items[id].wID >= SC_SIZE && menu->items[id].wID <= SC_RESTORE)
+                        id--;
+                }
+            }
+            else if (style & WS_SYSMENU)
+            {
+                /* switch to the system menu */
+                new_menu = get_win_sys_menu( new_hwnd );
+            }
+            else return FALSE;
+        }
+        else /* application returned a new menu to switch to */
+        {
+            new_menu = next_menu.hmenuNext;
+            new_hwnd = get_full_window_handle( next_menu.hwndNext );
+
+            if (is_menu( new_menu ) && is_window( new_hwnd ))
+            {
+                DWORD style = get_window_long( new_hwnd, GWL_STYLE );
+
+                if (style & WS_SYSMENU && get_sub_menu(get_win_sys_menu( new_hwnd ), 0) == new_menu)
+                {
+                    /* get the real system menu */
+                    new_menu = get_win_sys_menu( new_hwnd );
+                }
+                else if (style & WS_CHILD || get_menu( new_hwnd ) != new_menu )
+                {
+                    /* FIXME: what should we do? */
+                    TRACE( " -- got confused.\n" );
+                    return FALSE;
+                }
+            }
+            else return FALSE;
+        }
+
+        if (new_menu != pmt->hTopMenu)
+        {
+            select_item( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
+            if (pmt->hCurrentMenu != pmt->hTopMenu)
+                hide_sub_popups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, flags );
+        }
+
+        if (new_hwnd != pmt->hOwnerWnd)
+        {
+            pmt->hOwnerWnd = new_hwnd;
+            set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
+        }
+
+        pmt->hTopMenu = pmt->hCurrentMenu = new_menu; /* all subpopups are hidden */
+        select_item( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/***********************************************************************
+ *           get_sub_popup
+ *
+ * Return the handle of the selected sub-popup menu (if any).
+ */
+static HMENU get_sub_popup( HMENU hmenu )
+{
+    POPUPMENU *menu;
+    MENUITEM *item;
+
+    menu = unsafe_menu_ptr( hmenu );
+
+    if (!menu || menu->FocusedItem == NO_SELECTED_ITEM) return 0;
+
+    item = &menu->items[menu->FocusedItem];
+    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
+        return item->hSubMenu;
+    return 0;
+}
+
+/***********************************************************************
+ *           menu_key_escape
+ *
+ * Handle a VK_ESCAPE key event in a menu.
+ */
+static BOOL menu_key_escape( MTRACKER *pmt, UINT flags )
+{
+    BOOL ret = TRUE;
+
+    if (pmt->hCurrentMenu != pmt->hTopMenu)
+    {
+        POPUPMENU *menu = unsafe_menu_ptr( pmt->hCurrentMenu );
+
+        if (menu->wFlags & MF_POPUP)
+        {
+            HMENU top, prev_menu;
+
+            prev_menu = top = pmt->hTopMenu;
+
+            /* close topmost popup */
+            while (top != pmt->hCurrentMenu)
+            {
+                prev_menu = top;
+                top = get_sub_popup( prev_menu );
+            }
+
+            hide_sub_popups( pmt->hOwnerWnd, prev_menu, TRUE, flags );
+            pmt->hCurrentMenu = prev_menu;
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+static UINT get_start_of_next_column( HMENU handle )
+{
+    POPUPMENU *menu = unsafe_menu_ptr( handle );
+    UINT i;
+
+    if (!menu) return NO_SELECTED_ITEM;
+
+    i = menu->FocusedItem + 1;
+    if (i == NO_SELECTED_ITEM) return i;
+
+    while (i < menu->nItems)
+    {
+        if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+            return i;
+        i++;
+    }
+
+    return NO_SELECTED_ITEM;
+}
+
+static UINT get_start_of_prev_column( HMENU handle )
+{
+    POPUPMENU *menu = unsafe_menu_ptr( handle );
+    UINT i;
+
+    if (!menu) return NO_SELECTED_ITEM;
+
+    if (menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM)
+        return NO_SELECTED_ITEM;
+
+    /* Find the start of the column */
+    i = menu->FocusedItem;
+    while (i != 0 && !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))) i--;
+    if (i == 0) return NO_SELECTED_ITEM;
+
+    for (--i; i != 0; --i)
+    {
+        if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+            break;
+    }
+
+    TRACE( "ret %d.\n", i );
+    return i;
+}
+
+/***********************************************************************
+ *           suspend_popup
+ *
+ * Avoid showing the popup if the next input message is going to hide it anyway.
+ */
+static BOOL suspend_popup( MTRACKER *pmt, UINT message )
+{
+    MSG msg;
+
+    msg.hwnd = pmt->hOwnerWnd;
+    NtUserPeekMessage( &msg, 0, message, message, PM_NOYIELD | PM_REMOVE );
+    pmt->trackFlags |= TF_SKIPREMOVE;
+
+    switch (message)
+    {
+    case WM_KEYDOWN:
+        NtUserPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE );
+        if (msg.message == WM_KEYUP || msg.message == WM_PAINT)
+        {
+            NtUserPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE );
+            NtUserPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE );
+            if (msg.message == WM_KEYDOWN && (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
+            {
+                pmt->trackFlags |= TF_SUSPENDPOPUP;
+                return TRUE;
+            }
+        }
+        break;
+    }
+
+    /* failures go through this */
+    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
+    return FALSE;
+}
+
+static void menu_key_left( MTRACKER *pmt, UINT flags, UINT msg )
+{
+    POPUPMENU *menu;
+    HMENU tmp_menu, prev_menu;
+    UINT  prevcol;
+
+    prev_menu = tmp_menu = pmt->hTopMenu;
+    menu = unsafe_menu_ptr( tmp_menu );
+
+    /* Try to move 1 column left (if possible) */
+    if ((prevcol = get_start_of_prev_column( pmt->hCurrentMenu )) != NO_SELECTED_ITEM)
+    {
+        select_item( pmt->hOwnerWnd, pmt->hCurrentMenu, prevcol, TRUE, 0 );
+        return;
+    }
+
+    /* close topmost popup */
+    while (tmp_menu != pmt->hCurrentMenu)
+    {
+        prev_menu = tmp_menu;
+        tmp_menu = get_sub_popup( prev_menu );
+    }
+
+    hide_sub_popups( pmt->hOwnerWnd, prev_menu, TRUE, flags );
+    pmt->hCurrentMenu = prev_menu;
+
+    if ((prev_menu == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP))
+    {
+        /* move menu bar selection if no more popups are left */
+        if (!do_next_menu( pmt, VK_LEFT, flags ))
+            move_selection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
+
+        if (prev_menu != tmp_menu || pmt->trackFlags & TF_SUSPENDPOPUP)
+        {
+           /* A sublevel menu was displayed - display the next one
+            * unless there is another displacement coming up */
+            if (!suspend_popup( pmt, msg ))
+                pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, pmt->hTopMenu, TRUE, flags );
+        }
+    }
+}
+
+static void menu_right_key( MTRACKER *pmt, UINT flags, UINT msg )
+{
+    POPUPMENU *menu = unsafe_menu_ptr( pmt->hTopMenu );
+    HMENU tmp_menu;
+    UINT  nextcol;
+
+    TRACE( "menu_right_key called, cur %p (%s), top %p (%s).\n",
+           pmt->hCurrentMenu, debugstr_w(unsafe_menu_ptr( pmt->hCurrentMenu )->items[0].text ),
+           pmt->hTopMenu, debugstr_w( menu->items[0].text ));
+
+    if ((menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
+    {
+        /* If already displaying a popup, try to display sub-popup */
+        tmp_menu = pmt->hCurrentMenu;
+        pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, tmp_menu, TRUE, flags );
+
+        /* if subpopup was displayed then we are done */
+        if (tmp_menu != pmt->hCurrentMenu) return;
+    }
+
+    /* Check to see if there's another column */
+    if ((nextcol = get_start_of_next_column( pmt->hCurrentMenu )) != NO_SELECTED_ITEM)
+    {
+        TRACE( "Going to %d.\n", nextcol );
+        select_item( pmt->hOwnerWnd, pmt->hCurrentMenu, nextcol, TRUE, 0 );
+        return;
+    }
+
+    if (!(menu->wFlags & MF_POPUP))  /* menu bar tracking */
+    {
+        if (pmt->hCurrentMenu != pmt->hTopMenu)
+        {
+            hide_sub_popups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, flags );
+            tmp_menu = pmt->hCurrentMenu = pmt->hTopMenu;
+        }
+        else tmp_menu = 0;
+
+        /* try to move to the next item */
+        if (!do_next_menu( pmt, VK_RIGHT, flags ))
+            move_selection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
+
+        if (tmp_menu || pmt->trackFlags & TF_SUSPENDPOPUP)
+            if (!suspend_popup( pmt, msg ))
+                pmt->hCurrentMenu = show_sub_popup( pmt->hOwnerWnd, pmt->hTopMenu, TRUE, flags );
+    }
+}
+
+/***********************************************************************
+ *           menu_from_point
+ *
+ * Walks menu chain trying to find a menu pt maps to.
+ */
+static HMENU menu_from_point( HMENU handle, POINT pt )
+{
+   POPUPMENU *menu = unsafe_menu_ptr( handle );
+   UINT item = menu->FocusedItem;
+   HMENU ret = 0;
+
+   /* try subpopup first (if any) */
+   if (item != NO_SELECTED_ITEM && (menu->items[item].fType & MF_POPUP) &&
+       (menu->items[item].fState & MF_MOUSESELECT))
+       ret = menu_from_point( menu->items[item].hSubMenu, pt );
+
+   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
+   {
+       INT ht = handle_nc_hit_test( menu->hWnd, pt );
+       if (menu->wFlags & MF_POPUP)
+       {
+           if (ht != HTNOWHERE && ht != HTERROR) ret = handle;
+       }
+       else if (ht == HTSYSMENU)
+           ret = get_win_sys_menu( menu->hWnd );
+       else if (ht == HTMENU)
+           ret = get_menu( menu->hWnd );
+   }
+   return ret;
+}
+
+/***********************************************************************
+ *           find_item_by_key
+ *
+ * Find the menu item selected by a key press.
+ * Return item id, -1 if none, -2 if we should close the menu.
+ */
+static UINT find_item_by_key( HWND owner, HMENU hmenu, WCHAR key, BOOL force_menu_char )
+{
+    TRACE( "\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
+
+    if (!is_menu( hmenu )) hmenu = get_sub_menu( get_win_sys_menu( owner ), 0 );
+
+    if (hmenu)
+    {
+        POPUPMENU *menu = unsafe_menu_ptr( hmenu );
+        MENUITEM *item = menu->items;
+        LRESULT menuchar;
+
+        if (!force_menu_char)
+        {
+             BOOL cjk = get_system_metrics( SM_DBCSENABLED );
+             UINT i;
+
+             for (i = 0; i < menu->nItems; i++, item++)
+             {
+                if (item->text)
+                {
+                    const WCHAR *p = item->text - 2;
+                    do
+                    {
+                        const WCHAR *q = p + 2;
+                        p = wcschr( q, '&' );
+                        if (!p && cjk) p = wcschr( q, '\036' ); /* Japanese Win16 */
+                    }
+                    while (p && p[1] == '&');
+                    if (p && !wcsnicmp( &p[1], &key, 1)) return i;
+                }
+             }
+        }
+        menuchar = send_message( owner, WM_MENUCHAR,
+                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
+        if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD( menuchar );
+        if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)-2;
+    }
+    return -1;
+}
+
+static BOOL seh_release_capture;
+
+static void CALLBACK finally_release_capture( BOOL __normal )
+{
+    if (seh_release_capture) set_capture_window( 0, GUI_INMENUMODE, NULL );
+}
+
+static BOOL track_menu_impl( HMENU hmenu, UINT flags, int x, int y, HWND hwnd, const RECT *rect )
+{
+    BOOL enter_idle_sent = FALSE;
+    int executed_menu_id = -1;
+    HWND capture_win;
+    POPUPMENU *menu;
+    BOOL remove;
+    MTRACKER mt;
+    MSG msg;
+
+    mt.trackFlags = 0;
+    mt.hCurrentMenu = hmenu;
+    mt.hTopMenu = hmenu;
+    mt.hOwnerWnd = get_full_window_handle( hwnd );
+    mt.pt.x = x;
+    mt.pt.y = y;
+
+    TRACE( "hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
+           hmenu, flags, x, y, hwnd, wine_dbgstr_rect( rect ));
+
+    if (!(menu = unsafe_menu_ptr( hmenu )))
+    {
+        WARN( "Invalid menu handle %p\n", hmenu );
+        SetLastError( ERROR_INVALID_MENU_HANDLE );
+        return FALSE;
+    }
+
+    if (flags & TPM_BUTTONDOWN)
+    {
+        /* Get the result in order to start the tracking or not */
+        remove = menu_button_down( &mt, WM_LBUTTONDOWN, hmenu, flags );
+        exit_menu = !remove;
+    }
+
+    if (flags & TF_ENDMENU) exit_menu = TRUE;
+
+    /* owner may not be visible when tracking a popup, so use the menu itself */
+    capture_win = (flags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
+    set_capture_window( capture_win, GUI_INMENUMODE, NULL );
+
+    if ((flags & TPM_POPUPMENU) && menu->nItems == 0)
+        return FALSE;
+
+    seh_release_capture = TRUE;
+
+    while (!exit_menu)
+    {
+        if (!(menu = unsafe_menu_ptr( mt.hCurrentMenu ))) break;
+
+        /* we have to keep the message in the queue until it's
+         * clear that menu loop is not over yet. */
+        for (;;)
+        {
+            if (NtUserPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE ))
+            {
+                if (!NtUserCallMsgFilter( &msg, MSGF_MENU )) break;
+                /* remove the message from the queue */
+                NtUserPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE );
+            }
+            else
+            {
+                if (!enter_idle_sent)
+                {
+                    HWND win = (menu->wFlags & MF_POPUP) ? menu->hWnd : 0;
+                    enter_idle_sent = TRUE;
+                    send_message( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
+                }
+                NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 );
+            }
+        }
+
+        /* check if NtUserEndMenu() tried to cancel us, by posting this message */
+        if (msg.message == WM_CANCELMODE)
+        {
+            exit_menu = TRUE;
+            /* remove the message from the queue */
+            NtUserPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE );
+            break;
+        }
+
+        mt.pt = msg.pt;
+        if (msg.hwnd == menu->hWnd || msg.message != WM_TIMER) enter_idle_sent = FALSE;
+
+        remove = FALSE;
+        if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
+        {
+            /*
+             * Use the mouse coordinates in lParam instead of those in the MSG
+             * struct to properly handle synthetic messages. They are already
+             * in screen coordinates.
+             */
+            mt.pt.x = (short)LOWORD( msg.lParam );
+            mt.pt.y = (short)HIWORD( msg.lParam );
+
+            /* Find a menu for this mouse event */
+            hmenu = menu_from_point( mt.hTopMenu, mt.pt );
+
+            switch (msg.message)
+            {
+                /* no WM_NC... messages in captured state */
+                case WM_RBUTTONDBLCLK:
+                case WM_RBUTTONDOWN:
+                    if (!(flags & TPM_RIGHTBUTTON)) break;
+                    /* fall through */
+                case WM_LBUTTONDBLCLK:
+                case WM_LBUTTONDOWN:
+                    /* If the message belongs to the menu, removes it from the queue
+                     * Else, end menu tracking */
+                    remove = menu_button_down( &mt, msg.message, hmenu, flags );
+                    exit_menu = !remove;
+                    break;
+
+                case WM_RBUTTONUP:
+                    if (!(flags & TPM_RIGHTBUTTON)) break;
+                    /* fall through */
+                case WM_LBUTTONUP:
+                    /* Check if a menu was selected by the mouse */
+                    if (hmenu)
+                    {
+                        executed_menu_id = menu_button_up( &mt, hmenu, flags);
+                        TRACE( "executed_menu_id %d\n", executed_menu_id );
+
+                        /* End the loop if executed_menu_id is an item ID
+                         * or if the job was done (executed_menu_id = 0). */
+                        exit_menu = remove = executed_menu_id != -1;
+                    }
+                    else
+                        /* No menu was selected by the mouse. If the function was called by
+                         * NtUserTrackPopupMenuEx, continue with the menu tracking. */
+                        exit_menu = !(flags & TPM_POPUPMENU);
+
+                    break;
+
+                case WM_MOUSEMOVE:
+                    /* the selected menu item must be changed every time the mouse moves. */
+                    if (hmenu) exit_menu |= !menu_mouse_move( &mt, hmenu, flags );
+                    break;
+            }
+        }
+        else if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
+        {
+            remove = TRUE;  /* Keyboard messages are always removed */
+            switch (msg.message)
+            {
+            case WM_KEYDOWN:
+            case WM_SYSKEYDOWN:
+                switch(msg.wParam)
+                {
+                case VK_MENU:
+                case VK_F10:
+                    exit_menu = TRUE;
+                    break;
+
+                case VK_HOME:
+                case VK_END:
+                    select_item( mt.hOwnerWnd, mt.hCurrentMenu, NO_SELECTED_ITEM, FALSE, 0 );
+                    move_selection( mt.hOwnerWnd, mt.hCurrentMenu,
+                                    msg.wParam == VK_HOME ? ITEM_NEXT : ITEM_PREV );
+                    break;
+
+                case VK_UP:
+                case VK_DOWN: /* If on menu bar, pull-down the menu */
+                    menu = unsafe_menu_ptr( mt.hCurrentMenu );
+                    if (!(menu->wFlags & MF_POPUP))
+                        mt.hCurrentMenu = show_sub_popup( mt.hOwnerWnd, mt.hTopMenu, TRUE, flags );
+                    else  /* otherwise try to move selection */
+                        move_selection( mt.hOwnerWnd, mt.hCurrentMenu,
+                                        msg.wParam == VK_UP ? ITEM_PREV : ITEM_NEXT );
+                    break;
+
+                case VK_LEFT:
+                    menu_key_left( &mt, flags, msg.message );
+                    break;
+
+                case VK_RIGHT:
+                    menu_right_key( &mt, flags, msg.message );
+                    break;
+
+                case VK_ESCAPE:
+                    exit_menu = menu_key_escape( &mt, flags );
+                    break;
+
+                case VK_F1:
+                    {
+                        HELPINFO hi;
+                        hi.cbSize = sizeof(HELPINFO);
+                        hi.iContextType = HELPINFO_MENUITEM;
+                        if (menu->FocusedItem == NO_SELECTED_ITEM)
+                            hi.iCtrlId = 0;
+                        else
+                            hi.iCtrlId = menu->items[menu->FocusedItem].wID;
+                        hi.hItemHandle = hmenu;
+                        hi.dwContextId = menu->dwContextHelpID;
+                        hi.MousePos = msg.pt;
+                        send_message( hwnd, WM_HELP, 0, (LPARAM)&hi );
+                        break;
+                    }
+
+                default:
+                    NtUserTranslateMessage( &msg, 0 );
+                    break;
+                }
+                break;  /* WM_KEYDOWN */
+
+            case WM_CHAR:
+            case WM_SYSCHAR:
+                {
+                    UINT pos;
+
+                    if (msg.wParam == '\r' || msg.wParam == ' ')
+                    {
+                        executed_menu_id = exec_focused_item( &mt, mt.hCurrentMenu, flags );
+                        exit_menu = executed_menu_id != -2;
+                        break;
+                    }
+
+                    /* Hack to avoid control chars... */
+                    if (msg.wParam < 32) break;
+
+                    pos = find_item_by_key( mt.hOwnerWnd, mt.hCurrentMenu,
+                                            LOWORD( msg.wParam ), FALSE );
+                    if (pos == -2) exit_menu = TRUE;
+                    else if (pos == -1) message_beep( 0 );
+                    else
+                    {
+                        select_item( mt.hOwnerWnd, mt.hCurrentMenu, pos, TRUE, 0 );
+                        executed_menu_id = exec_focused_item( &mt,mt.hCurrentMenu, flags );
+                        exit_menu = executed_menu_id != -2;
+                    }
+                }
+                break;
+            }
+        }
+        else
+        {
+            NtUserPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE );
+            NtUserDispatchMessage( &msg );
+            continue;
+        }
+
+        if (!exit_menu) remove = TRUE;
+
+        /* finally remove message from the queue */
+        if (remove && !(mt.trackFlags & TF_SKIPREMOVE))
+            NtUserPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE );
+        else mt.trackFlags &= ~TF_SKIPREMOVE;
+    }
+
+    seh_release_capture = FALSE;
+    set_capture_window( 0, GUI_INMENUMODE, NULL );
+
+    /* If dropdown is still painted and the close box is clicked on
+     * then the menu will be destroyed as part of the DispatchMessage above.
+     * This will then invalidate the menu handle in mt.hTopMenu. We should
+     * check for this first.  */
+    if (is_menu( mt.hTopMenu ))
+    {
+        menu = unsafe_menu_ptr( mt.hTopMenu );
+
+        if (is_window( mt.hOwnerWnd ))
+        {
+            hide_sub_popups( mt.hOwnerWnd, mt.hTopMenu, FALSE, flags );
+
+            if (menu && (menu->wFlags & MF_POPUP))
+            {
+                NtUserDestroyWindow( menu->hWnd );
+                menu->hWnd = 0;
+
+                if (!(flags & TPM_NONOTIFY))
+                   send_message( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
+                                 MAKELPARAM( 0, IS_SYSTEM_MENU( menu )));
+            }
+            select_item( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
+            send_message( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM( 0, 0xffff ), 0 );
+        }
+    }
+
+    SetLastError( ERROR_SUCCESS );
+    /* The return value is only used by NtUserTrackPopupMenuEx */
+    if (!(flags & TPM_RETURNCMD)) return TRUE;
+    if (executed_menu_id == -1) executed_menu_id = 0;
+    return executed_menu_id;
+}
+
+/* FIXME: this is an ugly hack to work around unixlib exceptions limitations.
+ * For this to work properly we need recursive exception handlers capable of
+ * catching exceptions from client callbacks. We probably need to actually
+ * run on Unix stack first, so we need a hack for now. */
+struct track_menu_params
+{
+    HMENU handle;
+    UINT flags;
+    int x;
+    int y;
+    HWND hwnd;
+    const RECT *rect;
+};
+
+static NTSTATUS CDECL track_menu_proc( void *arg )
+{
+    struct track_menu_params *params = arg;
+    return track_menu_impl( params->handle, params->flags, params->x, params->y,
+                            params->hwnd, params->rect );
+}
+
+static BOOL track_menu( HMENU handle, UINT flags, int x, int y, HWND hwnd, const RECT *rect )
+{
+    struct track_menu_params params =
+        { .handle = handle, .flags = flags, .x = x, .y = y, .hwnd = hwnd, .rect = rect };
+    if (!user_callbacks)
+        return track_menu_impl( handle, flags, x, y, hwnd, rect );
+    return user_callbacks->try_finally( track_menu_proc, &params, finally_release_capture );
+}
+
+static BOOL init_tracking( HWND hwnd, HMENU handle, BOOL is_popup, UINT flags )
+{
+    POPUPMENU *menu;
+
+    TRACE( "hwnd=%p hmenu=%p\n", hwnd, handle );
+
+    NtUserHideCaret( 0 );
+
+    if (!(menu = unsafe_menu_ptr( handle ))) return FALSE;
+
+    /* This makes the menus of applications built with Delphi work.
+     * It also enables menus to be displayed in more than one window,
+     * but there are some bugs left that need to be fixed in this case.
+     */
+    if (!is_popup) menu->hWnd = hwnd;
+    if (!top_popup)
+    {
+        top_popup = menu->hWnd;
+        top_popup_hmenu = handle;
+    }
+
+    exit_menu = FALSE;
+
+    /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
+    if (!(flags & TPM_NONOTIFY))
+       send_message( hwnd, WM_ENTERMENULOOP, is_popup, 0 );
+
+    send_message( hwnd, WM_SETCURSOR, (WPARAM)hwnd, HTCAPTION );
+
+    if (!(flags & TPM_NONOTIFY))
+    {
+       send_message( hwnd, WM_INITMENU, (WPARAM)handle, 0 );
+       /* If an app changed/recreated menu bar entries in WM_INITMENU
+        * menu sizes will be recalculated once the menu created/shown. */
+    }
+
+    return TRUE;
+}
+
+static BOOL exit_tracking( HWND hwnd, BOOL is_popup )
+{
+    TRACE( "hwnd=%p\n", hwnd );
+
+    send_message( hwnd, WM_EXITMENULOOP, is_popup, 0 );
+    NtUserShowCaret( 0 );
+    top_popup = 0;
+    top_popup_hmenu = NULL;
+    return TRUE;
+}
+
+void track_mouse_menu_bar( HWND hwnd, INT ht, int x, int y )
+{
+    HMENU handle = ht == HTSYSMENU ? get_win_sys_menu( hwnd ) : get_menu( hwnd );
+    UINT flags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
+
+    TRACE( "wnd=%p ht=0x%04x %d,%d\n", hwnd, ht, x, y );
+
+    if (get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) flags |= TPM_LAYOUTRTL;
+    if (is_menu( handle ))
+    {
+        init_tracking( hwnd, handle, FALSE, flags );
+
+        /* fetch the window menu again, it may have changed */
+        handle = ht == HTSYSMENU ? get_win_sys_menu( hwnd ) : get_menu( hwnd );
+        track_menu( handle, flags, x, y, hwnd, NULL );
+        exit_tracking( hwnd, FALSE );
+    }
+}
+
+void track_keyboard_menu_bar( HWND hwnd, UINT wparam, WCHAR ch )
+{
+    UINT flags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
+    UINT item = NO_SELECTED_ITEM;
+    HMENU menu;
+
+    TRACE( "hwnd %p wparam 0x%04x ch 0x%04x\n", hwnd, wparam, ch );
+
+    /* find window that has a menu */
+    while (is_win_menu_disallowed( hwnd ))
+        if (!(hwnd = NtUserGetAncestor( hwnd, GA_PARENT ))) return;
+
+    /* check if we have to track a system menu */
+    menu = get_menu( hwnd );
+    if (!menu || is_iconic( hwnd ) || ch == ' ')
+    {
+        if (!(get_window_long( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
+        menu = get_win_sys_menu( hwnd );
+        item = 0;
+        wparam |= HTSYSMENU; /* prevent item lookup */
+    }
+    if (get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) flags |= TPM_LAYOUTRTL;
+
+    if (!is_menu( menu )) return;
+
+    init_tracking( hwnd, menu, FALSE, flags );
+
+    /* fetch the window menu again, it may have changed */
+    menu = (wparam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : get_menu( hwnd );
+
+    if (ch && ch != ' ')
+    {
+        item = find_item_by_key( hwnd, menu, ch, wparam & HTSYSMENU );
+        if (item >= -2)
+        {
+            if (item == -1) message_beep( 0 );
+            /* schedule end of menu tracking */
+            flags |= TF_ENDMENU;
+            goto track_menu;
+        }
+    }
+
+    select_item( hwnd, menu, item, TRUE, 0 );
+
+    if (!(wparam & HTSYSMENU) || ch == ' ')
+    {
+        if( item == NO_SELECTED_ITEM )
+            move_selection( hwnd, menu, ITEM_NEXT );
+        else
+            NtUserPostMessage( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
+    }
+
+track_menu:
+    track_menu( menu, flags, 0, 0, hwnd, NULL );
+    exit_tracking( hwnd, FALSE );
+}
+
+/**********************************************************************
+ *           NtUserTrackPopupMenuEx   (win32u.@)
+ */
+BOOL WINAPI NtUserTrackPopupMenuEx( HMENU handle, UINT flags, INT x, INT y, HWND hwnd,
+                                    TPMPARAMS *params )
+{
+    POPUPMENU *menu;
+    BOOL ret = FALSE;
+
+    TRACE( "hmenu %p flags %04x (%d,%d) hwnd %p params %p rect %s\n",
+           handle, flags, x, y, hwnd, params,
+           params ? wine_dbgstr_rect( &params->rcExclude ) : "-" );
+
+    if (!(menu = unsafe_menu_ptr( handle )))
+    {
+        SetLastError( ERROR_INVALID_MENU_HANDLE );
+        return FALSE;
+    }
+
+    if (is_window(menu->hWnd))
+    {
+        SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
+        return FALSE;
+    }
+
+    if (init_popup( hwnd, handle, flags ))
+    {
+        init_tracking( hwnd, handle, TRUE, flags );
+
+        /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
+        if (!(flags & TPM_NONOTIFY))
+            send_message( hwnd, WM_INITMENUPOPUP, (WPARAM)handle, 0 );
+
+        if (menu->wFlags & MF_SYSMENU)
+            init_sys_menu_popup( handle, get_window_long( hwnd, GWL_STYLE ),
+                                 get_class_long( hwnd, GCL_STYLE, FALSE ));
+
+        if (show_popup( hwnd, handle, 0, flags, x, y, 0, 0 ))
+            ret = track_menu( handle, flags | TPM_POPUPMENU, 0, 0, hwnd,
+                              params ? &params->rcExclude : NULL );
+        exit_tracking( hwnd, TRUE );
+
+        if (menu->hWnd)
+        {
+            NtUserDestroyWindow( menu->hWnd );
+            menu->hWnd = 0;
+
+            if (!(flags & TPM_NONOTIFY))
+                send_message( hwnd, WM_UNINITMENUPOPUP, (WPARAM)handle,
+                              MAKELPARAM( 0, IS_SYSTEM_MENU( menu )));
+        }
+        SetLastError( 0 );
+    }
+
+    return ret;
+}
+
+/**********************************************************************
+ *           NtUserHiliteMenuItem    (win32u.@)
+ */
+BOOL WINAPI NtUserHiliteMenuItem( HWND hwnd, HMENU handle, UINT item, UINT hilite )
+{
+    HMENU handle_menu;
+    UINT focused_item;
+    POPUPMENU *menu;
+    UINT pos;
+
+    TRACE( "(%p, %p, %04x, %04x);\n", hwnd, handle, item, hilite );
+
+    if (!(menu = find_menu_item(handle, item, hilite, &pos))) return FALSE;
+    handle_menu = menu->obj.handle;
+    focused_item = menu->FocusedItem;
+    release_menu_ptr(menu);
+
+    if (focused_item != pos)
+    {
+        hide_sub_popups( hwnd, handle_menu, FALSE, 0 );
+        select_item( hwnd, handle_menu, pos, TRUE, 0 );
+    }
+
+    return TRUE;
+}
+
+/**********************************************************************
+ *           NtUserGetMenuBarInfo    (win32u.@)
+ */
+BOOL WINAPI NtUserGetMenuBarInfo( HWND hwnd, LONG id, LONG item, MENUBARINFO *info )
+{
+    HMENU hmenu = NULL;
+    POPUPMENU *menu;
+    ATOM class_atom;
+
+    TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, id, item, info );
+
+    switch (id)
+    {
+    case OBJID_CLIENT:
+        class_atom = get_class_long( hwnd, GCW_ATOM, FALSE );
+        if (!class_atom)
+            return FALSE;
+        if (class_atom != POPUPMENU_CLASS_ATOM)
+        {
+            WARN("called on invalid window: %d\n", class_atom);
+            SetLastError(ERROR_INVALID_MENU_HANDLE);
+            return FALSE;
+        }
+
+        hmenu = (HMENU)get_window_long_ptr( hwnd, 0, FALSE );
+        break;
+    case OBJID_MENU:
+        hmenu = get_menu( hwnd );
+        break;
+    case OBJID_SYSMENU:
+        hmenu = NtUserGetSystemMenu( hwnd, FALSE );
+        break;
+    default:
+        return FALSE;
+    }
+
+    if (!hmenu)
+        return FALSE;
+
+    if (info->cbSize != sizeof(MENUBARINFO))
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    if (!(menu = grab_menu_ptr( hmenu ))) return FALSE;
+    if (item < 0 || item > menu->nItems)
+    {
+        release_menu_ptr( menu );
+        return FALSE;
+    }
+
+    if (!menu->Height)
+    {
+        SetRectEmpty( &info->rcBar );
+    }
+    else if (item == 0)
+    {
+        NtUserGetMenuItemRect( hwnd, hmenu, 0, &info->rcBar );
+        info->rcBar.right = info->rcBar.left + menu->Width;
+        info->rcBar.bottom = info->rcBar.top + menu->Height;
+    }
+    else
+    {
+        NtUserGetMenuItemRect( hwnd, hmenu, item - 1, &info->rcBar );
+    }
+
+    info->hMenu = hmenu;
+    info->hwndMenu = NULL;
+    info->fBarFocused = top_popup_hmenu == hmenu;
+    if (item)
+    {
+        info->fFocused = menu->FocusedItem == item - 1;
+        if (info->fFocused && (menu->items[item - 1].fType & MF_POPUP))
+        {
+            POPUPMENU *hwnd_menu = grab_menu_ptr( menu->items[item - 1].hSubMenu );
+            if (hwnd_menu)
+            {
+                info->hwndMenu = hwnd_menu->hWnd;
+                release_menu_ptr( hwnd_menu );
+            }
+        }
+    }
+    else
+    {
+        info->fFocused = info->fBarFocused;
+    }
+
+    release_menu_ptr( menu );
+    return TRUE;
+}
+
+/***********************************************************************
+ *           NtUserEndMenu   (win32u.@)
+ */
+BOOL WINAPI NtUserEndMenu(void)
+{
+    /* if we are in the menu code, and it is active, terminate the menu handling code */
+    if (!exit_menu && top_popup)
+    {
+        exit_menu = TRUE;
+
+        /* needs to be posted to wakeup the internal menu handler
+         * which will now terminate the menu, in the event that
+         * the main window was minimized, or lost focus, so we
+         * don't end up with an orphaned menu */
+        NtUserPostMessage( top_popup, WM_CANCELMODE, 0, 0 );
+    }
+    return exit_menu;
 }
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index 69fb831c03f..075fc714744 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -32,14 +32,12 @@ struct hardware_msg_data;
 
 struct user_callbacks
 {
-    BOOL (WINAPI *pEndMenu)(void);
     BOOL (WINAPI *pImmProcessKey)(HWND, HKL, UINT, LPARAM, DWORD);
     BOOL (WINAPI *pImmTranslateMessage)(HWND, UINT, WPARAM, LPARAM);
     NTSTATUS (WINAPI *pNtWaitForMultipleObjects)(ULONG,const HANDLE*,BOOLEAN,BOOLEAN,const LARGE_INTEGER*);
     void (CDECL *draw_nc_scrollbar)( HWND hwnd, HDC hdc, BOOL draw_horizontal, BOOL draw_vertical );
     void (CDECL *free_win_ptr)( struct tagWND *win );
     HMENU (CDECL *get_sys_menu)( HWND hwnd, HMENU popup );
-    HWND (CDECL *is_menu_active)(void);
     void (CDECL *notify_ime)( HWND hwnd, UINT param );
     BOOL (CDECL *post_dde_message)( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid,
                                     DWORD type );
@@ -50,6 +48,8 @@ struct user_callbacks
                                       void **buffer, size_t size );
     BOOL (WINAPI *register_imm)( HWND hwnd );
     void (WINAPI *unregister_imm)( HWND hwnd );
+    NTSTATUS (CDECL *try_finally)( NTSTATUS (CDECL *func)( void *), void *arg,
+                                   void (CALLBACK *finally_func)( BOOL ));
 };
 
 #define WM_SYSTIMER         0x0118
diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c
index a926049f0ed..6bbdfc66f50 100644
--- a/dlls/win32u/syscall.c
+++ b/dlls/win32u/syscall.c
@@ -114,6 +114,7 @@ static void * const syscalls[] =
     NtUserCreateWindowStation,
     NtUserDeleteMenu,
     NtUserDestroyAcceleratorTable,
+    NtUserEndMenu,
     NtUserFindExistingCursorIcon,
     NtUserFindWindowEx,
     NtUserGetAncestor,
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index d4ab352ccbd..01469c345c2 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -4663,7 +4663,7 @@ ULONG WINAPI NtUserGetProcessDpiAwarenessContext( HANDLE process )
     return dpi_awareness;
 }
 
-static BOOL message_beep( UINT i )
+BOOL message_beep( UINT i )
 {
     BOOL active = TRUE;
     NtUserSystemParametersInfo( SPI_GETBEEP, 0, &active, FALSE );
diff --git a/dlls/win32u/win32u.spec b/dlls/win32u/win32u.spec
index 54f5f990676..6f9dc0e921b 100644
--- a/dlls/win32u/win32u.spec
+++ b/dlls/win32u/win32u.spec
@@ -873,7 +873,7 @@
 @ stub NtUserEnableWindowGroupPolicy
 @ stub NtUserEnableWindowResizeOptimization
 @ stdcall NtUserEndDeferWindowPosEx(long long)
-@ stub NtUserEndMenu
+@ stdcall -syscall NtUserEndMenu()
 @ stdcall NtUserEndPaint(long ptr)
 @ stdcall NtUserEnumDisplayDevices(ptr long ptr long)
 @ stdcall NtUserEnumDisplayMonitors(long ptr ptr long)
@@ -952,7 +952,7 @@
 @ stdcall -syscall NtUserGetKeyboardState(ptr)
 @ stdcall -syscall NtUserGetLayeredWindowAttributes(long ptr ptr ptr)
 @ stub NtUserGetListBoxInfo
-@ stub NtUserGetMenuBarInfo
+@ stdcall NtUserGetMenuBarInfo(long long long ptr)
 @ stub NtUserGetMenuIndex
 @ stdcall -syscall NtUserGetMenuItemRect(long long long ptr)
 @ stdcall NtUserGetMessage(ptr long long long)
@@ -1022,7 +1022,7 @@
 @ stub NtUserHardErrorControl
 @ stdcall NtUserHideCaret(long)
 @ stub NtUserHidePointerContactVisualization
-@ stub NtUserHiliteMenuItem
+@ stdcall NtUserHiliteMenuItem(long long long long)
 @ stub NtUserHungWindowFromGhostWindow
 @ stub NtUserHwndQueryRedirectionInfo
 @ stub NtUserHwndSetRedirectionInfo
@@ -1278,7 +1278,7 @@
 @ stdcall -syscall NtUserThunkedMenuItemInfo(long long long long ptr ptr)
 @ stdcall NtUserToUnicodeEx(long long ptr ptr long long long)
 @ stdcall NtUserTrackMouseEvent(ptr)
-@ stub NtUserTrackPopupMenuEx
+@ stdcall NtUserTrackPopupMenuEx(long long long long long ptr)
 @ stub NtUserTransformPoint
 @ stub NtUserTransformRect
 @ stdcall NtUserTranslateAccelerator(long long ptr)
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index 23343c413dc..dc62f6846a5 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -241,6 +241,7 @@ struct unix_funcs
                                            UNICODE_STRING *res_name, DWORD *bpp, LONG unk );
     INT      (WINAPI *pNtUserGetKeyNameText)( LONG lparam, WCHAR *buffer, INT size );
     UINT     (WINAPI *pNtUserGetKeyboardLayoutList)( INT size, HKL *layouts );
+    BOOL     (WINAPI *pNtUserGetMenuBarInfo)( HWND hwnd, LONG id, LONG item, MENUBARINFO *info );
     BOOL     (WINAPI *pNtUserGetMessage)( MSG *msg, HWND hwnd, UINT first, UINT last );
     INT      (WINAPI *pNtUserGetPriorityClipboardFormat)( UINT *list, INT count );
     DWORD    (WINAPI *pNtUserGetQueueStatus)( UINT flags );
@@ -250,6 +251,7 @@ struct unix_funcs
     BOOL     (WINAPI *pNtUserGetUpdatedClipboardFormats)( UINT *formats, UINT size, UINT *out_size );
     BOOL     (WINAPI *pNtUserGetWindowPlacement)( HWND hwnd, WINDOWPLACEMENT *placement );
     BOOL     (WINAPI *pNtUserHideCaret)( HWND hwnd );
+    BOOL     (WINAPI *pNtUserHiliteMenuItem)( HWND hwnd, HMENU handle, UINT item, UINT hilite );
     HICON    (WINAPI *pNtUserInternalGetWindowIcon)( HWND hwnd, UINT type );
     BOOL     (WINAPI *pNtUserInvalidateRect)( HWND hwnd, const RECT *rect, BOOL erase );
     BOOL     (WINAPI *pNtUserInvalidateRgn)( HWND hwnd, HRGN hrgn, BOOL erase );
@@ -313,6 +315,8 @@ struct unix_funcs
     INT      (WINAPI *pNtUserToUnicodeEx)( UINT virt, UINT scan, const BYTE *state,
                                            WCHAR *str, int size, UINT flags, HKL layout );
     BOOL     (WINAPI *pNtUserTrackMouseEvent)( TRACKMOUSEEVENT *info );
+    BOOL     (WINAPI *pNtUserTrackPopupMenuEx)( HMENU handle, UINT flags, INT x, INT y, HWND hwnd,
+                                                TPMPARAMS *params );
     INT      (WINAPI *pNtUserTranslateAccelerator)( HWND hwnd, HACCEL accel, MSG *msg );
     BOOL     (WINAPI *pNtUserTranslateMessage)( const MSG *msg, UINT flags );
     BOOL     (WINAPI *pNtUserUnregisterClass)( UNICODE_STRING *name, HINSTANCE instance,
@@ -373,6 +377,8 @@ extern BOOL draw_frame_menu( HDC dc, RECT *r, UINT flags ) DECLSPEC_HIDDEN;
 extern BOOL draw_nc_sys_button( HWND hwnd, HDC hdc, BOOL down ) DECLSPEC_HIDDEN;
 extern BOOL draw_rect_edge( HDC hdc, RECT *rc, UINT uType, UINT uFlags, UINT width ) DECLSPEC_HIDDEN;
 extern void fill_rect( HDC dc, const RECT *rect, HBRUSH hbrush ) DECLSPEC_HIDDEN;
+extern void get_sys_popup_pos( HWND hwnd, RECT *rect ) DECLSPEC_HIDDEN;
+extern LRESULT handle_nc_hit_test( HWND hwnd, POINT pt ) DECLSPEC_HIDDEN;
 
 /* hook.c */
 extern LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam ) DECLSPEC_HIDDEN;
@@ -398,6 +404,8 @@ extern void update_mouse_tracking_info( HWND hwnd ) DECLSPEC_HIDDEN;
 /* menu.c */
 extern HMENU create_menu( BOOL is_popup ) DECLSPEC_HIDDEN;
 extern BOOL draw_menu_bar( HWND hwnd ) DECLSPEC_HIDDEN;
+extern UINT draw_nc_menu_bar( HDC hdc, RECT *rect, HWND hwnd ) DECLSPEC_HIDDEN;
+extern void end_menu( HWND hwnd ) DECLSPEC_HIDDEN;
 extern HMENU get_menu( HWND hwnd ) DECLSPEC_HIDDEN;
 extern UINT get_menu_bar_height( HWND hwnd, UINT width, INT org_x, INT org_y ) DECLSPEC_HIDDEN;
 extern BOOL get_menu_info( HMENU handle, MENUINFO *info ) DECLSPEC_HIDDEN;
@@ -408,6 +416,8 @@ extern HWND is_menu_active(void) DECLSPEC_HIDDEN;
 extern LRESULT popup_menu_window_proc( HWND hwnd, UINT message, WPARAM wparam,
                                        LPARAM lparam ) DECLSPEC_HIDDEN;
 extern BOOL set_window_menu( HWND hwnd, HMENU handle ) DECLSPEC_HIDDEN;
+extern void track_keyboard_menu_bar( HWND hwnd, UINT wparam, WCHAR ch ) DECLSPEC_HIDDEN;
+extern void track_mouse_menu_bar( HWND hwnd, INT ht, int x, int y ) DECLSPEC_HIDDEN;
 
 /* message.c */
 extern LRESULT dispatch_message( const MSG *msg, BOOL ansi ) DECLSPEC_HIDDEN;
@@ -444,6 +454,7 @@ extern RECT get_virtual_screen_rect( UINT dpi ) DECLSPEC_HIDDEN;
 extern BOOL is_exiting_thread( DWORD tid ) DECLSPEC_HIDDEN;
 extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN;
 extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to ) DECLSPEC_HIDDEN;
+extern BOOL message_beep( UINT i ) DECLSPEC_HIDDEN;
 extern POINT point_phys_to_win_dpi( HWND hwnd, POINT pt ) DECLSPEC_HIDDEN;
 extern POINT point_thread_to_win_dpi( HWND hwnd, POINT pt ) DECLSPEC_HIDDEN;
 extern RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect ) DECLSPEC_HIDDEN;
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 42274905d5b..e8562f6a685 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -4759,8 +4759,7 @@ BOOL WINAPI NtUserDestroyWindow( HWND hwnd )
 
     if (call_hooks( WH_CBT, HCBT_DESTROYWND, (WPARAM)hwnd, 0, TRUE )) return FALSE;
 
-    if (user_callbacks && is_menu_active() == hwnd)
-        user_callbacks->pEndMenu();
+    if (is_menu_active() == hwnd) NtUserEndMenu();
 
     is_child = (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) != 0;
 
@@ -5505,9 +5504,6 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
     case NtUserIsWindowDrawable:
         return is_window_drawable( hwnd, param );
 
-    case NtUserSetCaptureWindow:
-        return set_capture_window( hwnd, param, NULL );
-
     case NtUserSetWindowStyle:
         {
             STYLESTRUCT *style = (void *)param;
diff --git a/dlls/win32u/wrappers.c b/dlls/win32u/wrappers.c
index 945ea5d7fd5..ed4337eae84 100644
--- a/dlls/win32u/wrappers.c
+++ b/dlls/win32u/wrappers.c
@@ -981,6 +981,12 @@ INT WINAPI NtUserGetKeyNameText( LONG lparam, WCHAR *buffer, INT size )
     return unix_funcs->pNtUserGetKeyNameText( lparam, buffer, size );
 }
 
+BOOL WINAPI NtUserGetMenuBarInfo( HWND hwnd, LONG id, LONG item, MENUBARINFO *info )
+{
+    if (!unix_funcs) return 0;
+    return unix_funcs->pNtUserGetMenuBarInfo( hwnd, id, item, info );
+}
+
 BOOL WINAPI NtUserGetMessage( MSG *msg, HWND hwnd, UINT first, UINT last )
 {
     if (!unix_funcs) return FALSE;
@@ -1011,6 +1017,12 @@ BOOL WINAPI NtUserHideCaret( HWND hwnd )
     return unix_funcs->pNtUserHideCaret( hwnd );
 }
 
+BOOL WINAPI NtUserHiliteMenuItem( HWND hwnd, HMENU handle, UINT item, UINT hilite )
+{
+    if (!unix_funcs) return FALSE;
+    return unix_funcs->pNtUserHiliteMenuItem( hwnd, handle, item, hilite );
+}
+
 BOOL WINAPI NtUserMoveWindow( HWND hwnd, INT x, INT y, INT cx, INT cy, BOOL repaint )
 {
     if (!unix_funcs) return 0;
@@ -1352,6 +1364,13 @@ BOOL WINAPI NtUserTrackMouseEvent( TRACKMOUSEEVENT *info )
     return unix_funcs->pNtUserTrackMouseEvent( info );
 }
 
+BOOL WINAPI NtUserTrackPopupMenuEx( HMENU handle, UINT flags, INT x, INT y, HWND hwnd,
+                                    TPMPARAMS *params )
+{
+    if (!unix_funcs) return FALSE;
+    return unix_funcs->pNtUserTrackPopupMenuEx( handle, flags, x, y, hwnd, params );
+}
+
 INT WINAPI NtUserTranslateAccelerator( HWND hwnd, HACCEL accel, MSG *msg )
 {
     if (!unix_funcs) return 0;
diff --git a/dlls/wow64win/syscall.h b/dlls/wow64win/syscall.h
index 7c9627a6121..27e1ba76c56 100644
--- a/dlls/wow64win/syscall.h
+++ b/dlls/wow64win/syscall.h
@@ -101,6 +101,7 @@
     SYSCALL_ENTRY( NtUserCreateWindowStation ) \
     SYSCALL_ENTRY( NtUserDeleteMenu ) \
     SYSCALL_ENTRY( NtUserDestroyAcceleratorTable ) \
+    SYSCALL_ENTRY( NtUserEndMenu ) \
     SYSCALL_ENTRY( NtUserFindExistingCursorIcon ) \
     SYSCALL_ENTRY( NtUserFindWindowEx ) \
     SYSCALL_ENTRY( NtUserGetAncestor ) \
diff --git a/dlls/wow64win/user.c b/dlls/wow64win/user.c
index 51c0a5d0ed8..6208c939310 100644
--- a/dlls/wow64win/user.c
+++ b/dlls/wow64win/user.c
@@ -726,6 +726,11 @@ NTSTATUS WINAPI wow64_NtUserDeleteMenu( UINT *args )
     return NtUserDeleteMenu( menu, id, flags );
 }
 
+NTSTATUS WINAPI wow64_NtUserEndMenu( UINT *args )
+{
+    return NtUserEndMenu();
+}
+
 NTSTATUS WINAPI wow64_NtUserGetMenuItemRect( UINT *args )
 {
     HWND hwnd = get_handle( &args );
diff --git a/include/ntuser.h b/include/ntuser.h
index 0904aadff0f..56c920aacdf 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -544,6 +544,7 @@ DWORD   WINAPI NtUserDrawMenuBarTemp( HWND hwnd, HDC hdc, RECT *rect, HMENU hand
 BOOL    WINAPI NtUserEmptyClipboard(void);
 BOOL    WINAPI NtUserEnableMenuItem( HMENU handle, UINT id, UINT flags );
 BOOL    WINAPI NtUserEndDeferWindowPosEx( HDWP hdwp, BOOL async );
+BOOL    WINAPI NtUserEndMenu(void);
 BOOL    WINAPI NtUserEndPaint( HWND hwnd, const PAINTSTRUCT *ps );
 NTSTATUS WINAPI NtUserEnumDisplayDevices( UNICODE_STRING *device, DWORD index,
                                           DISPLAY_DEVICEW *info, DWORD flags );
@@ -590,6 +591,7 @@ UINT    WINAPI NtUserGetKeyboardLayoutList( INT size, HKL *layouts );
 BOOL    WINAPI NtUserGetKeyboardLayoutName( WCHAR *name );
 BOOL    WINAPI NtUserGetKeyboardState( BYTE *state );
 BOOL    WINAPI NtUserGetLayeredWindowAttributes( HWND hwnd, COLORREF *key, BYTE *alpha, DWORD *flags );
+BOOL    WINAPI NtUserGetMenuBarInfo( HWND hwnd, LONG id, LONG item, MENUBARINFO *info );
 BOOL    WINAPI NtUserGetMenuItemRect( HWND hwnd, HMENU menu, UINT item, RECT *rect );
 BOOL    WINAPI NtUserGetMessage( MSG *msg, HWND hwnd, UINT first, UINT last );
 int     WINAPI NtUserGetMouseMovePointsEx( UINT size, MOUSEMOVEPOINT *ptin, MOUSEMOVEPOINT *ptout,
@@ -611,6 +613,7 @@ BOOL    WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase );
 BOOL    WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement );
 int     WINAPI NtUserGetWindowRgnEx( HWND hwnd, HRGN hrgn, UINT unk );
 BOOL    WINAPI NtUserHideCaret( HWND hwnd );
+BOOL    WINAPI NtUserHiliteMenuItem( HWND hwnd, HMENU handle, UINT item, UINT hilite );
 NTSTATUS WINAPI NtUserInitializeClientPfnArrays( const struct user_client_procs *client_procsA,
                                                  const struct user_client_procs *client_procsW,
                                                  const void *client_workers, HINSTANCE user_module );
@@ -703,6 +706,7 @@ UINT    WINAPI NtUserThunkedMenuItemInfo( HMENU menu, UINT pos, UINT flags, UINT
 INT     WINAPI NtUserToUnicodeEx( UINT virt, UINT scan, const BYTE *state,
                                   WCHAR *str, int size, UINT flags, HKL layout );
 BOOL    WINAPI NtUserTrackMouseEvent( TRACKMOUSEEVENT *info );
+BOOL    WINAPI NtUserTrackPopupMenuEx( HMENU handle, UINT flags, INT x, INT y, HWND hwnd, TPMPARAMS *params );
 INT     WINAPI NtUserTranslateAccelerator( HWND hwnd, HACCEL accel, MSG *msg );
 BOOL    WINAPI NtUserTranslateMessage( const MSG *msg, UINT flags );
 BOOL    WINAPI NtUserUnhookWinEvent( HWINEVENTHOOK hEventHook );
@@ -1078,7 +1082,6 @@ enum
     NtUserCallHwndParam_ShowOwnedPopups,
     /* temporary exports */
     NtUserIsWindowDrawable,
-    NtUserSetCaptureWindow,
     NtUserSetWindowStyle,
     NtUserSpyGetMsgName,
 };
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/258



More information about the wine-devel mailing list