From e861d8be43c180c438b54a684f160cd32258dd92 Mon Sep 17 00:00:00 2001 From: Rein Klazes Date: Thu, 2 Apr 2009 11:37:11 +0200 Subject: [PATCH] user32: Fix for the ending of menu tracking in the default handler of WM_CANCELMODE message with test. --- dlls/user32/controls.h | 1 + dlls/user32/defwnd.c | 2 +- dlls/user32/menu.c | 32 +++++++++++++-- dlls/user32/tests/menu.c | 86 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/dlls/user32/controls.h b/dlls/user32/controls.h index 974cf5b890..f51a3d9ded 100644 --- a/dlls/user32/controls.h +++ b/dlls/user32/controls.h @@ -88,6 +88,7 @@ extern void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar ) DECLSPEC extern UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw ) DECLSPEC_HIDDEN; extern UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget ) DECLSPEC_HIDDEN; +extern void MENU_EndMenu(HWND) DECLSPEC_HIDDEN; /* nonclient area */ extern LRESULT NC_HandleNCPaint( HWND hwnd , HRGN clip) DECLSPEC_HIDDEN; diff --git a/dlls/user32/defwnd.c b/dlls/user32/defwnd.c index 38d8def4e0..e3ec37c46d 100644 --- a/dlls/user32/defwnd.c +++ b/dlls/user32/defwnd.c @@ -618,7 +618,7 @@ static LRESULT DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa case WM_CANCELMODE: iMenuSysKey = 0; - if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)) EndMenu(); + MENU_EndMenu( hwnd ); if (GetCapture() == hwnd) ReleaseCapture(); break; diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c index ff6af768b1..ab473eab5b 100644 --- a/dlls/user32/menu.c +++ b/dlls/user32/menu.c @@ -176,6 +176,7 @@ 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; @@ -1884,8 +1885,10 @@ static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags, hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE), (LPVOID)hmenu ); if( !menu->hWnd ) return FALSE; - if (!top_popup) top_popup = menu->hWnd; - + if (!top_popup) { + top_popup = menu->hWnd; + top_popup_hmenu = hmenu; + } /* Display the window */ SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0, @@ -1953,7 +1956,10 @@ static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex, if (lppop->FocusedItem == wIndex) return; if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd ); else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW); - if (!top_popup) top_popup = lppop->hWnd; + if (!top_popup) { + top_popup = lppop->hWnd; + top_popup_hmenu = hmenu; + } SelectObject( hdc, get_menu_font(FALSE)); @@ -2462,6 +2468,20 @@ 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->hwndOwner) EndMenu(); +} + /*********************************************************************** * MENU_PtMenu * @@ -3363,6 +3383,7 @@ static BOOL MENU_ExitTracking(HWND hWnd) SendMessageW( hWnd, WM_EXITMENULOOP, 0, 0 ); ShowCaret(0); top_popup = 0; + top_popup_hmenu = NULL; return TRUE; } @@ -3530,7 +3551,10 @@ static LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, case WM_DESTROY: /* zero out global pointer in case resident popup window was destroyed. */ - if (hwnd == top_popup) top_popup = 0; + if (hwnd == top_popup) { + top_popup = 0; + top_popup_hmenu = NULL; + } break; case WM_SHOWWINDOW: diff --git a/dlls/user32/tests/menu.c b/dlls/user32/tests/menu.c index f375123aac..169c223cc1 100644 --- a/dlls/user32/tests/menu.c +++ b/dlls/user32/tests/menu.c @@ -40,6 +40,7 @@ static ATOM atomMenuCheckClass; static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO); static UINT (WINAPI *pSendInput)(UINT, INPUT*, size_t); static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO); +static BOOL (WINAPI *pEndMenu) (void); static void init_function_pointers(void) { @@ -53,6 +54,7 @@ static void init_function_pointers(void) GET_PROC(GetMenuInfo) GET_PROC(SendInput) GET_PROC(SetMenuInfo) + GET_PROC(EndMenu) #undef GET_PROC } @@ -2847,6 +2849,89 @@ static void test_menu_trackpopupmenu(void) DestroyWindow(hwnd); } +/* test handling of WM_CANCELMODE messages */ +static int g_got_enteridle; +static HWND g_hwndtosend; +static LRESULT WINAPI menu_cancelmode_wnd_proc(HWND hwnd, UINT msg, + WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_ENTERMENULOOP: + g_got_enteridle = 0; + return SendMessage( g_hwndtosend, WM_CANCELMODE, 0, 0); + case WM_ENTERIDLE: + { + if( g_got_enteridle++ == 0) { + /* little hack to get another WM_ENTERIDLE message */ + PostMessage( hwnd, WM_MOUSEMOVE, 0, 0); + return SendMessage( g_hwndtosend, WM_CANCELMODE, 0, 0); + } + pEndMenu(); + return TRUE; + } + } + return DefWindowProc( hwnd, msg, wparam, lparam); +} + +static void test_menu_cancelmode(void) +{ + DWORD ret; + HWND hwnd, hwndchild; + HMENU menu; + if( !pEndMenu) { /* win95 */ + win_skip( "EndMenu is not available\n"); + return; + } + hwnd = CreateWindowEx( 0, MAKEINTATOM(atomMenuCheckClass), NULL, + WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, + NULL, NULL, NULL, NULL); + hwndchild = CreateWindowEx( 0, MAKEINTATOM(atomMenuCheckClass), NULL, + WS_VISIBLE | WS_CHILD, 10, 10, 20, 20, + hwnd, NULL, NULL, NULL); + ok( hwnd != NULL && hwndchild != NULL, + "CreateWindowEx failed with error %d\n", GetLastError()); + g_hwndtosend = hwnd; + SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG_PTR)menu_cancelmode_wnd_proc); + SetWindowLongPtr( hwndchild, GWLP_WNDPROC, (LONG_PTR)menu_cancelmode_wnd_proc); + menu = CreatePopupMenu(); + ok( menu != NULL, "CreatePopupMenu failed with error %d\n", GetLastError()); + ret = AppendMenuA( menu, MF_STRING, 1, "winetest"); + ok( ret, "Functie failed lasterror is %u\n", GetLastError()); + /* seems to be needed only on wine :( */ + {MSG msg; while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);} + /* test the effect of sending a WM_CANCELMODE message in the WM_INITMENULOOP + * handler of the menu owner */ + /* test results is exctracted from variable g_got_enteridle. Possible values: + * 0 : complete conformance. Sending WM_CANCELMODE cancels a menu initializing tracking + * 1 : Sending WM_CANCELMODE cancels a menu that is in tracking state + * 2 : Sending WM_CANCELMODE does not work + */ + /* menu owner is top level window */ + g_hwndtosend = hwnd; + ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwnd, NULL); + todo_wine { + ok( g_got_enteridle == 0, "received %d WM_ENTERIDLE messages, none expected\n", g_got_enteridle); + } + ok( g_got_enteridle < 2, "received %d WM_ENTERIDLE messages, should be less then 2\n", g_got_enteridle); + /* menu owner is child window */ + g_hwndtosend = hwndchild; + ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwndchild, NULL); + todo_wine { + ok(g_got_enteridle == 0, "received %d WM_ENTERIDLE messages, none expected\n", g_got_enteridle); + } + ok(g_got_enteridle < 2, "received %d WM_ENTERIDLE messages, should be less then 2\n", g_got_enteridle); + /* now send the WM_CANCELMODE messages to the WRONG window */ + /* those should fail ( to have any effect) */ + g_hwndtosend = hwnd; + ret = TrackPopupMenu( menu, 0x100, 100,100, 0, hwndchild, NULL); + ok( g_got_enteridle == 2, "received %d WM_ENTERIDLE messages, should be 2\n", g_got_enteridle); + /* cleanup */ + DestroyMenu( menu); + DestroyWindow( hwndchild); + DestroyWindow( hwnd); +} + START_TEST(menu) { init_function_pointers(); @@ -2884,4 +2969,5 @@ START_TEST(menu) test_menu_hilitemenuitem(); test_menu_trackpopupmenu(); + test_menu_cancelmode(); } -- 2.32.0.93.g670b81a890