comctl32: toolbar[4/4]: test and improve TB_SETHOTITEM

Mikołaj Zalewski mikolaj at zalewski.pl
Sat Oct 14 12:59:54 CDT 2006


There is one change in behaviour - in the previous implementation only 
flat toobars could have hot items - like in comctl32 v5. Currently it's 
possible to set a hot item with TB_SETHOTITEM no a non-flat toolbar - 
like in comctl32 v6. I changed it because I'd expect there is there is a 
bigger change an application relies on the second possibility than on 
the first.
-------------- next part --------------
diff --git a/dlls/comctl32/tests/toolbar.c b/dlls/comctl32/tests/toolbar.c
index a007fa8..110e8cb 100644
--- a/dlls/comctl32/tests/toolbar.c
+++ b/dlls/comctl32/tests/toolbar.c
@@ -33,6 +33,8 @@ #include "resources.h"
 #include "wine/test.h"
 
 HWND hMainWnd;
+BOOL g_fBlockHotItemChange;
+BOOL g_fReceivedHotItemChange;
  
 static void MakeButton(TBBUTTON *p, int idCommand, int fsStyle, int nString) {
   p->iBitmap = -2;
@@ -42,8 +44,27 @@ static void MakeButton(TBBUTTON *p, int 
   p->iString = nString;
 }
 
+LRESULT MyWnd_Notify(hWnd, wParam, lParam)
+{
+    NMHDR *hdr = (NMHDR *)lParam;
+    switch (hdr->code)
+    {
+        case TBN_HOTITEMCHANGE:
+            g_fReceivedHotItemChange = TRUE;
+            if (g_fBlockHotItemChange)
+                return 1;
+            break;
+    }
+    return 0;
+}
+
 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
+    switch (msg)
+    {
+        case WM_NOTIFY:
+            return MyWnd_Notify(hWnd, wParam, lParam);
+    }
     return DefWindowProcA(hWnd, msg, wParam, lParam);
 }
 
@@ -110,6 +131,32 @@ static void rebuild_toolbar(HWND *hToolb
     ok(SendMessage(*hToolbar, TB_AUTOSIZE, 0, 0) == 0, "TB_AUTOSIZE failed\n");
 }
 
+void rebuild_toolbar_with_buttons(HWND *hToolbar)
+{
+    TBBUTTON buttons[5];
+    rebuild_toolbar(hToolbar);
+    
+    ZeroMemory(&buttons, sizeof(buttons));
+    buttons[0].idCommand = 1;
+    buttons[0].fsStyle = BTNS_BUTTON;
+    buttons[0].fsState = TBSTATE_ENABLED;
+    buttons[1].idCommand = 3;
+    buttons[1].fsStyle = BTNS_BUTTON;
+    buttons[1].fsState = TBSTATE_ENABLED;
+    buttons[2].idCommand = 5;
+    buttons[2].fsStyle = BTNS_SEP;
+    buttons[2].fsState = TBSTATE_ENABLED;
+    buttons[3].idCommand = 7;
+    buttons[3].fsStyle = BTNS_BUTTON;
+    buttons[3].fsState = TBSTATE_ENABLED;
+    buttons[4].idCommand = 9;
+    buttons[4].fsStyle = BTNS_BUTTON;
+    buttons[4].fsState = 0;  /* disabled */
+    ok(SendMessage(*hToolbar, TB_ADDBUTTONS, 5, (LPARAM)buttons) == 1, "TB_ADDBUTTONS failed\n");
+    ok(SendMessage(*hToolbar, TB_AUTOSIZE, 0, 0) == 0, "TB_AUTOSIZE failed\n");
+}
+
+
 #define CHECK_IMAGELIST(count, dx, dy) { \
     int cx, cy; \
     HIMAGELIST himl = (HIMAGELIST)SendMessageA(hToolbar, TB_GETIMAGELIST, 0, 0); \
@@ -375,6 +422,93 @@ void test_add_string()
     CHECK_STRING_TABLE(14, ret7);
 }
 
+void test_hotitem()
+{
+    HWND hToolbar = NULL;
+    TBBUTTONINFO tbinfo;
+    LRESULT ret;
+
+    g_fBlockHotItemChange = FALSE;
+
+    rebuild_toolbar_with_buttons(&hToolbar);
+    /* set TBSTYLE_FLAT. comctl5 allows hot items only for such toolbars.
+     * comctl6 doesn't have this requirement even when theme == NULL */
+    SetWindowLong(hToolbar, GWL_STYLE, TBSTYLE_FLAT | GetWindowLong(hToolbar, GWL_STYLE));
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == -1, "Hot item: %ld, expected -1\n", ret);
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 1, 0);
+    ok(ret == -1, "TB_SETHOTITEM returned %ld, expected -1\n", ret);
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 1, "Hot item: %ld, expected 1\n", ret);
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 2, 0);
+    ok(ret == 1, "TB_SETHOTITEM returned %ld, expected 1\n", ret);
+
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 0xbeef, 0);
+    ok(ret == 2, "TB_SETHOTITEM returned %ld, expected 2\n", ret);
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 2, "Hot item: %lx, expected 2\n", ret);
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, -0xbeef, 0);
+    ok(ret == 2, "TB_SETHOTITEM returned %ld, expected 2\n", ret);
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == -1, "Hot item: %lx, expected -1\n", ret);
+
+    g_fReceivedHotItemChange = FALSE;
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 3, 0);
+    ok(ret == -1, "TB_SETHOTITEM returned %ld, expected -1\n", ret);
+    ok(g_fReceivedHotItemChange, "TBN_HOTITEMCHANGE not received\n");
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 3, "Hot item: %lx, expected 3\n", ret);
+    g_fBlockHotItemChange = TRUE;
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 2, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 2\n", ret);
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 3, "Hot item: %lx, expected 3\n", ret);
+    g_fBlockHotItemChange = FALSE;
+
+    g_fReceivedHotItemChange = FALSE;
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 0xbeaf, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    ok(g_fReceivedHotItemChange == FALSE, "TBN_HOTITEMCHANGE received for invalid parameter\n");
+
+    g_fReceivedHotItemChange = FALSE;
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 3, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    ok(g_fReceivedHotItemChange == FALSE, "TBN_HOTITEMCHANGE received after a duplication\n");
+
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, -0xbeaf, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    ok(g_fReceivedHotItemChange, "TBN_HOTITEMCHANGE not received\n");
+    SendMessage(hToolbar, TB_SETHOTITEM, 3, 0);
+
+    /* setting disabled buttons as hot failed and generates no notify */
+    g_fReceivedHotItemChange = FALSE;
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 4, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    ok(!g_fReceivedHotItemChange, "TBN_HOTITEMCHANGE received\n");
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 3, "Hot item: %lx, expected 3\n", ret);
+
+    /* but disabling a hot button works */
+    ret = SendMessage(hToolbar, TB_SETHOTITEM, 3, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    g_fReceivedHotItemChange = FALSE;
+    SendMessage(hToolbar, TB_ENABLEBUTTON, 7, FALSE);
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 3, "TB_SETHOTITEM returned %ld, expected 3\n", ret);
+    ok(g_fReceivedHotItemChange == FALSE, "Unexpected TBN_HOTITEMCHANGE\n");
+
+    SendMessage(hToolbar, TB_SETHOTITEM, 1, 0);
+    tbinfo.cbSize = sizeof(TBBUTTONINFO);
+    tbinfo.dwMask = TBIF_STATE;
+    tbinfo.fsState = 0;  /* disabled */
+    g_fReceivedHotItemChange = FALSE;
+    ok(SendMessage(hToolbar, TB_SETBUTTONINFO, 1, (LPARAM)&tbinfo) == TRUE, "TB_SETBUTTONINFO failed\n");
+    ret = SendMessage(hToolbar, TB_GETHOTITEM, 0, 0);
+    ok(ret == 1, "TB_SETHOTITEM returned %ld, expected 1\n", ret);
+    ok(g_fReceivedHotItemChange == FALSE, "Unexpected TBN_HOTITEMCHANGE\n");
+
+}
+
 START_TEST(toolbar)
 {
     WNDCLASSA wc;
@@ -403,6 +537,7 @@ START_TEST(toolbar)
     basic_test();
     test_add_bitmap();
     test_add_string();
+    test_hotitem();
 
     PostQuitMessage(0);
     while(GetMessageA(&msg,0,0,0)) {
diff --git a/dlls/comctl32/toolbar.c b/dlls/comctl32/toolbar.c
index db13165..4e8e215 100644
--- a/dlls/comctl32/toolbar.c
+++ b/dlls/comctl32/toolbar.c
@@ -4780,42 +4780,41 @@ TOOLBAR_SetHotItemEx (TOOLBAR_INFO *info
     {
         NMTBHOTITEM nmhotitem;
         TBUTTON_INFO *btnPtr = NULL, *oldBtnPtr = NULL;
-        LRESULT no_highlight;
 
-        /* Remove the effect of an old hot button if the button was
-           drawn with the hot button effect */
         if(infoPtr->nHotItem >= 0)
         {
             oldBtnPtr = &infoPtr->buttons[infoPtr->nHotItem];
-            oldBtnPtr->bHot = FALSE;
+            nmhotitem.idOld = oldBtnPtr->idCommand;
         }
+        else
+            nmhotitem.dwFlags |= HICF_ENTERING;
 
-        infoPtr->nHotItem = nHit;
-
-        /* It's not a separator or in nowhere. It's a hot button. */
         if (nHit >= 0)
+        {
             btnPtr = &infoPtr->buttons[nHit];
-
-	nmhotitem.dwFlags = dwReason;
-	if (oldBtnPtr)
-	    nmhotitem.idOld = oldBtnPtr->idCommand;
-	else
-	    nmhotitem.dwFlags |= HICF_ENTERING;
-	if (btnPtr)
-	    nmhotitem.idNew = btnPtr->idCommand;
+            nmhotitem.idNew = btnPtr->idCommand;
+            /* setting disabled buttons as hot fails */
+            if (!(btnPtr->fsState & TBSTATE_ENABLED))
+                return;
+        }
 	else
 	    nmhotitem.dwFlags |= HICF_LEAVING;
 
-	no_highlight = TOOLBAR_SendNotify(&nmhotitem.hdr, infoPtr, TBN_HOTITEMCHANGE);
+        nmhotitem.dwFlags = dwReason;
 
-	/* now invalidate the old and new buttons so they will be painted,
-	 * but only if they are enabled - disabled buttons cannot become hot */
-	if (oldBtnPtr && (oldBtnPtr->fsState & TBSTATE_ENABLED))
-	    InvalidateRect(infoPtr->hwndSelf, &oldBtnPtr->rect, TRUE);
-	if (btnPtr && !no_highlight && (btnPtr->fsState & TBSTATE_ENABLED))
+	/* now change the hot and invalidate the old and new buttons - if the
+	 * parent agrees */
+	if (!TOOLBAR_SendNotify(&nmhotitem.hdr, infoPtr, TBN_HOTITEMCHANGE))
 	{
-            btnPtr->bHot = TRUE;
-	    InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
+            infoPtr->nHotItem = nHit;
+            if (btnPtr) {
+                btnPtr->bHot = TRUE;
+                InvalidateRect(infoPtr->hwndSelf, &btnPtr->rect, TRUE);
+            }
+            if (oldBtnPtr) {
+                oldBtnPtr->bHot = FALSE;
+                InvalidateRect(infoPtr->hwndSelf, &oldBtnPtr->rect, TRUE);
+            }
         }
     }
 }
@@ -4828,14 +4827,16 @@ TOOLBAR_SetHotItem (HWND hwnd, WPARAM wP
 
     TRACE("hwnd = %p, nHit = %d\n", hwnd, (INT)wParam);
 
-    if ((INT) wParam < 0 || (INT)wParam > infoPtr->nNumButtons)
+    if ((INT)wParam > infoPtr->nNumButtons)
+        return infoPtr->nHotItem;
+    
+    if ((INT)wParam < 0)
         wParam = -1;
 
     /* NOTE: an application can still remove the hot item even if anchor
      * highlighting is enabled */
 
-    if ((infoPtr->dwStyle & TBSTYLE_FLAT) || GetWindowTheme (infoPtr->hwndSelf))
-        TOOLBAR_SetHotItemEx(infoPtr, wParam, HICF_OTHER);
+    TOOLBAR_SetHotItemEx(infoPtr, wParam, HICF_OTHER);
 
     if (nOldHotItem < 0)
         return -1;
-- 
1.4.2.3


More information about the wine-patches mailing list