[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, ¶ms, 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( ¶ms->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 ? ¶ms->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