[PATCH v11 0/2] MR10207: comctl32/treeview: Cycle all state images from custom image list when toggling checkboxes
Bug and test program- https://bugs.winehq.org/show_bug.cgi?id=59486 This bug breaks foobar2000 advanced preferences - radiocheckboxes work only because I have very specific hack around this Wine behavior. My next treeview patch, fixing TVM_ITEMCHANGING, depends on this one and will be made a MR after this is merged. -- v11: comctl32/treeview: Cycle all state images from custom image list when toggling checkboxes Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59486 comctl32/tests: Test cycling all state images from custom image list when toggling checkboxes https://gitlab.winehq.org/wine/wine/-/merge_requests/10207
From: Piotr Pawłowski <p@perkele.cc> --- dlls/comctl32/tests/treeview.c | 142 +++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c index fca750deaad..1265bafcfbd 100644 --- a/dlls/comctl32/tests/treeview.c +++ b/dlls/comctl32/tests/treeview.c @@ -39,6 +39,11 @@ static HTHEME (WINAPI *pGetWindowTheme)(HWND); static BOOL (WINAPI *pIsThemeBackgroundPartiallyTransparent)(HTHEME, int, int); static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); + +static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int); +static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); +static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP); + static const char *TEST_CALLBACK_TEXT = "callback_text"; static TVITEMA g_item_expanding, g_item_expanded; @@ -2564,6 +2569,67 @@ static void test_htreeitem_layout(BOOL is_version_6) DestroyWindow(hTree); } +/* Create a 3-state (unchecked/checked/indeterminate) image list */ +static HIMAGELIST setup_3state_imagelist(HWND wndTree) +{ + int itemHeight; + HDC dc; HBITMAP bmp; + RECT rcDraw; + const HBRUSH brush = (HBRUSH)GetStockObject(DC_BRUSH); + HIMAGELIST images; + HGDIOBJ restore; + + if (wndTree == NULL) return NULL; + itemHeight = (int)SendMessageA(wndTree, TVM_GETITEMHEIGHT, 0, 0); + if ( itemHeight <= 0 ) return NULL; + dc = GetDC(wndTree); + if ( dc == NULL ) return NULL; + bmp = CreateCompatibleBitmap(dc, itemHeight, itemHeight); + ReleaseDC(wndTree, dc); + if ( bmp == NULL ) return NULL; + + images = pImageList_Create(itemHeight, itemHeight, ILC_COLORDDB, 0, 5); + if ( images == NULL ) return NULL; + + dc = CreateCompatibleDC(NULL); + if ( dc == NULL ) + { + pImageList_Destroy(images); + return NULL; + } + + rcDraw.left = 0; rcDraw.top = 0; rcDraw.right = itemHeight; rcDraw.bottom = itemHeight; + + SetDCBrushColor(dc, GetSysColor(COLOR_WINDOW)); + + restore = SelectObject(dc, bmp); + FillRect(dc, &rcDraw, brush); + SelectObject(dc, restore); + pImageList_Add(images, bmp, NULL); + + restore = SelectObject(dc, bmp); + FillRect(dc, &rcDraw, brush); + DrawFrameControl(dc, &rcDraw, DFC_BUTTON, DFCS_BUTTONCHECK); + SelectObject(dc, restore); + pImageList_Add(images, bmp, NULL); + + restore = SelectObject(dc, bmp); + FillRect(dc, &rcDraw, brush); + DrawFrameControl(dc, &rcDraw, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED); + SelectObject(dc, restore); + pImageList_Add(images, bmp, NULL); + + restore = SelectObject(dc, bmp); + FillRect(dc, &rcDraw, brush); + DrawFrameControl(dc, &rcDraw, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_BUTTON3STATE); + SelectObject(dc, restore); + pImageList_Add(images, bmp, NULL); + + DeleteDC(dc); + + return images; +} + static void test_TVS_CHECKBOXES(void) { HIMAGELIST himl, himl2; @@ -2746,6 +2812,79 @@ static void test_TVS_CHECKBOXES(void) himl = (HIMAGELIST)SendMessageA(hTree, TVM_GETIMAGELIST, TVSIL_STATE, 0); ok(himl != NULL, "got %p\n", himl); + /* + Check cycling item states with default image list + 1 >> 2 >> 1 + */ + + item.hItem = hChild; + item.mask = TVIF_STATE; + item.state = INDEXTOSTATEIMAGEMASK(1); + ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + + ret = SendMessageA(hTree,TVM_SELECTITEM,TVGN_CARET,(LPARAM)hChild); + expect(TRUE, ret); + + SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); + item.hItem = hChild; + item.mask = TVIF_STATE; + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2), "item.state=%x\n", item.state); + + SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); + item.hItem = hChild; + item.mask = TVIF_STATE; + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1), "item.state=%x\n", item.state); + + /* + Check cycling item states with 3-state image list + 1 >> 2 >> 3 >> 1 + */ + + himl2 = setup_3state_imagelist(hTree); + ok(himl2 != NULL, "setup_3state_imagelist: %p\n", himl2); + + himl = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl2); + ok(himl != NULL, "got %p\n", himl); + + item.hItem = hChild; + item.mask = TVIF_STATE; + item.state = INDEXTOSTATEIMAGEMASK(1); + ret = SendMessageA(hTree, TVM_SETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + + ret = SendMessageA(hTree,TVM_SELECTITEM,TVGN_CARET,(LPARAM)hChild); + expect(TRUE, ret); + + SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); + item.hItem = hChild; + item.mask = TVIF_STATE; + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2), "item.state=%x\n", item.state); + + SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); + item.hItem = hChild; + item.mask = TVIF_STATE; + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + todo_wine ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(3), "item.state=%x\n", item.state); + + SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); + item.hItem = hChild; + item.mask = TVIF_STATE; + ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + todo_wine ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1), "item.state=%x\n", item.state); + + himl = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl); + ok(himl == himl2, "got %p\n", himl); + pImageList_Destroy(himl2); + DestroyWindow(hTree); } @@ -3191,6 +3330,9 @@ static void init_functions(void) #define X(module, f) p##f = (void*)GetProcAddress(module, #f); X(hComCtl32, InitCommonControlsEx); + X(hComCtl32, ImageList_Create); + X(hComCtl32, ImageList_Destroy); + X(hComCtl32, ImageList_Add); X(hUxtheme, GetWindowTheme); X(hUxtheme, IsThemeBackgroundPartiallyTransparent); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10207
From: Piotr Pawłowski <p@perkele.cc> --- dlls/comctl32/tests/treeview.c | 4 ++-- dlls/comctl32/treeview.c | 29 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c index 1265bafcfbd..4af67ccd660 100644 --- a/dlls/comctl32/tests/treeview.c +++ b/dlls/comctl32/tests/treeview.c @@ -2872,14 +2872,14 @@ static void test_TVS_CHECKBOXES(void) item.mask = TVIF_STATE; ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, ret); - todo_wine ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(3), "item.state=%x\n", item.state); + ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(3), "item.state=%x\n", item.state); SendMessageA(hTree, WM_KEYDOWN, VK_SPACE, 0); item.hItem = hChild; item.mask = TVIF_STATE; ret = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, ret); - todo_wine ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1), "item.state=%x\n", item.state); + ok((item.state&TVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(1), "item.state=%x\n", item.state); himl = (HIMAGELIST)SendMessageA(hTree, TVM_SETIMAGELIST, TVSIL_STATE, (LPARAM)himl); ok(himl == himl2, "got %p\n", himl); diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c index 8b38f098553..94ff829ff9d 100644 --- a/dlls/comctl32/treeview.c +++ b/dlls/comctl32/treeview.c @@ -2342,21 +2342,30 @@ TREEVIEW_ToggleItemState(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item) { if (infoPtr->dwStyle & TVS_CHECKBOXES) { - static const unsigned int state_table[] = { 0, 2, 1 }; + unsigned int state, stateImage, numStates; - unsigned int state; + /* Toggle item state cycles through images other than zero, if image list with more state images is set */ + numStates = 0; + if ( infoPtr->himlState ) numStates = ImageList_GetImageCount(infoPtr->himlState); + if ( numStates < 3 ) numStates = 3; - state = STATEIMAGEINDEX(item->state); - TRACE("state: 0x%x\n", state); - item->state &= ~TVIS_STATEIMAGEMASK; + state = item->state; + stateImage = STATEIMAGEINDEX(state); + TRACE("stateImage: 0x%x\n", stateImage); + state &= ~TVIS_STATEIMAGEMASK; - if (state < 3) - state = state_table[state]; + if ( stateImage > 0 ) + { + ++ stateImage; + if ( stateImage >= numStates ) stateImage = 1; + } - item->state |= INDEXTOSTATEIMAGEMASK(state); + state |= INDEXTOSTATEIMAGEMASK(stateImage); - TRACE("state: 0x%x\n", state); - TREEVIEW_Invalidate(infoPtr, item); + TRACE("stateImage: 0x%x\n", stateImage); + + item->state = state; + TREEVIEW_Invalidate(infoPtr, item); } } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10207
participants (1)
-
Piotr Pawłowski