This is soooo nice. I am very happy how it turned out. In fact, I am almost extatic because: -- the code turned out very nice, clean, and simple -- we are now virtually flicker free Please people, give it the worse lists, long, what have you: it should now be a pleasure to work with the thing: fast, light, etc. There are still sore spots: -- placing icons in LVS{SMALL,}ICON modes -- autoarrange in the above modes -- adding bulk items in LVS_OWNERDATA But other than that, we should be smocking. If not, I'd like to hear about it. ChangeLog Optimize invalidation on insert Share the invalidation code between {Delete,Insert}Item. --- dlls/comctl32/listview.c.V13 Mon Oct 21 00:57:06 2002 +++ dlls/comctl32/listview.c Mon Oct 21 01:29:57 2002 @@ -3969,6 +3970,70 @@ /*** * DESCRIPTION: + * Invalidates the listview after an item's insertion or deletion. + * + * PARAMETER(S): + * [I] infoPtr : valid pointer to the listview structure + * [I] nItem : item index + * [I] dir : -1 if deleting, 1 if inserting + * + * RETURN: + * None + */ +static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir) +{ + UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; + INT nPerCol, nItemCol, nItemRow; + RECT rcScroll; + POINT Origin; + + assert (abs(dir) == 1); + + /* arrange icons if autoarrange is on */ + if (infoPtr->dwStyle & LVS_AUTOARRANGE) + LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); + + /* scrollbars need updating */ + LISTVIEW_UpdateScroll(infoPtr); + + /* figure out the item's position */ + if (uView == LVS_REPORT) + nPerCol = infoPtr->nItemCount + 1; + else if (uView == LVS_LIST) + nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); + else /* LVS_ICON, or LVS_SMALLICON */ + return; + + nItemCol = nItem / nPerCol; + nItemRow = nItem % nPerCol; + LISTVIEW_GetOrigin(infoPtr, &Origin); + + /* move the items below up a slot */ + rcScroll.left = nItemCol * infoPtr->nItemWidth; + rcScroll.top = nItemRow * infoPtr->nItemHeight; + rcScroll.right = rcScroll.left + infoPtr->nItemWidth; + rcScroll.bottom = nPerCol * infoPtr->nItemHeight; + OffsetRect(&rcScroll, Origin.x, Origin.y); + if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) + ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, + &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); + + /* report has only that column, so we're done */ + if (uView == LVS_REPORT) return; + + /* now for LISTs, we have to deal with the columns to the right */ + rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth; + rcScroll.top = 0; + rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth; + rcScroll.bottom = nPerCol * infoPtr->nItemHeight; + OffsetRect(&rcScroll, Origin.x, Origin.y); + if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) + ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, + &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); +} + +/*** + * DESCRIPTION: * Removes an item from the listview control. * * PARAMETER(S): @@ -3982,12 +4047,9 @@ static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem) { UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; - INT nPerCol, nItemCol, nItemRow; NMLISTVIEW nmlv; LVITEMW item; - BOOL is_icon; - RECT rcScroll; - POINT Origin; + RECT rcBox; TRACE("(nItem=%d)\n", nItem); @@ -3998,6 +4060,10 @@ item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; LISTVIEW_SetItemState(infoPtr, nItem, &item); + /* we need to do this here, because we'll be deleting stuff */ + if (uView == LVS_SMALLICON || uView == LVS_ICON) + LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); + /* send LVN_DELETEITEM notification. */ ZeroMemory(&nmlv, sizeof (NMLISTVIEW)); nmlv.iItem = nItem; @@ -4018,13 +4084,9 @@ } DPA_Destroy(hdpaSubItems); } - - is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON); - if (is_icon) - { - RECT rcBox; - LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); + if (uView == LVS_SMALLICON || uView == LVS_ICON) + { DPA_DeletePtr(infoPtr->hdpaPosX, nItem); DPA_DeletePtr(infoPtr->hdpaPosY, nItem); LISTVIEW_InvalidateRect(infoPtr, &rcBox); @@ -4033,48 +4095,8 @@ infoPtr->nItemCount--; LISTVIEW_ShiftIndices(infoPtr, nItem, -1); - if (is_icon && (infoPtr->dwStyle & LVS_AUTOARRANGE)) - LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); - - LISTVIEW_UpdateScroll(infoPtr); - /* now is the invalidation fun */ - - /* there's nothing else to do in icon mode */ - if (is_icon) return TRUE; - - /* figure out the item's position */ - if (uView == LVS_REPORT) - nPerCol = infoPtr->nItemCount + 1; - else /* LVS_LIST */ - nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); - nItemCol = nItem / nPerCol; - nItemRow = nItem % nPerCol; - LISTVIEW_GetOrigin(infoPtr, &Origin); - - /* move the items below up a slot */ - rcScroll.left = nItemCol * infoPtr->nItemWidth; - rcScroll.top = nItemRow * infoPtr->nItemHeight; - rcScroll.right = rcScroll.left + infoPtr->nItemWidth; - rcScroll.bottom = nPerCol * infoPtr->nItemHeight; - OffsetRect(&rcScroll, Origin.x, Origin.y); - if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) - ScrollWindowEx(infoPtr->hwndSelf, 0, -infoPtr->nItemHeight, - &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); - - /* report has only that column, so we're done */ - if (uView == LVS_REPORT) return TRUE; - - /* now for LISTs, we have to deal with the columns to the right */ - rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth; - rcScroll.top = 0; - rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth; - rcScroll.bottom = nPerCol * infoPtr->nItemHeight; - OffsetRect(&rcScroll, Origin.x, Origin.y); - if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) - ScrollWindowEx(infoPtr->hwndSelf, 0, -infoPtr->nItemHeight, - &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); - + LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1); return TRUE; } @@ -5674,17 +5696,18 @@ notify_listview(infoPtr, LVN_INSERTITEM, &nmlv); /* align items (set position of each item) */ - if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) + if ((uView == LVS_SMALLICON || uView == LVS_ICON)) { - if (infoPtr->dwStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr); - else LISTVIEW_AlignTop(infoPtr); + RECT rcBox; + /* FIXME: compute X, Y here, inline */ + /*if (is_icon && (infoPtr->dwStyle & LVS_AUTOARRANGE))*/ + LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); + LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); + LISTVIEW_InvalidateRect(infoPtr, &rcBox); } - LISTVIEW_UpdateScroll(infoPtr); - - LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */ - - TRACE(" <- %d\n", nItem); + /* now is the invalidation fun */ + LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1); return nItem; undo: