The first patch is much larger than I'd like, but the combination of global variables and dependencies between functions made attempts to split it ugly and it didn't feel like an improvement.
From: Jacek Caban jacek@codeweavers.com
Signed-off-by: Jacek Caban jacek@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, };
From: Jacek Caban jacek@codeweavers.com
Signed-off-by: Jacek Caban jacek@codeweavers.com --- dlls/user32/controls.h | 1 - dlls/user32/defwnd.c | 26 ------ dlls/user32/nonclient.c | 182 ---------------------------------------- dlls/win32u/defwnd.c | 26 ++++++ 4 files changed, 26 insertions(+), 209 deletions(-)
diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h index 4fb21c9f0e3..3fadcc113c0 100644 --- a/dlls/user32/controls.h +++ b/dlls/user32/controls.h @@ -119,7 +119,6 @@ extern void MENU_EndMenu(HWND) DECLSPEC_HIDDEN; extern HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu ) DECLSPEC_HIDDEN;
/* nonclient area */ -extern LRESULT NC_HandleNCHitTest( HWND hwnd, POINT pt ) DECLSPEC_HIDDEN; extern LRESULT NC_HandleNCMouseMove( HWND hwnd, WPARAM wParam, LPARAM lParam ) DECLSPEC_HIDDEN; extern LRESULT NC_HandleNCMouseLeave( HWND hwnd ) DECLSPEC_HIDDEN; extern LRESULT NC_HandleNCLButtonDblClk( HWND hwnd, WPARAM wParam, LPARAM lParam) DECLSPEC_HIDDEN; diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c index 4c8748f10cb..57e12d8e00b 100644 --- a/dlls/user32/defwnd.c +++ b/dlls/user32/defwnd.c @@ -209,32 +209,6 @@ static LRESULT DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa } break;
- case WM_CONTEXTMENU: - if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD) - SendMessageW( GetParent(hwnd), msg, (WPARAM)hwnd, lParam ); - else - { - LONG hitcode; - POINT pt; - pt.x = (short)LOWORD(lParam); - pt.y = (short)HIWORD(lParam); - hitcode = NC_HandleNCHitTest(hwnd, pt); - - /* Track system popup if click was in the caption area. */ - if (hitcode==HTCAPTION || hitcode==HTSYSMENU) - TrackPopupMenu( NtUserGetSystemMenu(hwnd, FALSE), - TPM_LEFTBUTTON | TPM_RIGHTBUTTON, - pt.x, pt.y, 0, hwnd, NULL ); - } - break; - - case WM_POPUPSYSTEMMENU: - /* This is an undocumented message used by the windows taskbar to - display the system menu of windows that belong to other processes. */ - TrackPopupMenu( NtUserGetSystemMenu(hwnd, FALSE), TPM_LEFTBUTTON|TPM_RIGHTBUTTON, - (short)LOWORD(lParam), (short)HIWORD(lParam), 0, hwnd, NULL ); - return 0; - case WM_PRINT: DEFWND_Print(hwnd, (HDC)wParam, lParam); return 0; diff --git a/dlls/user32/nonclient.c b/dlls/user32/nonclient.c index 6c86c098e97..b803c99d43b 100644 --- a/dlls/user32/nonclient.c +++ b/dlls/user32/nonclient.c @@ -377,188 +377,6 @@ static void NC_GetInsideRect( HWND hwnd, enum coords_relative relative, RECT *re } }
- -/*********************************************************************** - * NC_HandleNCHitTest - * - * Handle a WM_NCHITTEST message. Called from DefWindowProc(). - */ -LRESULT NC_HandleNCHitTest( HWND hwnd, POINT pt ) -{ - RECT rect, rcClient; - DWORD style, ex_style; - - TRACE("hwnd=%p pt=%ld,%ld\n", hwnd, pt.x, pt.y ); - - WIN_GetRectangles( hwnd, COORDS_SCREEN, &rect, &rcClient ); - if (!PtInRect( &rect, pt )) return HTNOWHERE; - - style = GetWindowLongW( hwnd, GWL_STYLE ); - ex_style = GetWindowLongW( hwnd, GWL_EXSTYLE ); - - if (PtInRect( &rcClient, pt )) return HTCLIENT; - - /* Check borders */ - if (HAS_THICKFRAME( style, ex_style )) - { - InflateRect( &rect, -GetSystemMetrics(SM_CXFRAME), -GetSystemMetrics(SM_CYFRAME) ); - if (!PtInRect( &rect, pt )) - { - /* Check top sizing border */ - if (pt.y < rect.top) - { - if (pt.x < rect.left+GetSystemMetrics(SM_CXSIZE)) return HTTOPLEFT; - if (pt.x >= rect.right-GetSystemMetrics(SM_CXSIZE)) return HTTOPRIGHT; - return HTTOP; - } - /* Check bottom sizing border */ - if (pt.y >= rect.bottom) - { - if (pt.x < rect.left+GetSystemMetrics(SM_CXSIZE)) return HTBOTTOMLEFT; - if (pt.x >= rect.right-GetSystemMetrics(SM_CXSIZE)) return HTBOTTOMRIGHT; - return HTBOTTOM; - } - /* Check left sizing border */ - if (pt.x < rect.left) - { - if (pt.y < rect.top+GetSystemMetrics(SM_CYSIZE)) return HTTOPLEFT; - if (pt.y >= rect.bottom-GetSystemMetrics(SM_CYSIZE)) return HTBOTTOMLEFT; - return HTLEFT; - } - /* Check right sizing border */ - if (pt.x >= rect.right) - { - if (pt.y < rect.top+GetSystemMetrics(SM_CYSIZE)) return HTTOPRIGHT; - if (pt.y >= rect.bottom-GetSystemMetrics(SM_CYSIZE)) return HTBOTTOMRIGHT; - return HTRIGHT; - } - } - } - else /* No thick frame */ - { - if (HAS_DLGFRAME( style, ex_style )) - InflateRect(&rect, -GetSystemMetrics(SM_CXDLGFRAME), -GetSystemMetrics(SM_CYDLGFRAME)); - else if (HAS_THINFRAME( style )) - InflateRect(&rect, -GetSystemMetrics(SM_CXBORDER), -GetSystemMetrics(SM_CYBORDER)); - if (!PtInRect( &rect, pt )) return HTBORDER; - } - - /* Check caption */ - - if ((style & WS_CAPTION) == WS_CAPTION) - { - if (ex_style & WS_EX_TOOLWINDOW) - rect.top += GetSystemMetrics(SM_CYSMCAPTION) - 1; - else - rect.top += GetSystemMetrics(SM_CYCAPTION) - 1; - if (!PtInRect( &rect, pt )) - { - BOOL min_or_max_box = (style & WS_SYSMENU) && (style & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)); - if (ex_style & WS_EX_LAYOUTRTL) - { - /* Check system menu */ - if ((style & WS_SYSMENU) && !(ex_style & WS_EX_TOOLWINDOW) && NC_IconForWindow(hwnd)) - { - rect.right -= GetSystemMetrics(SM_CYCAPTION) - 1; - if (pt.x > rect.right) return HTSYSMENU; - } - - /* Check close button */ - if (style & WS_SYSMENU) - { - rect.left += GetSystemMetrics(SM_CYCAPTION); - if (pt.x < rect.left) return HTCLOSE; - } - - /* Check maximize box */ - /* In win95 there is automatically a Maximize button when there is a minimize one*/ - if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW)) - { - rect.left += GetSystemMetrics(SM_CXSIZE); - if (pt.x < rect.left) return HTMAXBUTTON; - } - - /* Check minimize box */ - if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW)) - { - rect.left += GetSystemMetrics(SM_CXSIZE); - if (pt.x < rect.left) return HTMINBUTTON; - } - } - else - { - /* Check system menu */ - if ((style & WS_SYSMENU) && !(ex_style & WS_EX_TOOLWINDOW) && NC_IconForWindow(hwnd)) - { - rect.left += GetSystemMetrics(SM_CYCAPTION) - 1; - if (pt.x < rect.left) return HTSYSMENU; - } - - /* Check close button */ - if (style & WS_SYSMENU) - { - rect.right -= GetSystemMetrics(SM_CYCAPTION); - if (pt.x > rect.right) return HTCLOSE; - } - - /* Check maximize box */ - /* In win95 there is automatically a Maximize button when there is a minimize one*/ - if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW)) - { - rect.right -= GetSystemMetrics(SM_CXSIZE); - if (pt.x > rect.right) return HTMAXBUTTON; - } - - /* Check minimize box */ - if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW)) - { - rect.right -= GetSystemMetrics(SM_CXSIZE); - if (pt.x > rect.right) return HTMINBUTTON; - } - } - return HTCAPTION; - } - } - - /* Check menu bar */ - - if (HAS_MENU( hwnd, style ) && (pt.y < rcClient.top) && - (pt.x >= rcClient.left) && (pt.x < rcClient.right)) - return HTMENU; - - /* Check vertical scroll bar */ - - if (ex_style & WS_EX_LAYOUTRTL) ex_style ^= WS_EX_LEFTSCROLLBAR; - if (style & WS_VSCROLL) - { - if((ex_style & WS_EX_LEFTSCROLLBAR) != 0) - rcClient.left -= GetSystemMetrics(SM_CXVSCROLL); - else - rcClient.right += GetSystemMetrics(SM_CXVSCROLL); - if (PtInRect( &rcClient, pt )) return HTVSCROLL; - } - - /* Check horizontal scroll bar */ - - if (style & WS_HSCROLL) - { - rcClient.bottom += GetSystemMetrics(SM_CYHSCROLL); - if (PtInRect( &rcClient, pt )) - { - /* Check size box */ - if ((style & WS_VSCROLL) && - ((((ex_style & WS_EX_LEFTSCROLLBAR) != 0) && (pt.x <= rcClient.left + GetSystemMetrics(SM_CXVSCROLL))) || - (((ex_style & WS_EX_LEFTSCROLLBAR) == 0) && (pt.x >= rcClient.right - GetSystemMetrics(SM_CXVSCROLL))))) - return HTSIZE; - return HTHSCROLL; - } - } - - /* Has to return HTNOWHERE if nothing was found - Could happen when a window has a customized non client area */ - return HTNOWHERE; -} - LRESULT NC_HandleNCMouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam) { RECT rect; diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index 75a8045f9e2..b438b6d5275 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2063,6 +2063,32 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, case WM_NCRBUTTONDOWN: return handle_nc_rbutton_down( hwnd, wparam, lparam );
+ case WM_CONTEXTMENU: + if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD) + send_message( get_parent( hwnd ), msg, (WPARAM)hwnd, lparam ); + else + { + LONG hitcode; + POINT pt; + pt.x = (short)LOWORD( lparam ); + pt.y = (short)HIWORD( lparam ); + hitcode = handle_nc_hit_test( hwnd, pt ); + + /* Track system popup if click was in the caption area. */ + if (hitcode == HTCAPTION || hitcode == HTSYSMENU) + NtUserTrackPopupMenuEx( NtUserGetSystemMenu( hwnd, FALSE ), + TPM_LEFTBUTTON | TPM_RIGHTBUTTON, + pt.x, pt.y, hwnd, NULL ); + } + break; + + case WM_POPUPSYSTEMMENU: + /* This is an undocumented message used by the windows taskbar to + * display the system menu of windows that belong to other processes. */ + NtUserTrackPopupMenuEx( NtUserGetSystemMenu( hwnd, FALSE ), TPM_LEFTBUTTON | TPM_RIGHTBUTTON, + (short)LOWORD(lparam), (short)HIWORD(lparam), hwnd, NULL ); + return 0; + case WM_WINDOWPOSCHANGING: return handle_window_pos_changing( hwnd, (WINDOWPOS *)lparam );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=117102
Your paranoid android.
=== debian11 (32 bit WoW report) ===
user32: win.c:11770: Test succeeded inside todo block: child should be topmost win.c:11773: Test failed: grandchild should be topmost win.c:11781: Test failed: child should NOT be topmost win.c:11784: Test succeeded inside todo block: grandchild should NOT be topmost win.c:11787: Test failed: 007200BA: expected NOT topmost win.c:11606: Test succeeded inside todo block: 5: hwnd 01580052 is still topmost
This merge request was approved by Huw Davies.