Rein Klazes : user32: Fix for the ending of menu tracking in the default handler of WM_CANCELMODE message with test .

Alexandre Julliard julliard at winehq.org
Fri Apr 3 10:28:41 CDT 2009


Module: wine
Branch: master
Commit: e861d8be43c180c438b54a684f160cd32258dd92
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=e861d8be43c180c438b54a684f160cd32258dd92

Author: Rein Klazes <wijn at online.nl>
Date:   Thu Apr  2 11:37:11 2009 +0200

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 974cf5b..f51a3d9 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 38d8def..e3ec37c 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 ff6af76..ab473ea 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 f375123..169c223 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();
 }




More information about the wine-cvs mailing list