This one unifies setting of item attributes between LVS_OWNERDATA, and normal. It also fixes a few potential bugs lurking in there. I don't have a OWNERDATA testcase, so testing from people with such beast is needed. Both positive, and negative feedback is appreciated. ChangeLog Unify set_{owner,main}_item functions Fix {old,new}state and lParam reporting in LVN_ITEMCHANG{ING,ED} Fix sanity check conditions for LVS_OWNERDATA. --- dlls/comctl32/listview.c.S7 Thu Oct 17 10:57:24 2002 +++ dlls/comctl32/listview.c Thu Oct 17 11:50:37 2002 @@ -2868,6 +2868,7 @@ return TRUE; } + /*** * DESCRIPTION: * Helper for LISTVIEW_SetItemT *only*: sets item attributes. @@ -2882,101 +2883,39 @@ * SUCCESS : TRUE * FAILURE : FALSE */ -static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged) +static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged) { - LONG lStyle = infoPtr->dwStyle; + LISTVIEW_ITEM *lpItem; NMLISTVIEW nmlv; - INT oldState; + UINT uChanged = 0; + LVITEMW item; TRACE("()\n"); - /* a virtual listview stores only the state for the main item */ - if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE; - - oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask; - if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState); - TRACE("oldState=%x, newState=%x\n", oldState, lpLVItem->state); - - /* we're done if we don't need to change anything we handle */ - if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask & - ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE; - - *bChanged = TRUE; - - /* - * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for - * by LVS_OWERNDATA list controls - */ - - /* if we handle the focus, and we're asked to change it, do it now */ - if ( lpLVItem->stateMask & LVIS_FOCUSED ) + if (infoPtr->dwStyle & LVS_OWNERDATA) { - if (lpLVItem->state & LVIS_FOCUSED) - infoPtr->nFocusedItem = lpLVItem->iItem; - else if (infoPtr->nFocusedItem == lpLVItem->iItem) - infoPtr->nFocusedItem = -1; + /* a virtual listview we stores only selection and focus */ + if ((lpLVItem->mask & ~LVIF_STATE) || (lpLVItem->stateMask & ~(LVIS_FOCUSED | LVIS_SELECTED))) + return FALSE; + lpItem = NULL; } - - /* and the selection is the only other state a virtual list may hold */ - if (lpLVItem->stateMask & LVIS_SELECTED) + else { - if (lpLVItem->state & LVIS_SELECTED) - { - if (lStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); - ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem); - } - else - ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem); + HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); + if (!hdpaSubItems) return FALSE; + if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return FALSE; } - /* notify the parent now that things have changed */ - ZeroMemory(&nmlv, sizeof(nmlv)); - nmlv.iItem = lpLVItem->iItem; - nmlv.uNewState = lpLVItem->state; - nmlv.uOldState = oldState; - nmlv.uChanged = LVIF_STATE; - notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); - - return TRUE; -} - -/*** - * DESCRIPTION: - * Helper for LISTVIEW_SetItemT *only*: sets item attributes. - * - * PARAMETER(S): - * [I] infoPtr : valid pointer to the listview structure - * [I] lpLVItem : valid pointer to new item atttributes - * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI - * [O] bChanged : will be set to TRUE if the item really changed - * - * RETURN: - * SUCCESS : TRUE - * FAILURE : FALSE - */ -static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged) -{ - LONG lStyle = infoPtr->dwStyle; - HDPA hdpaSubItems; - LISTVIEW_ITEM *lpItem; - NMLISTVIEW nmlv; - UINT uChanged = 0, oldState; - - TRACE("()\n"); - - hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); - if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE; - - lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0); - if (!lpItem) return FALSE; - /* we need to handle the focus, and selection differently */ - oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask; - if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState); + item.iItem = lpLVItem->iItem; + item.iSubItem = lpLVItem->iSubItem; + item.mask = LVIF_STATE | LVIF_PARAM; + item.stateMask = ~0; + if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE; - TRACE("oldState=%x, newState=%x\n", oldState, lpLVItem->state); + TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state); /* determine what fields will change */ - if ((lpLVItem->mask & LVIF_STATE) && ((oldState ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask)) + if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask)) uChanged |= LVIF_STATE; if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage)) @@ -2997,13 +2936,14 @@ ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.iItem = lpLVItem->iItem; - nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask; - nmlv.uOldState = lpItem->state & lpLVItem->stateMask; + nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask); + nmlv.uOldState = item.state; nmlv.uChanged = uChanged; - nmlv.lParam = lpItem->lParam; + nmlv.lParam = item.lParam; /* send LVN_ITEMCHANGING notification, if the item is not being inserted */ - if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv)) + /* and we are _NOT_ virtual (LVS_OWERNDATA) */ + if(lpItem && lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv)) return FALSE; /* copy information */ @@ -3021,11 +2961,14 @@ if (uChanged & LVIF_STATE) { - lpItem->state &= ~lpLVItem->stateMask; - lpItem->state |= (lpLVItem->state & lpLVItem->stateMask); + if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)) + { + lpItem->state &= ~lpLVItem->stateMask; + lpItem->state |= (lpLVItem->state & lpLVItem->stateMask); + } if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED) { - if (lStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); + if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem); } else if (lpLVItem->stateMask & LVIS_SELECTED) @@ -3045,10 +2988,10 @@ } /* if we're inserting the item, we're done */ - if (!lpItem->valid) return TRUE; + if (lpItem && !lpItem->valid) return TRUE; /* send LVN_ITEMCHANGED notification */ - nmlv.lParam = lpItem->lParam; + if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); return TRUE; @@ -3073,6 +3016,9 @@ HDPA hdpaSubItems; LISTVIEW_SUBITEM *lpSubItem; + /* we do not support subitems for virtual listviews */ + if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; + /* set subitem only if column is present */ if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem) return FALSE; @@ -3149,7 +3095,9 @@ if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) return FALSE; - + + if (lpLVItem->mask == 0) return TRUE; + /* For efficiency, we transform the lpLVItem->pszText to Unicode here */ if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText)) { @@ -3158,18 +3106,12 @@ } /* actually set the fields */ - if (infoPtr->dwStyle & LVS_OWNERDATA) - bResult = set_owner_item(infoPtr, lpLVItem, TRUE, &bChanged); - else - { - /* sanity checks first */ - if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE; + if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE; - if (lpLVItem->iSubItem) - bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged); - else - bResult = set_main_item(infoPtr, lpLVItem, TRUE, &bChanged); - } + if (lpLVItem->iSubItem) + bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged); + else + bResult = set_main_item(infoPtr, lpLVItem, TRUE, &bChanged); /* redraw item, if necessary */ if (bChanged && !infoPtr->bIsDrawing)