Fix button behaviour on WM_SETFOCUS/WM_KILLFOCUS with a test case

Dmitry Timoshkov dmitry at baikal.ru
Thu Aug 5 03:53:02 CDT 2004


Hello,

trying to merge an old fix for the button code I decided to write
a test case for it.

Changelog:
    Dmitry Timoshkov <dmitry at codeweavers.com>
    Fix button behaviour on WM_SETFOCUS/WM_KILLFOCUS with a test case.

diff -u cvs/hq/wine/controls/button.c wine/controls/button.c
--- cvs/hq/wine/controls/button.c	2004-04-01 19:05:33.000000000 +0900
+++ wine/controls/button.c	2004-08-05 16:58:58.000000000 +0900
@@ -152,9 +152,9 @@ inline static void paint_button( HWND hw
 /* 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_comm
         {
             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_comm
         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_comm
         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_comm
     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
     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
     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
 
     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 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
     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
 
     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
     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
     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 -u cvs/hq/wine/dlls/user/tests/msg.c wine/dlls/user/tests/msg.c
--- cvs/hq/wine/dlls/user/tests/msg.c	2004-07-10 14:03:08.000000000 +0900
+++ wine/dlls/user/tests/msg.c	2004-08-05 16:58:17.000000000 +0900
@@ -473,6 +473,7 @@ static const struct message WmEndCustomD
 /* 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(in
 
     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(in
 	    !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);
 }






More information about the wine-patches mailing list