From d134ca1ea83f5c5945e080c108d098e1876d1dbb Mon Sep 17 00:00:00 2001 From: Dmitry Timoshkov Date: Mon, 9 Aug 2004 23:38:40 +0000 Subject: [PATCH] Fix button behaviour on WM_SETFOCUS/WM_KILLFOCUS with a test case. --- controls/button.c | 61 +++++++++++--------- dlls/user/tests/msg.c | 126 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 27 deletions(-) diff --git a/controls/button.c b/controls/button.c index d3b1b860df7..9d600326008 100644 --- a/controls/button.c +++ b/controls/button.c @@ -152,9 +152,9 @@ inline static void paint_button( HWND hwnd, LONG style, UINT action ) /* retrieve the button text; returned buffer must be freed by caller */ inline static WCHAR *get_button_text( HWND hwnd ) { - INT len = GetWindowTextLengthW( hwnd ); + INT len = 512; WCHAR *buffer = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ); - if (buffer) GetWindowTextW( hwnd, buffer, len + 1 ); + if (buffer) InternalGetWindowText( hwnd, buffer, len + 1 ); return buffer; } @@ -209,9 +209,12 @@ static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, { HDC hdc = (HDC)wParam; RECT rc; - HBRUSH hBrush = (HBRUSH)SendMessageW(GetParent(hWnd), WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); + HBRUSH hBrush; + HWND parent = GetParent(hWnd); + if (!parent) parent = hWnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ - hBrush = (HBRUSH)DefWindowProcW(GetParent(hWnd), WM_CTLCOLORBTN, + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hdc, (LPARAM)hWnd); GetClientRect(hWnd, &rc); FillRect(hdc, &rc, hBrush); @@ -319,11 +322,13 @@ static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, HDC hdc = GetDC(hWnd); HBRUSH hbrush; RECT client, rc; + HWND parent = GetParent(hWnd); - hbrush = (HBRUSH)SendMessageW(GetParent(hWnd), WM_CTLCOLORSTATIC, + if (!parent) parent = hWnd; + hbrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hWnd); if (!hbrush) /* did the app forget to call DefWindowProc ? */ - hbrush = (HBRUSH)DefWindowProcW(GetParent(hWnd), WM_CTLCOLORSTATIC, + hbrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hWnd); GetClientRect(hWnd, &client); @@ -353,16 +358,6 @@ static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, return (LRESULT)get_button_font( hWnd ); case WM_SETFOCUS: - if ((btn_type == BS_RADIOBUTTON || btn_type == BS_AUTORADIOBUTTON) && (GetCapture() != hWnd) && - !(SendMessageW(hWnd, BM_GETCHECK, 0, 0) & BST_CHECKED)) - { - /* The notification is sent when the button (BS_AUTORADIOBUTTON) - is unchecked and the focus was not given by a mouse click. */ - if (btn_type == BS_AUTORADIOBUTTON) - SendMessageW( hWnd, BM_SETCHECK, BUTTON_CHECKED, 0 ); - SendMessageW( GetParent(hWnd), WM_COMMAND, - MAKEWPARAM( GetWindowLongA(hWnd,GWL_ID), BN_CLICKED ), (LPARAM)hWnd); - } set_button_state( hWnd, get_button_state(hWnd) | BUTTON_HASFOCUS ); paint_button( hWnd, btn_type, ODA_FOCUS ); break; @@ -370,7 +365,6 @@ static LRESULT WINAPI ButtonWndProc_common(HWND hWnd, UINT uMsg, case WM_KILLFOCUS: set_button_state( hWnd, get_button_state(hWnd) & ~BUTTON_HASFOCUS ); paint_button( hWnd, btn_type, ODA_FOCUS ); - InvalidateRect( hWnd, NULL, TRUE ); break; case WM_SYSCOLORCHANGE: @@ -725,12 +719,15 @@ static void PB_Paint( HWND hwnd, HDC hDC, UINT action ) LONG state = get_button_state( hwnd ); LONG style = GetWindowLongA( hwnd, GWL_STYLE ); BOOL pushedState = (state & BUTTON_HIGHLIGHTED); + HWND parent; GetClientRect( hwnd, &rc ); /* Send WM_CTLCOLOR to allow changing the font (the colors are fixed) */ if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); - SendMessageW( GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); + parent = GetParent(hwnd); + if (!parent) parent = hwnd; + SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); hOldPen = (HPEN)SelectObject(hDC, SYSCOLOR_GetPen(COLOR_WINDOWFRAME)); hOldBrush =(HBRUSH)SelectObject(hDC,GetSysColorBrush(COLOR_BTNFACE)); oldBkMode = SetBkMode(hDC, TRANSPARENT); @@ -808,6 +805,7 @@ static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) HFONT hFont; LONG state = get_button_state( hwnd ); LONG style = GetWindowLongA( hwnd, GWL_STYLE ); + HWND parent; if (style & BS_PUSHLIKE) { @@ -820,10 +818,12 @@ static void CB_Paint( HWND hwnd, HDC hDC, UINT action ) if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); - hBrush = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORSTATIC, + parent = GetParent(hwnd); + if (!parent) parent = hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ - hBrush = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORSTATIC, + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd ); if (style & BS_LEFTTEXT) @@ -953,14 +953,15 @@ static void GB_Paint( HWND hwnd, HDC hDC, UINT action ) UINT dtFlags; TEXTMETRICW tm; LONG style = GetWindowLongA( hwnd, GWL_STYLE ); - - if (action != ODA_DRAWENTIRE) return; + HWND parent; if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); /* GroupBox acts like static control, so it sends CTLCOLORSTATIC */ - hbr = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); + parent = GetParent(hwnd); + if (!parent) parent = hwnd; + hbr = (HBRUSH)SendMessageW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); if (!hbr) /* did the app forget to call defwindowproc ? */ - hbr = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORSTATIC, + hbr = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORSTATIC, (WPARAM)hDC, (LPARAM)hwnd); GetClientRect( hwnd, &rc); @@ -1000,6 +1001,7 @@ static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) HBRUSH hBrush; HFONT hFont; LONG state = get_button_state( hwnd ); + HWND parent; if (action == ODA_SELECT) return; @@ -1007,9 +1009,11 @@ static void UB_Paint( HWND hwnd, HDC hDC, UINT action ) if ((hFont = get_button_font( hwnd ))) SelectObject( hDC, hFont ); - hBrush = (HBRUSH)SendMessageW(GetParent(hwnd), WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); + parent = GetParent(hwnd); + if (!parent) parent = hwnd; + hBrush = (HBRUSH)SendMessageW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); if (!hBrush) /* did the app forget to call defwindowproc ? */ - hBrush = (HBRUSH)DefWindowProcW(GetParent(hwnd), WM_CTLCOLORBTN, + hBrush = (HBRUSH)DefWindowProcW(parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd); FillRect( hDC, &rc, hBrush ); @@ -1030,6 +1034,7 @@ static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) HRGN clipRegion; RECT clipRect; UINT id = GetWindowLongA( hwnd, GWL_ID ); + HWND parent; dis.CtlType = ODT_BUTTON; dis.CtlID = id; @@ -1053,7 +1058,9 @@ static void OB_Paint( HWND hwnd, HDC hDC, UINT action ) DPtoLP(hDC, (LPPOINT) &clipRect, 2); IntersectClipRect(hDC, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); - SetBkColor( hDC, GetSysColor( COLOR_BTNFACE ) ); + parent = GetParent(hwnd); + if (!parent) parent = hwnd; + SendMessageW( parent, WM_CTLCOLORBTN, (WPARAM)hDC, (LPARAM)hwnd ); SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis ); SelectClipRgn(hDC, clipRegion); } diff --git a/dlls/user/tests/msg.c b/dlls/user/tests/msg.c index 0f273045cae..a5d28192da2 100644 --- a/dlls/user/tests/msg.c +++ b/dlls/user/tests/msg.c @@ -473,6 +473,7 @@ static const struct message WmEndCustomDialogSeq[] = { /* Creation and destruction of a modal dialog (32) */ static const struct message WmModalDialogSeq[] = { { WM_CANCELMODE, sent|parent }, + { HCBT_SETFOCUS, hook }, { WM_KILLFOCUS, sent|parent }, { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 }, { WM_ENABLE, sent|parent|wparam, 0 }, @@ -1607,6 +1608,126 @@ static void test_messages(void) flush_sequence(); } +/****************** button message test *************************/ +static const struct message WmSetFocusButtonSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { WM_SETFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { 0 } +}; +static const struct message WmKillFocusButtonSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORBTN, sent|defwinproc }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { 0 } +}; +static const struct message WmSetFocusStaticSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, + { WM_SETFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORSTATIC, sent|defwinproc }, + { 0 } +}; +static const struct message WmKillFocusStaticSeq[] = +{ + { HCBT_SETFOCUS, hook }, + { WM_KILLFOCUS, sent|wparam, 0 }, + { WM_CTLCOLORSTATIC, sent|defwinproc }, + { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, + { 0 } +}; + +static WNDPROC old_button_proc; + +static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static long defwndproc_counter = 0; + LRESULT ret; + struct message msg; + + trace("%p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam); + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(&msg); + + defwndproc_counter++; + ret = CallWindowProcA(old_button_proc, hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; +} + +static void subclass_button(void) +{ + WNDCLASSA cls; + + if (!GetClassInfoA(0, "button", &cls)) assert(0); + + old_button_proc = cls.lpfnWndProc; + + cls.hInstance = GetModuleHandle(0); + cls.lpfnWndProc = button_hook_proc; + cls.lpszClassName = "my_button_class"; + if (!RegisterClassA(&cls)) assert(0); +} + +static void test_button_messages(void) +{ + static const struct + { + DWORD style; + const struct message *setfocus; + const struct message *killfocus; + } button[] = { + { BS_PUSHBUTTON, WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_DEFPUSHBUTTON, WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_CHECKBOX, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_AUTOCHECKBOX, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_RADIOBUTTON, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_3STATE, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_AUTO3STATE, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_GROUPBOX, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_USERBUTTON, WmSetFocusButtonSeq, WmKillFocusButtonSeq }, + { BS_AUTORADIOBUTTON, WmSetFocusStaticSeq, WmKillFocusStaticSeq }, + { BS_OWNERDRAW, WmSetFocusButtonSeq, WmKillFocusButtonSeq } + }; + int i; + HWND hwnd; + + subclass_button(); + + for (i = 0; i < sizeof(button)/sizeof(button[0]); i++) + { + hwnd = CreateWindowExA(0, "my_button_class", "test", button[i].style | WS_POPUP, + 0, 0, 50, 14, 0, 0, 0, NULL); + ok(hwnd != 0, "Failed to create button window\n"); + + ShowWindow(hwnd, SW_SHOW); + UpdateWindow(hwnd); + SetFocus(0); + flush_sequence(); + + trace("button style %08lx\n", button[i].style); + SetFocus(hwnd); + ok_sequence(button[i].setfocus, "SetFocus(hwnd) on a button"); + + SetFocus(0); + ok_sequence(button[i].killfocus, "SetFocus(0) on a button"); + + DestroyWindow(hwnd); + } +} +/************* end of button message test ********************/ + static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static long defwndproc_counter = 0; @@ -1784,6 +1905,9 @@ static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) trace("CBT: %d, %08x, %08lx\n", nCode, wParam, lParam); + /* Log also SetFocus(0) calls */ + if (!wParam) wParam = lParam; + if (GetClassNameA((HWND)wParam, buf, sizeof(buf))) { if (!strcmp(buf, "TestWindowClass") || @@ -1794,6 +1918,7 @@ static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) !strcmp(buf, "MDI_frame_class") || !strcmp(buf, "MDI_client_class") || !strcmp(buf, "MDI_child_class") || + !strcmp(buf, "my_button_class") || !strcmp(buf, "#32770")) { struct message msg; @@ -1817,6 +1942,7 @@ START_TEST(msg) test_messages(); test_mdi_messages(); + test_button_messages(); UnhookWindowsHookEx(hCBT_hook); }