From b9d8a842f0ba1e4c3c59b9e9ede667bea1761e6e Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Sun, 12 Jun 2022 16:52:40 +0200 Subject: [PATCH] win32u: Move menu tracking implementation from user32. Signed-off-by: Jacek Caban --- dlls/user32/controls.h | 3 - dlls/user32/defwnd.c | 3 +- dlls/user32/input.c | 10 - dlls/user32/menu.c | 5854 +++++++++++------------------------------- 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, 3400 insertions(+), 4465 deletions(-) rewrite dlls/user32/menu.c (66%) 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 dissimilarity index 66% index d0637a61809..74a43eaab1a 100644 --- a/dlls/user32/menu.c +++ b/dlls/user32/menu.c @@ -1,4366 +1,1488 @@ -/* - * Menu functions - * - * Copyright 1993 Martin Ayotte - * Copyright 1994 Alexandre Julliard - * Copyright 1997 Morten Welinder - * Copyright 2005 Maxime Bellengé - * Copyright 2006 Phil Krylov - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* - * Note: the style MF_MOUSESELECT is used to mark popup items that - * have been selected, i.e. their popup menu is currently displayed. - * This is probably not the meaning this style has in MS-Windows. - * - * Note 2: where there is a difference, these menu API's are according - * the behavior of Windows 2k and Windows XP. Known differences with - * Windows 9x/ME are documented in the comments, in case an application - * is found to depend on the old behavior. - * - * TODO: - * implements styles : - * - MNS_AUTODISMISS - * - MNS_DRAGDROP - * - MNS_MODELESS - */ - -#include -#include - -#define OEMRESOURCE - -#include "windef.h" -#include "winbase.h" -#include "wingdi.h" -#include "winnls.h" -#include "wine/server.h" -#include "wine/exception.h" -#include "win.h" -#include "controls.h" -#include "user_private.h" -#include "wine/debug.h" - -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 - - /* Margins for popup menus */ -#define MENU_MARGIN 3 - - /* (other menu->FocusedItem values give the position of the focused item) */ -#define NO_SELECTED_ITEM 0xffff - -#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_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 | \ - MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ ) -#define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU) -#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 - */ -const struct builtin_class_descr MENU_builtin_class = -{ - (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */ - CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */ - WINPROC_MENU, /* proc */ - sizeof(HMENU), /* extra */ - IDC_ARROW, /* cursor */ - (HBRUSH)(COLOR_MENU+1) /* brush */ -}; - - -/*********************************************************************** - * debug_print_menuitem - * - * Print a menuitem in readable form. - */ - -#define debug_print_menuitem(pre, mp, post) \ - do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0) - -#define MENUOUT(text) \ - TRACE("%s%s", (count++ ? "," : ""), (text)) - -#define MENUFLAG(bit,text) \ - do { \ - if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \ - } while (0) - -static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp, - const char *postfix) -{ - static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM", - "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE", - "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE", - "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"}; - TRACE("%s ", prefix); - if (mp) { - UINT flags = mp->fType; - TRACE( "{ ID=0x%Ix", mp->wID); - if ( mp->hSubMenu) - TRACE( ", Sub=%p", mp->hSubMenu); - if (flags) { - int count = 0; - TRACE( ", fType="); - MENUFLAG( MFT_SEPARATOR, "sep"); - MENUFLAG( MFT_OWNERDRAW, "own"); - MENUFLAG( MFT_BITMAP, "bit"); - MENUFLAG(MF_POPUP, "pop"); - MENUFLAG(MFT_MENUBARBREAK, "barbrk"); - MENUFLAG(MFT_MENUBREAK, "brk"); - MENUFLAG(MFT_RADIOCHECK, "radio"); - MENUFLAG(MFT_RIGHTORDER, "rorder"); - MENUFLAG(MF_SYSMENU, "sys"); - MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */ - if (flags) - TRACE( "+0x%x", flags); - } - flags = mp->fState; - if (flags) { - int count = 0; - TRACE( ", State="); - MENUFLAG(MFS_GRAYED, "grey"); - MENUFLAG(MFS_DEFAULT, "default"); - MENUFLAG(MFS_DISABLED, "dis"); - MENUFLAG(MFS_CHECKED, "check"); - MENUFLAG(MFS_HILITE, "hi"); - MENUFLAG(MF_USECHECKBITMAPS, "usebit"); - MENUFLAG(MF_MOUSESELECT, "mouse"); - if (flags) - TRACE( "+0x%x", flags); - } - if (mp->hCheckBit) - TRACE( ", Chk=%p", mp->hCheckBit); - if (mp->hUnCheckBit) - TRACE( ", Unc=%p", mp->hUnCheckBit); - if (mp->text) - TRACE( ", Text=%s", debugstr_w(mp->text)); - if (mp->dwItemData) - TRACE( ", ItemData=0x%08Ix", mp->dwItemData); - if (mp->hbmpItem) - { - if( IS_MAGIC_BITMAP(mp->hbmpItem)) - TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]); - else - TRACE( ", hbitmap=%p", mp->hbmpItem); - } - TRACE( " }"); - } else - TRACE( "NULL"); - TRACE(" %s\n", postfix); -} - -#undef MENUOUT -#undef MENUFLAG - - -/*********************************************************************** - * MENU_GetMenu - * - * Validate the given menu handle and returns the menu structure pointer. - */ -static POPUPMENU *MENU_GetMenu(HMENU hMenu) -{ - POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU ); - - if (menu == OBJ_OTHER_PROCESS) - { - WARN( "other process menu %p?\n", hMenu); - return NULL; - } - if (menu) release_user_handle_ptr( menu ); /* FIXME! */ - else WARN("invalid menu handle=%p\n", hMenu); - return menu; -} - -static POPUPMENU *grab_menu_ptr(HMENU hMenu) -{ - POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU ); - - if (menu == OBJ_OTHER_PROCESS) - { - WARN("other process menu %p?\n", hMenu); - return NULL; - } - - if (menu) - menu->refcount++; - else - WARN("invalid menu handle=%p\n", hMenu); - return menu; -} - -static void release_menu_ptr(POPUPMENU *menu) -{ - if (menu) - { - menu->refcount--; - release_user_handle_ptr(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 - * - * Return the default system menu. - */ -static HMENU MENU_CopySysPopup(BOOL mdi) -{ - HMENU hMenu = LoadMenuW(user32_module, mdi ? L"SYSMENUMDI" : L"SYSMENU"); - - if( hMenu ) { - MENUINFO minfo; - MENUITEMINFOW miteminfo; - POPUPMENU* menu = MENU_GetMenu(hMenu); - menu->wFlags |= MF_SYSMENU | MF_POPUP; - /* decorate the menu with bitmaps */ - minfo.cbSize = sizeof( MENUINFO); - minfo.dwStyle = MNS_CHECKORBMP; - minfo.fMask = MIM_STYLE; - SetMenuInfo( hMenu, &minfo); - miteminfo.cbSize = sizeof( MENUITEMINFOW); - miteminfo.fMask = MIIM_BITMAP; - miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE; - SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo); - miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE; - SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo); - miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE; - SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo); - miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE; - SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo); - NtUserSetMenuDefaultItem( hMenu, SC_CLOSE, FALSE ); - } - else - ERR("Unable to load default system menu\n" ); - - TRACE("returning %p (mdi=%d).\n", hMenu, mdi ); - - return hMenu; -} - - -/********************************************************************** - * MENU_GetSysMenu - * - * Create a copy of the system menu. System menu in Windows is - * a special menu bar with the single entry - system menu popup. - * This popup is presented to the outside world as a "system menu". - * 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) -{ - UINT uItem = NO_SELECTED_ITEM; - HMENU hTrackMenu; - UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON; - - TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar); - - /* 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 */ - - 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 */ - } - if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL; - - if (!IsMenu( hTrackMenu )) return; - - MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags ); - - /* fetch the window menu again, it may have changed */ - hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd ); - - if( wChar && wChar != ' ' ) - { - uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) ); - if ( uItem >= (UINT)(-2) ) - { - if( uItem == (UINT)(-1) ) MessageBeep(0); - /* schedule end of menu tracking */ - wFlags |= TF_ENDMENU; - goto track_menu; - } - } - - MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 ); - - if (!(wParam & HTSYSMENU) || wChar == ' ') - { - if( uItem == NO_SELECTED_ITEM ) - MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT ); - else - PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 ); - } - -track_menu: - MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL ); - MENU_ExitTracking( hwnd, FALSE ); -} - -/********************************************************************** - * TrackPopupMenuEx (USER32.@) - */ -BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y, - HWND hWnd, LPTPMPARAMS lpTpm ) -{ - 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; - } - - if (MENU_InitPopup( hWnd, hMenu, wFlags )) - { - MENU_InitTracking(hWnd, hMenu, TRUE, wFlags); - - /* 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)); - - 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); - - if (menu->hWnd) - { - NtUserDestroyWindow( menu->hWnd ); - menu->hWnd = 0; - - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu, - MAKELPARAM(0, IS_SYSTEM_MENU(menu)) ); - } - SetLastError(0); - } - - return ret; -} - -/********************************************************************** - * TrackPopupMenu (USER32.@) - * - * Like the win32 API, the function return the command ID only if the - * flag TPM_RETURNCMD is on. - * - */ -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); -} - -/*********************************************************************** - * PopupMenuWndProc - * - * NOTE: Windows has totally different (and undocumented) popup wndproc. - */ -LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM 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: - case WM_PRINTCLIENT: - case WM_ERASEBKGND: - case WM_SHOWWINDOW: - case MN_GETHMENU: - return NtUserMessageCall( hwnd, message, wParam, lParam, - NULL, NtUserPopupMenuWndProc, FALSE ); - - default: - return DefWindowProcW( hwnd, message, wParam, lParam ); - } - return 0; -} - - -/******************************************************************* - * ChangeMenuA (USER32.@) - */ -BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data, - UINT id, UINT flags ) -{ - TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags ); - if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND, - id, data ); - if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE ); - if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE, - id, data ); - if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu, - flags & MF_BYPOSITION ? pos : id, - flags & ~MF_REMOVE ); - /* Default: MF_INSERT */ - return InsertMenuA( hMenu, pos, flags, id, data ); -} - - -/******************************************************************* - * ChangeMenuW (USER32.@) - */ -BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data, - UINT id, UINT flags ) -{ - TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags ); - if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND, - id, data ); - if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE ); - if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE, - id, data ); - if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu, - flags & MF_BYPOSITION ? pos : id, - flags & ~MF_REMOVE ); - /* Default: MF_INSERT */ - return InsertMenuW( hMenu, pos, flags, id, data ); -} - - -/******************************************************************* - * GetMenuStringA (USER32.@) - */ -INT WINAPI GetMenuStringA( - HMENU hMenu, /* [in] menuhandle */ - UINT wItemID, /* [in] menu item (dep. on wFlags) */ - LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/ - INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/ - UINT wFlags /* [in] MF_ flags */ -) -{ - POPUPMENU *menu; - MENUITEM *item; - UINT pos; - INT ret; - - TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags ); - if (str && nMaxSiz) str[0] = '\0'; - - if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos))) - { - SetLastError( ERROR_MENU_ITEM_NOT_FOUND); - return 0; - } - item = &menu->items[pos]; - - if (!item->text) - ret = 0; - else if (!str || !nMaxSiz) - ret = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL ); - else - { - if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL )) - str[nMaxSiz-1] = 0; - ret = strlen(str); - } - release_menu_ptr(menu); - - TRACE("returning %s\n", debugstr_a(str)); - return ret; -} - - -/******************************************************************* - * GetMenuStringW (USER32.@) - */ -INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID, - LPWSTR str, INT nMaxSiz, UINT wFlags ) -{ - POPUPMENU *menu; - MENUITEM *item; - UINT pos; - INT ret; - - TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags ); - if (str && nMaxSiz) str[0] = '\0'; - - if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos))) - { - SetLastError( ERROR_MENU_ITEM_NOT_FOUND); - return 0; - } - item = &menu->items[pos]; - - if (!str || !nMaxSiz) - ret = item->text ? lstrlenW(item->text) : 0; - else if (!item->text) - { - str[0] = 0; - ret = 0; - } - else - { - lstrcpynW( str, item->text, nMaxSiz ); - ret = lstrlenW(str); - } - release_menu_ptr(menu); - - TRACE("returning %s\n", debugstr_w(str)); - return ret; -} - - -/********************************************************************** - * 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.@) - */ -UINT WINAPI GetMenuState( HMENU menu, UINT item, UINT flags ) -{ - return NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuState, NULL, NULL ); -} - - -/********************************************************************** - * GetMenuItemCount (USER32.@) - */ -INT WINAPI GetMenuItemCount( HMENU menu ) -{ - return NtUserGetMenuItemCount( menu ); -} - - -/********************************************************************** - * GetMenuItemID (USER32.@) - */ -UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos ) -{ - POPUPMENU *menu; - UINT id, pos; - - if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos))) - return -1; - - id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID; - release_menu_ptr(menu); - return id; -} - - -/********************************************************************** - * MENU_mnu2mnuii - * - * Uses flags, id and text ptr, passed by InsertMenu() and - * ModifyMenu() to setup a MenuItemInfo structure. - */ -static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, - LPMENUITEMINFOW pmii) -{ - ZeroMemory( pmii, sizeof( MENUITEMINFOW)); - pmii->cbSize = sizeof( MENUITEMINFOW); - pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE; - /* setting bitmap clears text and vice versa */ - if( IS_STRING_ITEM(flags)) { - pmii->fMask |= MIIM_STRING | MIIM_BITMAP; - if( !str) - flags |= MF_SEPARATOR; - /* Item beginning with a backspace is a help item */ - /* FIXME: wrong place, this is only true in win16 */ - else if( *str == '\b') { - flags |= MF_HELP; - str++; - } - pmii->dwTypeData = (LPWSTR)str; - } else if( flags & MFT_BITMAP){ - pmii->fMask |= MIIM_BITMAP | MIIM_STRING; - pmii->hbmpItem = (HBITMAP)str; - } - if( flags & MF_OWNERDRAW){ - pmii->fMask |= MIIM_DATA; - pmii->dwItemData = (ULONG_PTR) str; - } - if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) { - pmii->fMask |= MIIM_SUBMENU; - pmii->hSubMenu = (HMENU)id; - } - if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED; - pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT; - pmii->fType = flags & MENUITEMINFO_TYPE_MASK; - pmii->wID = (UINT)id; -} - - -/******************************************************************* - * InsertMenuW (USER32.@) - */ -BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags, - UINT_PTR id, LPCWSTR str ) -{ - MENUITEMINFOW mii; - - if (IS_STRING_ITEM(flags) && str) - TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %s\n", - hMenu, pos, flags, id, debugstr_w(str) ); - else TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %p (not a string)\n", - hMenu, pos, flags, id, str ); - - MENU_mnu2mnuii( flags, id, str, &mii); - mii.fMask |= MIIM_CHECKMARKS; - return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserInsertMenuItem, &mii, NULL ); -} - - -/******************************************************************* - * InsertMenuA (USER32.@) - */ -BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags, - UINT_PTR id, LPCSTR str ) -{ - BOOL ret = FALSE; - - if (IS_STRING_ITEM(flags) && str) - { - INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); - if (newstr) - { - MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len ); - ret = InsertMenuW( hMenu, pos, flags, id, newstr ); - HeapFree( GetProcessHeap(), 0, newstr ); - } - return ret; - } - else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str ); -} - - -/******************************************************************* - * AppendMenuA (USER32.@) - */ -BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags, - UINT_PTR id, LPCSTR data ) -{ - return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data ); -} - - -/******************************************************************* - * AppendMenuW (USER32.@) - */ -BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags, - UINT_PTR id, LPCWSTR data ) -{ - return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data ); -} - - -/******************************************************************* - * ModifyMenuW (USER32.@) - */ -BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags, - UINT_PTR id, LPCWSTR str ) -{ - MENUITEMINFOW mii; - - if (IS_STRING_ITEM(flags)) - TRACE("%p %d %04x %04Ix %s\n", hMenu, pos, flags, id, debugstr_w(str) ); - else - TRACE("%p %d %04x %04Ix %p\n", hMenu, pos, flags, id, str ); - - MENU_mnu2mnuii( flags, id, str, &mii); - return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserSetMenuItemInfo, &mii, NULL ); -} - - -/******************************************************************* - * ModifyMenuA (USER32.@) - */ -BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags, - UINT_PTR id, LPCSTR str ) -{ - BOOL ret = FALSE; - - if (IS_STRING_ITEM(flags) && str) - { - INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); - if (newstr) - { - MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len ); - ret = ModifyMenuW( hMenu, pos, flags, id, newstr ); - HeapFree( GetProcessHeap(), 0, newstr ); - } - return ret; - } - else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str ); -} - - -/********************************************************************** - * CreatePopupMenu (USER32.@) - */ -HMENU WINAPI CreatePopupMenu(void) -{ - return NtUserCreateMenu( TRUE ); -} - - -/********************************************************************** - * GetMenuCheckMarkDimensions (USER.417) - * GetMenuCheckMarkDimensions (USER32.@) - */ -DWORD WINAPI GetMenuCheckMarkDimensions(void) -{ - return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) ); -} - - -/********************************************************************** - * SetMenuItemBitmaps (USER32.@) - */ -BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags, - HBITMAP hNewUnCheck, HBITMAP hNewCheck) -{ - POPUPMENU *menu; - MENUITEM *item; - UINT pos; - - if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos))) - return FALSE; - - item = &menu->items[pos]; - if (!hNewCheck && !hNewUnCheck) - { - item->fState &= ~MF_USECHECKBITMAPS; - } - else /* Install new bitmaps */ - { - item->hCheckBit = hNewCheck; - item->hUnCheckBit = hNewUnCheck; - item->fState |= MF_USECHECKBITMAPS; - } - release_menu_ptr(menu); - - return TRUE; -} - - -/********************************************************************** - * CreateMenu (USER32.@) - */ -HMENU WINAPI CreateMenu(void) -{ - return NtUserCreateMenu( FALSE ); -} - - -/********************************************************************** - * GetMenu (USER32.@) - */ -HMENU WINAPI GetMenu( HWND hWnd ) -{ - HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID ); - TRACE("for %p returning %p\n", hWnd, retvalue); - 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.@) - */ -HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos ) -{ - POPUPMENU *menu; - HMENU submenu; - UINT pos; - - if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos))) - return 0; - - if (menu->items[pos].fType & MF_POPUP) - submenu = menu->items[pos].hSubMenu; - else - submenu = 0; - - release_menu_ptr(menu); - return submenu; -} - - -/********************************************************************** - * DrawMenuBar (USER32.@) - */ -BOOL WINAPI DrawMenuBar( HWND hwnd ) -{ - return NtUserDrawMenuBar( 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.@) - */ -HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name ) -{ - HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU ); - if (!hrsrc) return 0; - return LoadMenuIndirectA( LoadResource( instance, hrsrc )); -} - - -/***************************************************************** - * LoadMenuW (USER32.@) - */ -HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name ) -{ - HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU ); - if (!hrsrc) return 0; - return LoadMenuIndirectW( LoadResource( instance, hrsrc )); -} - - -/********************************************************************** - * LoadMenuIndirectW (USER32.@) - */ -HMENU WINAPI LoadMenuIndirectW( LPCVOID template ) -{ - HMENU hMenu; - WORD version, offset; - LPCSTR p = template; - - version = GET_WORD(p); - p += sizeof(WORD); - TRACE("%p, ver %d\n", template, version ); - switch (version) - { - case 0: /* standard format is version of 0 */ - offset = GET_WORD(p); - p += sizeof(WORD) + offset; - if (!(hMenu = CreateMenu())) return 0; - if (!MENU_ParseResource( p, hMenu )) - { - NtUserDestroyMenu( hMenu ); - return 0; - } - return hMenu; - case 1: /* extended format is version of 1 */ - offset = GET_WORD(p); - p += sizeof(WORD) + offset; - if (!(hMenu = CreateMenu())) return 0; - if (!MENUEX_ParseResource( p, hMenu)) - { - NtUserDestroyMenu( hMenu ); - return 0; - } - return hMenu; - default: - ERR("version %d not supported.\n", version); - return 0; - } -} - - -/********************************************************************** - * LoadMenuIndirectA (USER32.@) - */ -HMENU WINAPI LoadMenuIndirectA( LPCVOID template ) -{ - return LoadMenuIndirectW( template ); -} - - -/********************************************************************** - * IsMenu (USER32.@) - */ -BOOL WINAPI IsMenu( HMENU menu ) -{ - MENUINFO info; - - info.cbSize = sizeof(info); - info.fMask = 0; - if (GetMenuInfo( menu, &info )) return TRUE; - - SetLastError(ERROR_INVALID_MENU_HANDLE); - return FALSE; -} - -/********************************************************************** - * GetMenuItemInfo_common - */ - -static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT id, BOOL bypos, - LPMENUITEMINFOW lpmii, BOOL unicode) -{ - POPUPMENU *menu; - MENUITEM *item; - UINT pos; - - menu = find_menu_item(hmenu, id, bypos ? MF_BYPOSITION : 0, &pos); - - item = menu ? &menu->items[pos] : NULL; - - debug_print_menuitem("GetMenuItemInfo_common: ", item, ""); - - if (!menu) - { - SetLastError( ERROR_MENU_ITEM_NOT_FOUND); - return FALSE; - } - - if( lpmii->fMask & MIIM_TYPE) { - if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) { - release_menu_ptr(menu); - WARN("invalid combination of fMask bits used\n"); - /* this does not happen on Win9x/ME */ - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK; - if (item->hbmpItem && !IS_MAGIC_BITMAP(item->hbmpItem)) - lpmii->fType |= MFT_BITMAP; - lpmii->hbmpItem = item->hbmpItem; /* not on Win9x/ME */ - if( lpmii->fType & MFT_BITMAP) { - lpmii->dwTypeData = (LPWSTR) item->hbmpItem; - lpmii->cch = 0; - } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) { - /* this does not happen on Win9x/ME */ - lpmii->dwTypeData = 0; - lpmii->cch = 0; - } - } - - /* copy the text string */ - if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) { - if (!item->text) { - if(lpmii->dwTypeData && lpmii->cch) { - if( unicode) - *((WCHAR *)lpmii->dwTypeData) = 0; - else - *((CHAR *)lpmii->dwTypeData) = 0; - } - lpmii->cch = 0; - } else { - int len; - if (unicode) - { - len = lstrlenW(item->text); - if(lpmii->dwTypeData && lpmii->cch) - lstrcpynW(lpmii->dwTypeData, item->text, lpmii->cch); - } - else - { - len = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, - 0, NULL, NULL ) - 1; - if(lpmii->dwTypeData && lpmii->cch) - if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, - (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL )) - ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0; - } - /* if we've copied a substring we return its length */ - if(lpmii->dwTypeData && lpmii->cch) - if (lpmii->cch <= len + 1) - lpmii->cch--; - else - lpmii->cch = len; - else { - /* return length of string */ - /* not on Win9x/ME if fType & MFT_BITMAP */ - lpmii->cch = len; - } - } - } - - if (lpmii->fMask & MIIM_FTYPE) - lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK; - - if (lpmii->fMask & MIIM_BITMAP) - lpmii->hbmpItem = item->hbmpItem; - - if (lpmii->fMask & MIIM_STATE) - lpmii->fState = item->fState & MENUITEMINFO_STATE_MASK; - - if (lpmii->fMask & MIIM_ID) - lpmii->wID = item->wID; - - if (lpmii->fMask & MIIM_SUBMENU) - lpmii->hSubMenu = item->hSubMenu; - else { - /* hSubMenu is always cleared - * (not on Win9x/ME ) */ - lpmii->hSubMenu = 0; - } - - if (lpmii->fMask & MIIM_CHECKMARKS) { - lpmii->hbmpChecked = item->hCheckBit; - lpmii->hbmpUnchecked = item->hUnCheckBit; - } - if (lpmii->fMask & MIIM_DATA) - lpmii->dwItemData = item->dwItemData; - - release_menu_ptr(menu); - return TRUE; -} - -/********************************************************************** - * GetMenuItemInfoA (USER32.@) - */ -BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos, - LPMENUITEMINFOA lpmii) -{ - BOOL ret; - MENUITEMINFOA mii; - if( lpmii->cbSize != sizeof( mii) && - lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - memcpy( &mii, lpmii, lpmii->cbSize); - mii.cbSize = sizeof( mii); - ret = GetMenuItemInfo_common (hmenu, item, bypos, - (LPMENUITEMINFOW)&mii, FALSE); - mii.cbSize = lpmii->cbSize; - memcpy( lpmii, &mii, mii.cbSize); - return ret; -} - -/********************************************************************** - * GetMenuItemInfoW (USER32.@) - */ -BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos, - LPMENUITEMINFOW lpmii) -{ - BOOL ret; - MENUITEMINFOW mii; - if( lpmii->cbSize != sizeof( mii) && - lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - memcpy( &mii, lpmii, lpmii->cbSize); - mii.cbSize = sizeof( mii); - ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE); - mii.cbSize = lpmii->cbSize; - memcpy( lpmii, &mii, mii.cbSize); - return ret; -} - - -/********************************************************************** - * MENU_NormalizeMenuItemInfoStruct - * - * Helper for SetMenuItemInfo and InsertMenuItemInfo: - * check, copy and extend the MENUITEMINFO struct from the version that the application - * supplied to the version used by wine source. */ -static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in, - MENUITEMINFOW *pmii_out ) -{ - /* do we recognize the size? */ - if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) && - pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) { - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - /* copy the fields that we have */ - memcpy( pmii_out, pmii_in, pmii_in->cbSize); - /* if the hbmpItem member is missing then extend */ - if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) { - pmii_out->cbSize = sizeof( MENUITEMINFOW); - pmii_out->hbmpItem = NULL; - } - /* test for invalid bit combinations */ - if( (pmii_out->fMask & MIIM_TYPE && - pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) || - (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) { - WARN("invalid combination of fMask bits used\n"); - /* this does not happen on Win9x/ME */ - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - /* convert old style (MIIM_TYPE) to the new */ - if( pmii_out->fMask & MIIM_TYPE){ - pmii_out->fMask |= MIIM_FTYPE; - if( IS_STRING_ITEM(pmii_out->fType)){ - pmii_out->fMask |= MIIM_STRING; - } else if( (pmii_out->fType) & MFT_BITMAP){ - pmii_out->fMask |= MIIM_BITMAP; - pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData)); - } - } - return TRUE; -} - -/********************************************************************** - * SetMenuItemInfoA (USER32.@) - */ -BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos, - const MENUITEMINFOA *lpmii) -{ - WCHAR *strW = NULL; - MENUITEMINFOW mii; - BOOL ret; - - TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); - - if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; - - if ((mii.fMask & MIIM_STRING) && mii.dwTypeData) - { - const char *str = (const char *)mii.dwTypeData; - UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; - MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len ); - mii.dwTypeData = strW; - } - - ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0, - NtUserSetMenuItemInfo, &mii, NULL ); - - HeapFree( GetProcessHeap(), 0, strW ); - return ret; -} - -/********************************************************************** - * SetMenuItemInfoW (USER32.@) - */ -BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos, - const MENUITEMINFOW *lpmii) -{ - MENUITEMINFOW mii; - - TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); - - if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE; - - return NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0, - NtUserSetMenuItemInfo, &mii, NULL ); -} - -/********************************************************************** - * GetMenuDefaultItem (USER32.@) - */ -UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags) -{ - POPUPMENU *menu; - MENUITEM * item; - UINT i = 0; - - TRACE("(%p,%d,%d)\n", hmenu, bypos, flags); - - if (!(menu = MENU_GetMenu(hmenu))) return -1; - - /* find default item */ - item = menu->items; - - /* empty menu */ - if (! item) return -1; - - while ( !( item->fState & MFS_DEFAULT ) ) - { - i++; item++; - if (i >= menu->nItems ) return -1; - } - - /* default: don't return disabled items */ - if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1; - - /* search rekursiv when needed */ - if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) ) - { - UINT ret; - ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags ); - if ( -1 != ret ) return ret; - - /* when item not found in submenu, return the popup item */ - } - return ( bypos ) ? i : item->wID; - -} - - -/********************************************************************** - * InsertMenuItemA (USER32.@) - */ -BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos, - const MENUITEMINFOA *lpmii) -{ - WCHAR *strW = NULL; - MENUITEMINFOW mii; - BOOL ret; - - TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii); - - if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; - - if ((mii.fMask & MIIM_STRING) && mii.dwTypeData) - { - const char *str = (const char *)mii.dwTypeData; - UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); - if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; - MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len ); - mii.dwTypeData = strW; - } - - ret = NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0, - NtUserInsertMenuItem, &mii, NULL ); - - HeapFree( GetProcessHeap(), 0, strW ); - return ret; -} - - -/********************************************************************** - * InsertMenuItemW (USER32.@) - */ -BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos, - const MENUITEMINFOW *lpmii) -{ - MENUITEMINFOW mii; - - TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii); - - if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE; - - return NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0, - NtUserInsertMenuItem, &mii, NULL ); -} - -/********************************************************************** - * CheckMenuRadioItem (USER32.@) - */ - -BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last, - UINT check, UINT flags) -{ - POPUPMENU *first_menu = NULL, *check_menu; - UINT i, check_pos; - BOOL done = FALSE; - - for (i = first; i <= last; i++) - { - MENUITEM *item; - - if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos))) - continue; - - if (!first_menu) - first_menu = grab_menu_ptr(check_menu->obj.handle); - - if (first_menu != check_menu) - { - release_menu_ptr(check_menu); - continue; - } - - item = &check_menu->items[check_pos]; - if (item->fType != MFT_SEPARATOR) - { - if (i == check) - { - item->fType |= MFT_RADIOCHECK; - item->fState |= MFS_CHECKED; - done = TRUE; - } - else - { - /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */ - item->fState &= ~MFS_CHECKED; - } - } - - release_menu_ptr(check_menu); - } - release_menu_ptr(first_menu); - - return done; -} - - -/********************************************************************** - * SetMenuInfo (USER32.@) - */ -BOOL WINAPI SetMenuInfo( HMENU menu, const MENUINFO *info ) -{ - TRACE( "(%p %p)\n", menu, info ); - - if (!info || info->cbSize != sizeof(*info)) - { - SetLastError( ERROR_INVALID_PARAMETER); - return FALSE; - } - - return NtUserThunkedMenuInfo( menu, info ); -} - -/********************************************************************** - * GetMenuInfo (USER32.@) - */ -BOOL WINAPI GetMenuInfo( HMENU menu, MENUINFO *info ) -{ - return NtUserGetMenuInfo( menu, info ); -} - - -/********************************************************************** - * GetMenuContextHelpId (USER32.@) - */ -DWORD WINAPI GetMenuContextHelpId( HMENU menu ) -{ - MENUINFO info; - TRACE( "(%p)\n", menu ); - info.cbSize = sizeof(info); - info.fMask = MIM_HELPID; - return GetMenuInfo( menu, &info ) ? info.dwContextHelpID : 0; -} - -/********************************************************************** - * MenuItemFromPoint (USER32.@) - */ -INT WINAPI MenuItemFromPoint( HWND hwnd, HMENU menu, POINT pt ) -{ - return NtUserMenuItemFromPoint( hwnd, menu, pt.x, pt.y ); -} - - -/********************************************************************** - * CalcMenuBar (USER32.@) - */ -DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect) -{ - FIXME("(%p, %ld, %ld, %ld, %p): stub\n", hwnd, left, right, top, rect); - return 0; -} - - -/********************************************************************** - * TranslateAcceleratorA (USER32.@) - * TranslateAccelerator (USER32.@) - */ -INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg ) -{ - switch (msg->message) - { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - return NtUserTranslateAccelerator( hWnd, hAccel, msg ); - - case WM_CHAR: - case WM_SYSCHAR: - { - MSG msgW = *msg; - char ch = LOWORD(msg->wParam); - WCHAR wch; - MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1); - msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam)); - return NtUserTranslateAccelerator( hWnd, hAccel, &msgW ); - } - - default: - return 0; - } -} +/* + * Menu functions + * + * Copyright 1993 Martin Ayotte + * Copyright 1994 Alexandre Julliard + * Copyright 1997 Morten Welinder + * Copyright 2005 Maxime Bellengé + * Copyright 2006 Phil Krylov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * Note: the style MF_MOUSESELECT is used to mark popup items that + * have been selected, i.e. their popup menu is currently displayed. + * This is probably not the meaning this style has in MS-Windows. + * + * Note 2: where there is a difference, these menu API's are according + * the behavior of Windows 2k and Windows XP. Known differences with + * Windows 9x/ME are documented in the comments, in case an application + * is found to depend on the old behavior. + * + * TODO: + * implements styles : + * - MNS_AUTODISMISS + * - MNS_DRAGDROP + * - MNS_MODELESS + */ + +#include +#include + +#define OEMRESOURCE + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winnls.h" +#include "wine/server.h" +#include "wine/exception.h" +#include "win.h" +#include "controls.h" +#include "user_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(menu); + + + /* Space between 2 columns */ +#define MENU_COL_SPACE 4 + + /* Margins for popup menus */ +#define MENU_MARGIN 3 + + /* (other menu->FocusedItem values give the position of the focused item) */ +#define NO_SELECTED_ITEM 0xffff + +#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 MENUITEMINFO_TYPE_MASK \ + (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \ + MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \ + MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ ) +#define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU) +#define STATE_MASK (~TYPE_MASK) +#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT)) + +/********************************************************************* + * menu class descriptor + */ +const struct builtin_class_descr MENU_builtin_class = +{ + (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */ + CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */ + WINPROC_MENU, /* proc */ + sizeof(HMENU), /* extra */ + IDC_ARROW, /* cursor */ + (HBRUSH)(COLOR_MENU+1) /* brush */ +}; + + +/*********************************************************************** + * debug_print_menuitem + * + * Print a menuitem in readable form. + */ + +#define debug_print_menuitem(pre, mp, post) \ + do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0) + +#define MENUOUT(text) \ + TRACE("%s%s", (count++ ? "," : ""), (text)) + +#define MENUFLAG(bit,text) \ + do { \ + if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \ + } while (0) + +static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp, + const char *postfix) +{ + static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM", + "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE", + "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE", + "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"}; + TRACE("%s ", prefix); + if (mp) { + UINT flags = mp->fType; + TRACE( "{ ID=0x%Ix", mp->wID); + if ( mp->hSubMenu) + TRACE( ", Sub=%p", mp->hSubMenu); + if (flags) { + int count = 0; + TRACE( ", fType="); + MENUFLAG( MFT_SEPARATOR, "sep"); + MENUFLAG( MFT_OWNERDRAW, "own"); + MENUFLAG( MFT_BITMAP, "bit"); + MENUFLAG(MF_POPUP, "pop"); + MENUFLAG(MFT_MENUBARBREAK, "barbrk"); + MENUFLAG(MFT_MENUBREAK, "brk"); + MENUFLAG(MFT_RADIOCHECK, "radio"); + MENUFLAG(MFT_RIGHTORDER, "rorder"); + MENUFLAG(MF_SYSMENU, "sys"); + MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */ + if (flags) + TRACE( "+0x%x", flags); + } + flags = mp->fState; + if (flags) { + int count = 0; + TRACE( ", State="); + MENUFLAG(MFS_GRAYED, "grey"); + MENUFLAG(MFS_DEFAULT, "default"); + MENUFLAG(MFS_DISABLED, "dis"); + MENUFLAG(MFS_CHECKED, "check"); + MENUFLAG(MFS_HILITE, "hi"); + MENUFLAG(MF_USECHECKBITMAPS, "usebit"); + MENUFLAG(MF_MOUSESELECT, "mouse"); + if (flags) + TRACE( "+0x%x", flags); + } + if (mp->hCheckBit) + TRACE( ", Chk=%p", mp->hCheckBit); + if (mp->hUnCheckBit) + TRACE( ", Unc=%p", mp->hUnCheckBit); + if (mp->text) + TRACE( ", Text=%s", debugstr_w(mp->text)); + if (mp->dwItemData) + TRACE( ", ItemData=0x%08Ix", mp->dwItemData); + if (mp->hbmpItem) + { + if( IS_MAGIC_BITMAP(mp->hbmpItem)) + TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]); + else + TRACE( ", hbitmap=%p", mp->hbmpItem); + } + TRACE( " }"); + } else + TRACE( "NULL"); + TRACE(" %s\n", postfix); +} + +#undef MENUOUT +#undef MENUFLAG + + +/*********************************************************************** + * MENU_GetMenu + * + * Validate the given menu handle and returns the menu structure pointer. + */ +static POPUPMENU *MENU_GetMenu(HMENU hMenu) +{ + POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU ); + + if (menu == OBJ_OTHER_PROCESS) + { + WARN( "other process menu %p?\n", hMenu); + return NULL; + } + if (menu) release_user_handle_ptr( menu ); /* FIXME! */ + else WARN("invalid menu handle=%p\n", hMenu); + return menu; +} + +static POPUPMENU *grab_menu_ptr(HMENU hMenu) +{ + POPUPMENU *menu = get_user_handle_ptr( hMenu, NTUSER_OBJ_MENU ); + + if (menu == OBJ_OTHER_PROCESS) + { + WARN("other process menu %p?\n", hMenu); + return NULL; + } + + if (menu) + menu->refcount++; + else + WARN("invalid menu handle=%p\n", hMenu); + return menu; +} + +static void release_menu_ptr(POPUPMENU *menu) +{ + if (menu) + { + menu->refcount--; + release_user_handle_ptr(menu); + } +} + +/*********************************************************************** + * MENU_CopySysPopup + * + * Return the default system menu. + */ +static HMENU MENU_CopySysPopup(BOOL mdi) +{ + HMENU hMenu = LoadMenuW(user32_module, mdi ? L"SYSMENUMDI" : L"SYSMENU"); + + if( hMenu ) { + MENUINFO minfo; + MENUITEMINFOW miteminfo; + POPUPMENU* menu = MENU_GetMenu(hMenu); + menu->wFlags |= MF_SYSMENU | MF_POPUP; + /* decorate the menu with bitmaps */ + minfo.cbSize = sizeof( MENUINFO); + minfo.dwStyle = MNS_CHECKORBMP; + minfo.fMask = MIM_STYLE; + SetMenuInfo( hMenu, &minfo); + miteminfo.cbSize = sizeof( MENUITEMINFOW); + miteminfo.fMask = MIIM_BITMAP; + miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE; + SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo); + miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE; + SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo); + miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE; + SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo); + miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE; + SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo); + NtUserSetMenuDefaultItem( hMenu, SC_CLOSE, FALSE ); + } + else + ERR("Unable to load default system menu\n" ); + + TRACE("returning %p (mdi=%d).\n", hMenu, mdi ); + + return hMenu; +} + + +/********************************************************************** + * MENU_GetSysMenu + * + * Create a copy of the system menu. System menu in Windows is + * a special menu bar with the single entry - system menu popup. + * This popup is presented to the outside world as a "system menu". + * 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; +} + + +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_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; +} + + +/********************************************************************** + * TrackPopupMenu (USER32.@) + * + * Like the win32 API, the function return the command ID only if the + * flag TPM_RETURNCMD is on. + * + */ +BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y, + INT nReserved, HWND hWnd, const RECT *lpRect ) +{ + return NtUserTrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL ); +} + +/*********************************************************************** + * PopupMenuWndProc + * + * NOTE: Windows has totally different (and undocumented) popup wndproc. + */ +LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch(message) + { + case WM_DESTROY: + case WM_CREATE: + case WM_MOUSEACTIVATE: + case WM_PAINT: + case WM_PRINTCLIENT: + case WM_ERASEBKGND: + case WM_SHOWWINDOW: + case MN_GETHMENU: + return NtUserMessageCall( hwnd, message, wParam, lParam, + NULL, NtUserPopupMenuWndProc, FALSE ); + + default: + return DefWindowProcW( hwnd, message, wParam, lParam ); + } + return 0; +} + + +/******************************************************************* + * ChangeMenuA (USER32.@) + */ +BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data, + UINT id, UINT flags ) +{ + TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags ); + if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND, + id, data ); + if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE ); + if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE, + id, data ); + if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu, + flags & MF_BYPOSITION ? pos : id, + flags & ~MF_REMOVE ); + /* Default: MF_INSERT */ + return InsertMenuA( hMenu, pos, flags, id, data ); +} + + +/******************************************************************* + * ChangeMenuW (USER32.@) + */ +BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data, + UINT id, UINT flags ) +{ + TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags ); + if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND, + id, data ); + if (flags & MF_DELETE) return NtUserDeleteMenu( hMenu, pos, flags & ~MF_DELETE ); + if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE, + id, data ); + if (flags & MF_REMOVE) return NtUserRemoveMenu( hMenu, + flags & MF_BYPOSITION ? pos : id, + flags & ~MF_REMOVE ); + /* Default: MF_INSERT */ + return InsertMenuW( hMenu, pos, flags, id, data ); +} + + +/******************************************************************* + * GetMenuStringA (USER32.@) + */ +INT WINAPI GetMenuStringA( + HMENU hMenu, /* [in] menuhandle */ + UINT wItemID, /* [in] menu item (dep. on wFlags) */ + LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/ + INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/ + UINT wFlags /* [in] MF_ flags */ +) +{ + POPUPMENU *menu; + MENUITEM *item; + UINT pos; + INT ret; + + TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags ); + if (str && nMaxSiz) str[0] = '\0'; + + if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos))) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); + return 0; + } + item = &menu->items[pos]; + + if (!item->text) + ret = 0; + else if (!str || !nMaxSiz) + ret = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL ); + else + { + if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL )) + str[nMaxSiz-1] = 0; + ret = strlen(str); + } + release_menu_ptr(menu); + + TRACE("returning %s\n", debugstr_a(str)); + return ret; +} + + +/******************************************************************* + * GetMenuStringW (USER32.@) + */ +INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID, + LPWSTR str, INT nMaxSiz, UINT wFlags ) +{ + POPUPMENU *menu; + MENUITEM *item; + UINT pos; + INT ret; + + TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags ); + if (str && nMaxSiz) str[0] = '\0'; + + if (!(menu = find_menu_item(hMenu, wItemID, wFlags, &pos))) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); + return 0; + } + item = &menu->items[pos]; + + if (!str || !nMaxSiz) + ret = item->text ? lstrlenW(item->text) : 0; + else if (!item->text) + { + str[0] = 0; + ret = 0; + } + else + { + lstrcpynW( str, item->text, nMaxSiz ); + ret = lstrlenW(str); + } + release_menu_ptr(menu); + + TRACE("returning %s\n", debugstr_w(str)); + return ret; +} + + +/********************************************************************** + * GetMenuState (USER32.@) + */ +UINT WINAPI GetMenuState( HMENU menu, UINT item, UINT flags ) +{ + return NtUserThunkedMenuItemInfo( menu, item, flags, NtUserGetMenuState, NULL, NULL ); +} + + +/********************************************************************** + * GetMenuItemCount (USER32.@) + */ +INT WINAPI GetMenuItemCount( HMENU menu ) +{ + return NtUserGetMenuItemCount( menu ); +} + + +/********************************************************************** + * GetMenuItemID (USER32.@) + */ +UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos ) +{ + POPUPMENU *menu; + UINT id, pos; + + if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos))) + return -1; + + id = menu->items[pos].fType & MF_POPUP ? -1 : menu->items[pos].wID; + release_menu_ptr(menu); + return id; +} + + +/********************************************************************** + * MENU_mnu2mnuii + * + * Uses flags, id and text ptr, passed by InsertMenu() and + * ModifyMenu() to setup a MenuItemInfo structure. + */ +static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, + LPMENUITEMINFOW pmii) +{ + ZeroMemory( pmii, sizeof( MENUITEMINFOW)); + pmii->cbSize = sizeof( MENUITEMINFOW); + pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE; + /* setting bitmap clears text and vice versa */ + if( IS_STRING_ITEM(flags)) { + pmii->fMask |= MIIM_STRING | MIIM_BITMAP; + if( !str) + flags |= MF_SEPARATOR; + /* Item beginning with a backspace is a help item */ + /* FIXME: wrong place, this is only true in win16 */ + else if( *str == '\b') { + flags |= MF_HELP; + str++; + } + pmii->dwTypeData = (LPWSTR)str; + } else if( flags & MFT_BITMAP){ + pmii->fMask |= MIIM_BITMAP | MIIM_STRING; + pmii->hbmpItem = (HBITMAP)str; + } + if( flags & MF_OWNERDRAW){ + pmii->fMask |= MIIM_DATA; + pmii->dwItemData = (ULONG_PTR) str; + } + if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) { + pmii->fMask |= MIIM_SUBMENU; + pmii->hSubMenu = (HMENU)id; + } + if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED; + pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT; + pmii->fType = flags & MENUITEMINFO_TYPE_MASK; + pmii->wID = (UINT)id; +} + + +/******************************************************************* + * InsertMenuW (USER32.@) + */ +BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags, + UINT_PTR id, LPCWSTR str ) +{ + MENUITEMINFOW mii; + + if (IS_STRING_ITEM(flags) && str) + TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %s\n", + hMenu, pos, flags, id, debugstr_w(str) ); + else TRACE("hMenu %p, pos %d, flags %08x, id %04Ix, str %p (not a string)\n", + hMenu, pos, flags, id, str ); + + MENU_mnu2mnuii( flags, id, str, &mii); + mii.fMask |= MIIM_CHECKMARKS; + return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserInsertMenuItem, &mii, NULL ); +} + + +/******************************************************************* + * InsertMenuA (USER32.@) + */ +BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags, + UINT_PTR id, LPCSTR str ) +{ + BOOL ret = FALSE; + + if (IS_STRING_ITEM(flags) && str) + { + INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + if (newstr) + { + MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len ); + ret = InsertMenuW( hMenu, pos, flags, id, newstr ); + HeapFree( GetProcessHeap(), 0, newstr ); + } + return ret; + } + else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str ); +} + + +/******************************************************************* + * AppendMenuA (USER32.@) + */ +BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags, + UINT_PTR id, LPCSTR data ) +{ + return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data ); +} + + +/******************************************************************* + * AppendMenuW (USER32.@) + */ +BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags, + UINT_PTR id, LPCWSTR data ) +{ + return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data ); +} + + +/******************************************************************* + * ModifyMenuW (USER32.@) + */ +BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags, + UINT_PTR id, LPCWSTR str ) +{ + MENUITEMINFOW mii; + + if (IS_STRING_ITEM(flags)) + TRACE("%p %d %04x %04Ix %s\n", hMenu, pos, flags, id, debugstr_w(str) ); + else + TRACE("%p %d %04x %04Ix %p\n", hMenu, pos, flags, id, str ); + + MENU_mnu2mnuii( flags, id, str, &mii); + return NtUserThunkedMenuItemInfo( hMenu, pos, flags, NtUserSetMenuItemInfo, &mii, NULL ); +} + + +/******************************************************************* + * ModifyMenuA (USER32.@) + */ +BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags, + UINT_PTR id, LPCSTR str ) +{ + BOOL ret = FALSE; + + if (IS_STRING_ITEM(flags) && str) + { + INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + if (newstr) + { + MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len ); + ret = ModifyMenuW( hMenu, pos, flags, id, newstr ); + HeapFree( GetProcessHeap(), 0, newstr ); + } + return ret; + } + else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str ); +} + + +/********************************************************************** + * CreatePopupMenu (USER32.@) + */ +HMENU WINAPI CreatePopupMenu(void) +{ + return NtUserCreateMenu( TRUE ); +} + + +/********************************************************************** + * GetMenuCheckMarkDimensions (USER.417) + * GetMenuCheckMarkDimensions (USER32.@) + */ +DWORD WINAPI GetMenuCheckMarkDimensions(void) +{ + return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) ); +} + + +/********************************************************************** + * SetMenuItemBitmaps (USER32.@) + */ +BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags, + HBITMAP hNewUnCheck, HBITMAP hNewCheck) +{ + POPUPMENU *menu; + MENUITEM *item; + UINT pos; + + if (!(menu = find_menu_item(hMenu, nPos, wFlags, &pos))) + return FALSE; + + item = &menu->items[pos]; + if (!hNewCheck && !hNewUnCheck) + { + item->fState &= ~MF_USECHECKBITMAPS; + } + else /* Install new bitmaps */ + { + item->hCheckBit = hNewCheck; + item->hUnCheckBit = hNewUnCheck; + item->fState |= MF_USECHECKBITMAPS; + } + release_menu_ptr(menu); + + return TRUE; +} + + +/********************************************************************** + * CreateMenu (USER32.@) + */ +HMENU WINAPI CreateMenu(void) +{ + return NtUserCreateMenu( FALSE ); +} + + +/********************************************************************** + * GetMenu (USER32.@) + */ +HMENU WINAPI GetMenu( HWND hWnd ) +{ + HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID ); + TRACE("for %p returning %p\n", hWnd, retvalue); + return retvalue; +} + + +/********************************************************************** + * GetSubMenu (USER32.@) + */ +HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos ) +{ + POPUPMENU *menu; + HMENU submenu; + UINT pos; + + if (!(menu = find_menu_item(hMenu, nPos, MF_BYPOSITION, &pos))) + return 0; + + if (menu->items[pos].fType & MF_POPUP) + submenu = menu->items[pos].hSubMenu; + else + submenu = 0; + + release_menu_ptr(menu); + return submenu; +} + + +/********************************************************************** + * DrawMenuBar (USER32.@) + */ +BOOL WINAPI DrawMenuBar( HWND hwnd ) +{ + return NtUserDrawMenuBar( hwnd ); +} + + +/***************************************************************** + * LoadMenuA (USER32.@) + */ +HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name ) +{ + HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU ); + if (!hrsrc) return 0; + return LoadMenuIndirectA( LoadResource( instance, hrsrc )); +} + + +/***************************************************************** + * LoadMenuW (USER32.@) + */ +HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name ) +{ + HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU ); + if (!hrsrc) return 0; + return LoadMenuIndirectW( LoadResource( instance, hrsrc )); +} + + +/********************************************************************** + * LoadMenuIndirectW (USER32.@) + */ +HMENU WINAPI LoadMenuIndirectW( LPCVOID template ) +{ + HMENU hMenu; + WORD version, offset; + LPCSTR p = template; + + version = GET_WORD(p); + p += sizeof(WORD); + TRACE("%p, ver %d\n", template, version ); + switch (version) + { + case 0: /* standard format is version of 0 */ + offset = GET_WORD(p); + p += sizeof(WORD) + offset; + if (!(hMenu = CreateMenu())) return 0; + if (!MENU_ParseResource( p, hMenu )) + { + NtUserDestroyMenu( hMenu ); + return 0; + } + return hMenu; + case 1: /* extended format is version of 1 */ + offset = GET_WORD(p); + p += sizeof(WORD) + offset; + if (!(hMenu = CreateMenu())) return 0; + if (!MENUEX_ParseResource( p, hMenu)) + { + NtUserDestroyMenu( hMenu ); + return 0; + } + return hMenu; + default: + ERR("version %d not supported.\n", version); + return 0; + } +} + + +/********************************************************************** + * LoadMenuIndirectA (USER32.@) + */ +HMENU WINAPI LoadMenuIndirectA( LPCVOID template ) +{ + return LoadMenuIndirectW( template ); +} + + +/********************************************************************** + * IsMenu (USER32.@) + */ +BOOL WINAPI IsMenu( HMENU menu ) +{ + MENUINFO info; + + info.cbSize = sizeof(info); + info.fMask = 0; + if (GetMenuInfo( menu, &info )) return TRUE; + + SetLastError(ERROR_INVALID_MENU_HANDLE); + return FALSE; +} + +/********************************************************************** + * GetMenuItemInfo_common + */ + +static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT id, BOOL bypos, + LPMENUITEMINFOW lpmii, BOOL unicode) +{ + POPUPMENU *menu; + MENUITEM *item; + UINT pos; + + menu = find_menu_item(hmenu, id, bypos ? MF_BYPOSITION : 0, &pos); + + item = menu ? &menu->items[pos] : NULL; + + debug_print_menuitem("GetMenuItemInfo_common: ", item, ""); + + if (!menu) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); + return FALSE; + } + + if( lpmii->fMask & MIIM_TYPE) { + if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) { + release_menu_ptr(menu); + WARN("invalid combination of fMask bits used\n"); + /* this does not happen on Win9x/ME */ + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK; + if (item->hbmpItem && !IS_MAGIC_BITMAP(item->hbmpItem)) + lpmii->fType |= MFT_BITMAP; + lpmii->hbmpItem = item->hbmpItem; /* not on Win9x/ME */ + if( lpmii->fType & MFT_BITMAP) { + lpmii->dwTypeData = (LPWSTR) item->hbmpItem; + lpmii->cch = 0; + } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) { + /* this does not happen on Win9x/ME */ + lpmii->dwTypeData = 0; + lpmii->cch = 0; + } + } + + /* copy the text string */ + if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) { + if (!item->text) { + if(lpmii->dwTypeData && lpmii->cch) { + if( unicode) + *((WCHAR *)lpmii->dwTypeData) = 0; + else + *((CHAR *)lpmii->dwTypeData) = 0; + } + lpmii->cch = 0; + } else { + int len; + if (unicode) + { + len = lstrlenW(item->text); + if(lpmii->dwTypeData && lpmii->cch) + lstrcpynW(lpmii->dwTypeData, item->text, lpmii->cch); + } + else + { + len = WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, + 0, NULL, NULL ) - 1; + if(lpmii->dwTypeData && lpmii->cch) + if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, + (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL )) + ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0; + } + /* if we've copied a substring we return its length */ + if(lpmii->dwTypeData && lpmii->cch) + if (lpmii->cch <= len + 1) + lpmii->cch--; + else + lpmii->cch = len; + else { + /* return length of string */ + /* not on Win9x/ME if fType & MFT_BITMAP */ + lpmii->cch = len; + } + } + } + + if (lpmii->fMask & MIIM_FTYPE) + lpmii->fType = item->fType & MENUITEMINFO_TYPE_MASK; + + if (lpmii->fMask & MIIM_BITMAP) + lpmii->hbmpItem = item->hbmpItem; + + if (lpmii->fMask & MIIM_STATE) + lpmii->fState = item->fState & MENUITEMINFO_STATE_MASK; + + if (lpmii->fMask & MIIM_ID) + lpmii->wID = item->wID; + + if (lpmii->fMask & MIIM_SUBMENU) + lpmii->hSubMenu = item->hSubMenu; + else { + /* hSubMenu is always cleared + * (not on Win9x/ME ) */ + lpmii->hSubMenu = 0; + } + + if (lpmii->fMask & MIIM_CHECKMARKS) { + lpmii->hbmpChecked = item->hCheckBit; + lpmii->hbmpUnchecked = item->hUnCheckBit; + } + if (lpmii->fMask & MIIM_DATA) + lpmii->dwItemData = item->dwItemData; + + release_menu_ptr(menu); + return TRUE; +} + +/********************************************************************** + * GetMenuItemInfoA (USER32.@) + */ +BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos, + LPMENUITEMINFOA lpmii) +{ + BOOL ret; + MENUITEMINFOA mii; + if( lpmii->cbSize != sizeof( mii) && + lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + memcpy( &mii, lpmii, lpmii->cbSize); + mii.cbSize = sizeof( mii); + ret = GetMenuItemInfo_common (hmenu, item, bypos, + (LPMENUITEMINFOW)&mii, FALSE); + mii.cbSize = lpmii->cbSize; + memcpy( lpmii, &mii, mii.cbSize); + return ret; +} + +/********************************************************************** + * GetMenuItemInfoW (USER32.@) + */ +BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos, + LPMENUITEMINFOW lpmii) +{ + BOOL ret; + MENUITEMINFOW mii; + if( lpmii->cbSize != sizeof( mii) && + lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + memcpy( &mii, lpmii, lpmii->cbSize); + mii.cbSize = sizeof( mii); + ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE); + mii.cbSize = lpmii->cbSize; + memcpy( lpmii, &mii, mii.cbSize); + return ret; +} + + +/********************************************************************** + * MENU_NormalizeMenuItemInfoStruct + * + * Helper for SetMenuItemInfo and InsertMenuItemInfo: + * check, copy and extend the MENUITEMINFO struct from the version that the application + * supplied to the version used by wine source. */ +static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in, + MENUITEMINFOW *pmii_out ) +{ + /* do we recognize the size? */ + if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) && + pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + /* copy the fields that we have */ + memcpy( pmii_out, pmii_in, pmii_in->cbSize); + /* if the hbmpItem member is missing then extend */ + if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) { + pmii_out->cbSize = sizeof( MENUITEMINFOW); + pmii_out->hbmpItem = NULL; + } + /* test for invalid bit combinations */ + if( (pmii_out->fMask & MIIM_TYPE && + pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) || + (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) { + WARN("invalid combination of fMask bits used\n"); + /* this does not happen on Win9x/ME */ + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + /* convert old style (MIIM_TYPE) to the new */ + if( pmii_out->fMask & MIIM_TYPE){ + pmii_out->fMask |= MIIM_FTYPE; + if( IS_STRING_ITEM(pmii_out->fType)){ + pmii_out->fMask |= MIIM_STRING; + } else if( (pmii_out->fType) & MFT_BITMAP){ + pmii_out->fMask |= MIIM_BITMAP; + pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData)); + } + } + return TRUE; +} + +/********************************************************************** + * SetMenuItemInfoA (USER32.@) + */ +BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos, + const MENUITEMINFOA *lpmii) +{ + WCHAR *strW = NULL; + MENUITEMINFOW mii; + BOOL ret; + + TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; + + if ((mii.fMask & MIIM_STRING) && mii.dwTypeData) + { + const char *str = (const char *)mii.dwTypeData; + UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; + MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len ); + mii.dwTypeData = strW; + } + + ret = NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0, + NtUserSetMenuItemInfo, &mii, NULL ); + + HeapFree( GetProcessHeap(), 0, strW ); + return ret; +} + +/********************************************************************** + * SetMenuItemInfoW (USER32.@) + */ +BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos, + const MENUITEMINFOW *lpmii) +{ + MENUITEMINFOW mii; + + TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); + + if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE; + + return NtUserThunkedMenuItemInfo( hmenu, item, bypos ? MF_BYPOSITION : 0, + NtUserSetMenuItemInfo, &mii, NULL ); +} + +/********************************************************************** + * GetMenuDefaultItem (USER32.@) + */ +UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags) +{ + POPUPMENU *menu; + MENUITEM * item; + UINT i = 0; + + TRACE("(%p,%d,%d)\n", hmenu, bypos, flags); + + if (!(menu = MENU_GetMenu(hmenu))) return -1; + + /* find default item */ + item = menu->items; + + /* empty menu */ + if (! item) return -1; + + while ( !( item->fState & MFS_DEFAULT ) ) + { + i++; item++; + if (i >= menu->nItems ) return -1; + } + + /* default: don't return disabled items */ + if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1; + + /* search rekursiv when needed */ + if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) ) + { + UINT ret; + ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags ); + if ( -1 != ret ) return ret; + + /* when item not found in submenu, return the popup item */ + } + return ( bypos ) ? i : item->wID; + +} + + +/********************************************************************** + * InsertMenuItemA (USER32.@) + */ +BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos, + const MENUITEMINFOA *lpmii) +{ + WCHAR *strW = NULL; + MENUITEMINFOW mii; + BOOL ret; + + TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; + + if ((mii.fMask & MIIM_STRING) && mii.dwTypeData) + { + const char *str = (const char *)mii.dwTypeData; + UINT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + if (!(strW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE; + MultiByteToWideChar( CP_ACP, 0, str, -1, strW, len ); + mii.dwTypeData = strW; + } + + ret = NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0, + NtUserInsertMenuItem, &mii, NULL ); + + HeapFree( GetProcessHeap(), 0, strW ); + return ret; +} + + +/********************************************************************** + * InsertMenuItemW (USER32.@) + */ +BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos, + const MENUITEMINFOW *lpmii) +{ + MENUITEMINFOW mii; + + TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii); + + if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE; + + return NtUserThunkedMenuItemInfo( hMenu, uItem, bypos ? MF_BYPOSITION : 0, + NtUserInsertMenuItem, &mii, NULL ); +} + +/********************************************************************** + * CheckMenuRadioItem (USER32.@) + */ + +BOOL WINAPI CheckMenuRadioItem(HMENU hMenu, UINT first, UINT last, + UINT check, UINT flags) +{ + POPUPMENU *first_menu = NULL, *check_menu; + UINT i, check_pos; + BOOL done = FALSE; + + for (i = first; i <= last; i++) + { + MENUITEM *item; + + if (!(check_menu = find_menu_item(hMenu, i, flags, &check_pos))) + continue; + + if (!first_menu) + first_menu = grab_menu_ptr(check_menu->obj.handle); + + if (first_menu != check_menu) + { + release_menu_ptr(check_menu); + continue; + } + + item = &check_menu->items[check_pos]; + if (item->fType != MFT_SEPARATOR) + { + if (i == check) + { + item->fType |= MFT_RADIOCHECK; + item->fState |= MFS_CHECKED; + done = TRUE; + } + else + { + /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */ + item->fState &= ~MFS_CHECKED; + } + } + + release_menu_ptr(check_menu); + } + release_menu_ptr(first_menu); + + return done; +} + + +/********************************************************************** + * SetMenuInfo (USER32.@) + */ +BOOL WINAPI SetMenuInfo( HMENU menu, const MENUINFO *info ) +{ + TRACE( "(%p %p)\n", menu, info ); + + if (!info || info->cbSize != sizeof(*info)) + { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + + return NtUserThunkedMenuInfo( menu, info ); +} + +/********************************************************************** + * GetMenuInfo (USER32.@) + */ +BOOL WINAPI GetMenuInfo( HMENU menu, MENUINFO *info ) +{ + return NtUserGetMenuInfo( menu, info ); +} + + +/********************************************************************** + * GetMenuContextHelpId (USER32.@) + */ +DWORD WINAPI GetMenuContextHelpId( HMENU menu ) +{ + MENUINFO info; + TRACE( "(%p)\n", menu ); + info.cbSize = sizeof(info); + info.fMask = MIM_HELPID; + return GetMenuInfo( menu, &info ) ? info.dwContextHelpID : 0; +} + +/********************************************************************** + * MenuItemFromPoint (USER32.@) + */ +INT WINAPI MenuItemFromPoint( HWND hwnd, HMENU menu, POINT pt ) +{ + return NtUserMenuItemFromPoint( hwnd, menu, pt.x, pt.y ); +} + + +/********************************************************************** + * CalcMenuBar (USER32.@) + */ +DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect) +{ + FIXME("(%p, %ld, %ld, %ld, %p): stub\n", hwnd, left, right, top, rect); + return 0; +} + + +/********************************************************************** + * TranslateAcceleratorA (USER32.@) + * TranslateAccelerator (USER32.@) + */ +INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg ) +{ + switch (msg->message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + return NtUserTranslateAccelerator( hWnd, hAccel, msg ); + + case WM_CHAR: + case WM_SYSCHAR: + { + MSG msgW = *msg; + char ch = LOWORD(msg->wParam); + WCHAR wch; + MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1); + msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam)); + return NtUserTranslateAccelerator( hWnd, hAccel, &msgW ); + } + + default: + return 0; + } +} 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, }; -- 2.11.4.GIT