From: Andrew Eikum Subject: [PATCH] user32: Implement GetMenuBarInfo Message-Id: <4CB336F0.3000408@codeweavers.com> Date: Mon, 11 Oct 2010 11:10:24 -0500 Likely fixes bug 18776. This patch is based heavily on a patch from Rein Klazes which was rejected last summer. According to comments on the bug, Rein's patch appears abandoned. The style has been cleaned up, as well as some minor improvements to the tests to pass on more platforms. --- dlls/user32/menu.c | 71 +++++++++++++++++++++- dlls/user32/tests/menu.c | 152 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 219 insertions(+), 4 deletions(-) diff --git a/dlls/user32/menu.c b/dlls/user32/menu.c index 4d2edf8..3c7a79c 100644 --- a/dlls/user32/menu.c +++ b/dlls/user32/menu.c @@ -4168,8 +4168,75 @@ HMENU WINAPI GetMenu( HWND hWnd ) */ BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi ) { - FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi ); - return FALSE; + LPPOPUPMENU lpmenu; + HMENU hmenu = NULL; + + TRACE("(%p, %d, %d, %p)\n", hwnd, idObject, idItem, pmbi); + + if (!pmbi || pmbi->cbSize != sizeof(MENUBARINFO) || idItem < 0) + return FALSE; + + if (idObject == OBJID_MENU) + hmenu = GetMenu(hwnd); + else if (idObject == OBJID_SYSMENU) + hmenu = GetSystemMenu(hwnd, FALSE); + else if (idObject == OBJID_CLIENT) + FIXME("OBJID_CLIENT not handled\n"); + + if (!hmenu) + return FALSE; + + lpmenu = MENU_GetMenu(hmenu); + if (!lpmenu || idItem > lpmenu->nItems) + return FALSE; + + pmbi->hMenu = hmenu; + + if (lpmenu->Height == 0) + SetRectEmpty(&pmbi->rcBar); + else if (idItem == 0) + { + GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar); + pmbi->rcBar.right = pmbi->rcBar.left + lpmenu->Width; + pmbi->rcBar.bottom = pmbi->rcBar.top + lpmenu->Height; + } + else + GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar); + + pmbi->hwndMenu = 0; + if (top_popup_hmenu == hmenu && top_popup == hwnd) + { + pmbi->fBarFocused = TRUE; + if (idItem) + { + if (lpmenu->FocusedItem == idItem - 1) + { + pmbi->fFocused = TRUE; + /* fill in the hwndMenu member if the selected item + * has an open sub menu */ + if (lpmenu->items[idItem - 1].fType & MF_POPUP) + { + HMENU hsubmenu; + POPUPMENU *lpsubmenu; + hsubmenu = lpmenu->items[idItem - 1].hSubMenu; + lpsubmenu = MENU_GetMenu(hsubmenu); + if (lpsubmenu) + pmbi->hwndMenu = lpsubmenu->hWnd; + } + } + else + pmbi->fFocused = FALSE; + } + else + pmbi->fFocused = TRUE; + } + else + { + pmbi->fBarFocused = FALSE; + pmbi->fFocused = FALSE; + } + + return TRUE; } /********************************************************************** diff --git a/dlls/user32/tests/menu.c b/dlls/user32/tests/menu.c index deecbe8..0ead303 100644 --- a/dlls/user32/tests/menu.c +++ b/dlls/user32/tests/menu.c @@ -38,6 +38,7 @@ static ATOM atomMenuCheckClass; static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO); +static BOOL (WINAPI *pGetMenuBarInfo)(HWND,LONG,LONG,PMENUBARINFO); static UINT (WINAPI *pSendInput)(UINT, INPUT*, size_t); static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO); static BOOL (WINAPI *pEndMenu) (void); @@ -52,6 +53,7 @@ static void init_function_pointers(void) trace("GetProcAddress(%s) failed\n", #func); GET_PROC(GetMenuInfo) + GET_PROC(GetMenuBarInfo) GET_PROC(SendInput) GET_PROC(SetMenuInfo) GET_PROC(EndMenu) @@ -116,6 +118,7 @@ typedef struct /* globals to communicate between test and wndproc */ static BOOL bMenuVisible; +static HMENU popmenu; static BOOL got_input; static HMENU hMenus[4]; @@ -263,6 +266,102 @@ static void register_menu_check_class(void) atomMenuCheckClass = RegisterClass(&wc); } +static void test_getmenubarinfo(void) +{ + BOOL ret; + HMENU hmenu; + MENUBARINFO mbi; + RECT rcw, rci; + HWND hwnd; + + if (!pGetMenuBarInfo) { + win_skip("GetMenuBarInfo is not available\n"); + return; + } + + mbi.cbSize = sizeof(MENUBARINFO); + + hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL, + WS_SYSMENU | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, + NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError()); + + /* no menu: getmenubarinfo should fail */ + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi); + ok(ret == FALSE, "GetMenuBarInfo should not have been successful\n"); + + /* create menubar, no items yet */ + hmenu = CreateMenu(); + ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError()); + + ret = SetMenu(hwnd, hmenu); + ok(ret, "SetMenu failed with error %d\n", GetLastError()); + + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, -1, &mbi); + ok(ret == FALSE, "GetMenuBarInfo should have failed\n"); + + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, NULL); + ok(ret == FALSE, "GetMenuBarInfo should have failed\n"); + + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi); + ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError()); + + ok(mbi.rcBar.left == 0 && mbi.rcBar.top == 0 && + mbi.rcBar.bottom == 0 && mbi.rcBar.right == 0, + "rcBar: Expected 0,0-0,0, got: %d,%d-%d,%d\n", + mbi.rcBar.left, mbi.rcBar.top, mbi.rcBar.right, mbi.rcBar.bottom); + ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n", + mbi.hMenu, hmenu); + ok(mbi.fBarFocused == 0, "fBarFocused: Got %d instead of 0.\n", mbi.fBarFocused); + ok(mbi.fFocused == 0, "fFocused: Got %d instead of 0.\n", mbi.fFocused); + + /* add some items */ + ret = AppendMenu(hmenu, MF_STRING , 100, "item 1"); + ok(ret, "AppendMenu failed.\n"); + ret = AppendMenu(hmenu, MF_STRING , 101, "item 2"); + ok(ret, "AppendMenu failed.\n"); + ret = SetMenu(hwnd, hmenu); + ok(ret, "SetMenu failed with error %d\n", GetLastError()); + + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, -1, &mbi); + ok(ret == FALSE, "GetMenuBarInfo should have failed\n"); + + /* get info for the whole menu */ + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi); + ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError()); + + /* calculate menu rectangle, from window rectangle and the position of the first item */ + ret = GetWindowRect(hwnd, &rcw); + ok(ret, "GetWindowRect failed.\n"); + ret = GetMenuItemRect(hwnd, hmenu, 0, &rci); + ok(ret, "GetMenuItemRect failed.\n"); +todo_wine + ok(mbi.rcBar.left == rci.left && mbi.rcBar.top == rci.top && + mbi.rcBar.bottom == rci.bottom && mbi.rcBar.right == rcw.right - rci.left + rcw.left, + "rcBar: Got %d,%d-%d,%d instead of %d,%d-%d,%d\n", + mbi.rcBar.left, mbi.rcBar.top, mbi.rcBar.right, mbi.rcBar.bottom, + rci.left, rci.top, rcw.right - rci.left + rcw.left, rci.bottom); + ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n", mbi.hMenu, hmenu); + ok(mbi.fBarFocused == 0, "fBarFocused: got %d instead of 0\n", mbi.fBarFocused); + ok(mbi.fFocused == 0, "fFocused: got %d instead of 0\n", mbi.fFocused); + + /* get info for item nr.2 */ + ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 2, &mbi); + ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError()); + ret = GetMenuItemRect(hwnd, hmenu, 1, &rci); + ok(ret, "GetMenuItemRect failed.\n"); + ok(mbi.rcBar.left == rci.left && mbi.rcBar.top == rci.top && + mbi.rcBar.bottom == rci.bottom && mbi.rcBar.right == rci.right, + "rcBar: Got %d,%d-%d,%d instead of %d,%d-%d,%d\n", + mbi.rcBar.left, mbi.rcBar.top, mbi.rcBar.right, mbi.rcBar.bottom, + rci.left, rci.top, rci.right, rci.bottom); + ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n", mbi.hMenu, hmenu); + ok(mbi.fBarFocused == 0, "fBarFocused: got %d instead of 0\n", mbi.fBarFocused); + ok(mbi.fFocused == 0, "fFocused: got %d instead of 0\n", mbi.fFocused); + + DestroyWindow(hwnd); +} + /* demonstrates that windows locks the menu object so that it is still valid * even after a client calls DestroyMenu on it */ static void test_menu_locked_by_window(void) @@ -1957,14 +2056,20 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter) { int ret = TRUE, elapsed = 0; + trace("running test %u\n", i); + got_input = i && menu_tests[i-1].bMenuVisible; if (menu_tests[i].type == INPUT_KEYBOARD) - for (j = 0; menu_tests[i].wVk[j] != 0; j++) + for (j = 0; menu_tests[i].wVk[j] != 0; j++){ send_key(menu_tests[i].wVk[j]); + Sleep(20); + } else - for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++) + for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++){ if (!(ret = click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]))) break; + Sleep(20); + } if (!ret) { @@ -1995,6 +2100,42 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter) } else ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i); + + if(pGetMenuBarInfo) + { + MENUBARINFO mbi; + HMENU hmenu; + UINT state; + BOOL br; + + mbi.cbSize = sizeof(MENUBARINFO); + + /* get info for the menu */ + br = pGetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi); + ok(br, "test %u: GetMenuBarInfo failed\n", i); + hmenu = GetMenu(hWnd); + ok(!mbi.hwndMenu, "test %u: GetMenuBarInfo.hwndMenu wrong: %p expected NULL\n", + i, mbi.hwndMenu); + ok(mbi.hMenu == hmenu, "test %u: GetMenuBarInfo got wrong menu: %p expected %p\n", + i, mbi.hMenu, hmenu); + ok(!bMenuVisible == !mbi.fBarFocused, "test %u: GetMenuBarInfo.fBarFocused (%d) is wrong\n", + i, mbi.fBarFocused != 0); + ok(!bMenuVisible == !mbi.fFocused, "test %u: GetMenuBarInfo.fFocused (%d) is wrong\n", + i, mbi.fFocused != 0); + /* get info for the menu's first item */ + br = pGetMenuBarInfo(hWnd, OBJID_MENU, 1, &mbi); + ok(br, "test %u: GetMenuBarInfo failed\n", i); + state = GetMenuState(hmenu, 0, MF_BYPOSITION); + ok(!mbi.hwndMenu == !popmenu || broken(mbi.hwndMenu == NULL) /* NT4 */, + "test %u: GetMenuBarInfo.hwndMenu wrong: %p expected %sNULL\n", + i, mbi.hwndMenu, popmenu ? "not " : ""); + ok(mbi.hMenu == hmenu, "test %u: GetMenuBarInfo got wrong menu: %p expected %p\n", + i, mbi.hMenu, hmenu); + ok(!bMenuVisible == !mbi.fBarFocused, "test %u: GetMenuBarInfo.fBarFocused (%d) is wrong\n", + i, mbi.fBarFocused != 0); + ok(!(bMenuVisible && (state & MF_HILITE)) == !mbi.fFocused, + "test %u: GetMenuBarInfo.fFocused (%d) is wrong\n", i, mbi.fFocused != 0); + } } return 0; } @@ -2003,6 +2144,12 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { + case WM_INITMENUPOPUP: + popmenu = (HMENU)wParam; + break; + case WM_UNINITMENUPOPUP: + popmenu = NULL; + break; case WM_ENTERMENULOOP: bMenuVisible = TRUE; break; @@ -3268,6 +3415,7 @@ START_TEST(menu) test_menu_locked_by_window(); test_subpopup_locked_by_menu(); test_menu_ownerdraw(); + test_getmenubarinfo(); test_menu_bmp_and_string(); test_menu_getmenuinfo(); test_menu_setmenuinfo();