From 10283f4c063e0e39a46fd45b220c40fced616246 Mon Sep 17 00:00:00 2001 From: Huw Campbell Date: Tue, 24 Feb 2026 19:21:46 +1100 Subject: [PATCH] Fix ownership of the puColumns data. Testing confirmed that the data is copied into the list view and can be discarded by the caller. Docs showed that the list view writes into the caller position, and doesn't change the pointer value. Interestingly, when a callback is used, the listview allocates to place the data, probably to not "split" ownership or double allocate. --- dll/win32/comctl32/listview.c | 49 +++++++++---------- .../rostests/winetests/comctl32/listview.c | 1 + 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/dll/win32/comctl32/listview.c b/dll/win32/comctl32/listview.c index fcd3dbefcf2..47d9d753ebd 100644 --- a/dll/win32/comctl32/listview.c +++ b/dll/win32/comctl32/listview.c @@ -555,8 +555,8 @@ static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW) int r = bw ? lstrcmpW(aw, bw) : 1; textfreeT(bw, isW); return r; - } - + } + return 1; } @@ -2467,7 +2467,6 @@ static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW RECT Box, SelectBox, Icon, Label; COLUMN_INFO *lpColumnInfo = NULL; SIZE labelSize = { 0, 0 }; - INT subItemOffset; TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE)); @@ -2661,24 +2660,12 @@ calc_label: Label.right = Label.left + labelSize.cx; Label.bottom = Label.top + labelSize.cy;; } - else if (infoPtr->tileLines && lpLVItem->cColumns && lpLVItem->puColumns) { - /* Calculate the offset for each sub item in tiles view. - * Fixme: Doing scan over the row even though we should just process them in order. */ - subItemOffset = 0; - for (INT subItemSearch = 0; subItemSearch < lpLVItem->cColumns; subItemSearch++) { - if (lpLVItem->iSubItem == lpLVItem->puColumns[subItemSearch]) { - subItemOffset = subItemSearch + 1; - break; - } - } - if (subItemOffset) { - Label.top = Box.top + subItemOffset * (infoPtr->ntmHeight + infoPtr->ntmExternalLeading); - Label.right = Label.left + labelSize.cx; - Label.bottom = Label.top + labelSize.cy;; - } else { - Label.top = Label.bottom = Box.top; - Label.right = Label.left; - } + else if (infoPtr->tileLines && lpLVItem->cColumns) { + /* Side channel. Subitems can't have cColumns; and the fetch won't overwrite it, so put + * the index of the sub-item into the cColumns field. */ + Label.top = Box.top + lpLVItem->cColumns * (infoPtr->ntmHeight + infoPtr->ntmExternalLeading); + Label.right = Label.left + labelSize.cx; + Label.bottom = Label.top + labelSize.cy;; } else { Label.top = Label.bottom = Box.top; Label.right = Label.left; @@ -5065,6 +5052,7 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERAT RECT *focus, rcBox; NMLVCUSTOMDRAW nmlvcd; LVITEMW lvItem = {0}; + UINT puColumns[20] = {0}; INT j; TRACE("(hdc=%p, nItem=%d, subitems=%p, pos=%s)\n", hdc, nItem, subitems, wine_dbgstr_point(&pos)); @@ -5072,7 +5060,11 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERAT /* get information needed for drawing the item */ lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE; if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT; - if (infoPtr->uView == LV_VIEW_TILE) lvItem.mask |= LVIF_COLUMNS; + if (infoPtr->uView == LV_VIEW_TILE) { + lvItem.mask |= LVIF_COLUMNS; + lvItem.cColumns = 20; + lvItem.puColumns = puColumns; + } lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT | LVIS_OVERLAYMASK; lvItem.iItem = nItem; lvItem.cchTextMax = DISP_TEXT_SIZE; @@ -5169,18 +5161,25 @@ static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, ITERAT * * Reports mode is a bit different here, there can be many columns, so it has to be careful to * only pull the few required, as many could be off screen. + * + * NOTE: TileView Positions + * Because these are laid out in order, and their displacement depends on those preceding them, + * we pack some side channel information into lvItem.cColumns. */ if (infoPtr->uView == LV_VIEW_TILE && infoPtr->tileLines) { + lvItem.mask = LVIF_TEXT; + lvItem.puColumns = NULL; + j = min(infoPtr->tileLines, lvItem.cColumns); - for (UINT subIx; subIx < j; subIx++) + for (UINT subIx = 0; subIx < j; subIx++) { - lvItem.iSubItem = lvItem.puColumns[subIx]; + lvItem.cColumns = subIx + 1; + lvItem.iSubItem = puColumns[subIx]; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; szDispText[0] = 0; - lvItem.mask = LVIF_TEXT; if (LISTVIEW_GetItemW(infoPtr, &lvItem)) LISTVIEW_DrawItemPart(infoPtr, &lvItem, &nmlvcd, &pos); diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index 75004b4f65b..26846b812b3 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -5617,6 +5617,7 @@ static void test_hover(void) } /* test WM_MOUSEHOVER forwarding */ + g_block_hover = FALSE; flush_sequences(sequences, NUM_MSG_SEQUENCES); r = SendMessageA(hwnd, WM_MOUSEHOVER, 0, 0); expect(0, r); -- 2.51.2