This one is essential: it will allow some fundamental optimizations, in particular in {SMALL,}ICON modes, which are just brutal right now. It introduces the notion of a item iterator; it's really neat, IMO. Next will be the optimizations I was talking about, on top of this one. ChangeLog Introduce the notion of item iterators, and use them to clean up code. --- dlls/comctl32/listview.c.M9 Tue Oct 8 21:32:06 2002 +++ dlls/comctl32/listview.c Wed Oct 9 03:15:11 2002 @@ -74,8 +74,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(listview); -/* Some definitions for inline edit control */ - typedef struct tagCOLUMNCACHE { RECT rc; @@ -109,6 +107,14 @@ INT upper; } RANGE; +typedef struct tagITERATOR +{ + INT nItem; + RANGE range; + HDPA ranges; + INT index; +} ITERATOR; + typedef struct tagLISTVIEW_INFO { HWND hwndSelf; @@ -637,12 +643,12 @@ return bResult; } -static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo) +static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, RANGE range) { NMLVCACHEHINT nmlv; - nmlv.iFrom = iFrom; - nmlv.iTo = iTo; + nmlv.iFrom = range.lower; + nmlv.iTo = range.upper; notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr); } @@ -711,6 +717,106 @@ return bReturn; } +/******** Item iterator functions **********************************/ + +static BOOL iterator_create_frameditems(ITERATOR*, LISTVIEW_INFO*, const RECT*, HRGN); + +static inline BOOL iterator_next(ITERATOR* i) +{ + if (i->nItem == -1) + { + if (i->ranges) goto pickarange; + return (i->nItem = i->range.lower) != -1; + } + i->nItem++; + if (i->nItem <= i->range.upper) + return TRUE; + +pickarange: + if (i->ranges && i->index < i->ranges->nItemCount) + { + i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++); + return (i->nItem = i->range.lower) != -1; + } + i->nItem = i->range.lower = i->range.upper = -1; + return FALSE; +} + +static RANGE iterator_range(ITERATOR* i) +{ + RANGE range; + + if (!i->ranges) return i->range; + + range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower; + range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper; + return range; +} + +static inline void iterator_destroy(ITERATOR* i) +{ + if (i->ranges) DPA_Destroy(i->ranges); +} + +static inline BOOL iterator_create_empty(ITERATOR* i) +{ + ZeroMemory(i, sizeof(*i)); + i->nItem = i->range.lower = i->range.upper = -1; + return TRUE; +} + +static inline BOOL iterator_create_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr) +{ + return iterator_create_frameditems(i, infoPtr, &infoPtr->rcList, 0); +} + +static BOOL iterator_create_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc) +{ + HRGN rgnClip; + RECT rcClip; + INT rgntype; + + rgntype = GetClipBox(hdc, &rcClip); + if (rgntype == NULLREGION) + return iterator_create_empty(i); + if (rgntype == SIMPLEREGION) + rgnClip = 0; + else + { + FIXME("TODO: complex clipped region!\n"); + rgnClip = 0; + } + return iterator_create_frameditems(i, infoPtr, &rcClip, rgnClip); +} + +static BOOL iterator_create_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc, HRGN rgn) +{ + UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; + INT nPerCol, nPerRow; + + if (rgn) FIXME("Don't know how to handle regions yet.\n"); + + /* in case we fail, we want to return an empty iterator */ + if (!iterator_create_empty(i)) return FALSE; + + if (uView == LVS_ICON || uView == LVS_SMALLICON) + { + i->range.lower = 0; + i->range.upper = infoPtr->nItemCount; + return TRUE; + } + + i->range.lower = LISTVIEW_GetTopIndex(infoPtr); + + nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1) + 1; + if (uView == LVS_REPORT) + nPerRow = 1; + else /* uView == LVS_LIST */ + nPerRow = max((infoPtr->rcList.right - infoPtr->rcList.left)/infoPtr->nItemWidth, 1); + + i->range.upper = min(i->range.lower + nPerCol * nPerRow, infoPtr->nItemCount); + return TRUE; +} /******** Misc helper functions ************************************/ static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg, @@ -783,51 +889,6 @@ return max(nListHeight / infoPtr->nItemHeight, 1); } -/*** - * DESCRIPTION: - * Retrieves the range of visible items. Note that the upper limit - * may be a bit larger than the actual last visible item. - * - * PARAMETER(S): - * [I] infoPtr : valid pointer to the listview structure - * - * RETURN: - * maximum range of visible items - */ -static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr) -{ - UINT uView = LISTVIEW_GetType(infoPtr); - INT nPerCol, nPerRow; - RANGE visrange; - - visrange.lower = LISTVIEW_GetTopIndex(infoPtr); - - if (uView == LVS_REPORT) - { - nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1; - nPerRow = 1; - } - else if (uView == LVS_LIST) - { - nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1; - nPerRow = LISTVIEW_GetCountPerRow(infoPtr); - } - else - { - /* FIXME: this is correct only in autoarrange mode */ - nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1; - nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1; - } - - visrange.upper = visrange.lower + nPerCol * nPerRow; - if (visrange.upper > infoPtr->nItemCount) - visrange.upper = infoPtr->nItemCount; - - TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper); - - return visrange; -} - /************************************************************************* * LISTVIEW_ProcessLetterKeys @@ -1201,15 +1262,15 @@ */ static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr) { - RANGE visrange; - INT i; - - visrange = LISTVIEW_GetVisibleRange(infoPtr); - for (i = visrange.lower; i <= visrange.upper; i++) + ITERATOR i; + + iterator_create_visibleitems(&i, infoPtr); + while(iterator_next(&i)) { - if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) - LISTVIEW_InvalidateItem(infoPtr, i); + if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)) + LISTVIEW_InvalidateItem(infoPtr, i.nItem); } + iterator_destroy(&i); } @@ -2327,18 +2388,21 @@ else { RECT rcItem, rcSelMark; + ITERATOR i; rcItem.left = LVIR_BOUNDS; if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return; rcSelMark.left = LVIR_BOUNDS; if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return; UnionRect(&rcSel, &rcItem, &rcSelMark); - for (i = 0; i <= infoPtr->nItemCount; i++) + iterator_create_frameditems(&i, infoPtr, &rcSel, 0); + while(iterator_next(&i)) { - LISTVIEW_GetItemPosition(infoPtr, i, &ptItem); + LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem); if (PtInRect(&rcSel, ptItem)) - LISTVIEW_SetItemState(infoPtr, i, &item); + LISTVIEW_SetItemState(infoPtr, i.nItem, &item); } + iterator_destroy(&i); } LISTVIEW_SetItemFocus(infoPtr, nItem); @@ -2434,23 +2498,25 @@ * SUCCESS : item index * FAILURE : -1 */ +/* FIXME: get rid of this function, use HitTest instead */ static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt) { - RANGE visrange; + ITERATOR i; RECT rcItem; - INT i; - - visrange = LISTVIEW_GetVisibleRange(infoPtr); - for (i = visrange.lower; i <= visrange.upper; i++) + + iterator_create_visibleitems(&i, infoPtr); + while(iterator_next(&i)) { rcItem.left = LVIR_SELECTBOUNDS; - if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem)) + if (LISTVIEW_GetItemRect(infoPtr, i.nItem, &rcItem)) { - TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem)); - if (PtInRect(&rcItem, pt)) return i; + TRACE("i=%d, rcItem=%s\n", i.nItem, debugrect(&rcItem)); + if (PtInRect(&rcItem, pt)) break; } } - return -1; + iterator_destroy(&i); + + return i.nItem; } /*** @@ -3286,44 +3352,31 @@ */ static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc) { - INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth, rgntype; UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID); HWND hwndParent = GetParent(infoPtr->hwndSelf); POINT Origin, Position; DRAWITEMSTRUCT dis; LVITEMW item; - RECT rcClip; + ITERATOR i; TRACE("()\n"); - /* figure out what to draw */ - /* FIXME: this works for REPORT only */ - rgntype = GetClipBox(hdc, &rcClip); - if (rgntype == NULLREGION) return; - nUpdateHeight = rcClip.bottom - rcClip.top + 1; - nUpdateWidth = rcClip.right - rcClip.left; - nTop = LISTVIEW_GetTopIndex(infoPtr); - nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight; - if (nItem < nTop) - nItem = nTop; - nLast = nItem + nUpdateHeight / infoPtr->nItemHeight; - if (nUpdateHeight % infoPtr->nItemHeight) nLast++; - if (nLast > infoPtr->nItemCount) - nLast = infoPtr->nItemCount; ZeroMemory(&dis, sizeof(dis)); /* Get scroll info once before loop */ if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return; - + /* figure out what we need to draw */ + iterator_create_clippeditems(&i, infoPtr, hdc); + /* send cache hint notification */ if (infoPtr->dwStyle & LVS_OWNERDATA) - notify_odcachehint(infoPtr, nItem, nLast); + notify_odcachehint(infoPtr, iterator_range(&i)); - /* iterate through the invalidated rows */ - for (; nItem < nLast; nItem++) + /* iterate through the invalidated rows */ + while(iterator_next(&i)) { - item.iItem = nItem; + item.iItem = i.nItem; item.iSubItem = 0; item.mask = LVIF_PARAM | LVIF_STATE; item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; @@ -3331,14 +3384,14 @@ dis.CtlType = ODT_LISTVIEW; dis.CtlID = uID; - dis.itemID = nItem; + dis.itemID = item.iItem; dis.itemAction = ODA_DRAWENTIRE; dis.itemState = 0; if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED; if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS; dis.hwndItem = infoPtr->hwndSelf; dis.hDC = hdc; - if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue; + if (!LISTVIEW_GetItemListOrigin(infoPtr, dis.itemID, &Position)) continue; dis.rcItem.left = Position.x + Origin.x; dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth; dis.rcItem.top = Position.y + Origin.y; @@ -3348,6 +3401,7 @@ TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem)); SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); } + iterator_destroy(&i); } /*** @@ -3365,7 +3419,6 @@ static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode) { INT rgntype, nDrawPosY, j; - INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth; INT nColumnCount, nFirstCol, nLastCol; RECT rcItem, rcClip, rcFullSelect; BOOL bFullSelected, isFocused; @@ -3375,27 +3428,14 @@ LVCOLUMNW lvColumn; LVITEMW item; POINT ptOrig; + ITERATOR i; TRACE("()\n"); /* figure out what to draw */ rgntype = GetClipBox(hdc, &rcClip); if (rgntype == NULLREGION) return; - nUpdateHeight = rcClip.bottom - rcClip.top + 1; - nUpdateWidth = rcClip.right - rcClip.left; - nTop = LISTVIEW_GetTopIndex(infoPtr); - nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight; - if (nItem < nTop) - nItem = nTop; - nLast = nItem + nUpdateHeight / infoPtr->nItemHeight; - if (nUpdateHeight % infoPtr->nItemHeight) nLast++; - if (nLast > infoPtr->nItemCount) - nLast = infoPtr->nItemCount; - - /* send cache hint notification */ - if (infoPtr->dwStyle & LVS_OWNERDATA) - notify_odcachehint(infoPtr, nItem, nLast); - + /* cache column info */ nColumnCount = Header_GetItemCount(infoPtr->hwndHeader); lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE)); @@ -3428,27 +3468,34 @@ else if (lvColumn.fmt & LVCFMT_CENTER) lpCols[j].align = DT_CENTER; } - - /* a last few bits before we start drawing */ - TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n", - nTop, nItem, nLast, nFirstCol, nLastCol); - bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT; - nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight; /* save dc values we're gonna trash while drawing */ oldTa.bkMode = GetBkMode(hdc); oldTa.bkColor = GetBkColor(hdc); oldTa.fgColor = GetTextColor(hdc); - /* iterate through the invalidated rows */ - for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight) + /* figure out what we need to draw */ + iterator_create_clippeditems(&i, infoPtr, hdc); + + /* a last few bits before we start drawing */ + bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT; + TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol); + + /* send cache hint notification */ + if (infoPtr->dwStyle & LVS_OWNERDATA) + notify_odcachehint(infoPtr, iterator_range(&i)); + + /* iterate through the invalidated rows */ + while(iterator_next(&i)) { + nDrawPosY = i.nItem * infoPtr->nItemHeight; + /* compute the full select rectangle, if needed */ if (bFullSelected) { item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT; item.stateMask = LVIS_SELECTED; - item.iItem = nItem; + item.iItem = i.nItem; item.iSubItem = 0; if (!LISTVIEW_GetItemW(infoPtr, &item)) continue; @@ -3458,7 +3505,7 @@ rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX); rcFullSelect.top = nDrawPosY; rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight; - OffsetRect(&rcFullSelect, ptOrig.x, 0); + OffsetRect(&rcFullSelect, ptOrig.x, ptOrig.y); } /* draw the background of the selection rectangle, if need be */ @@ -3471,7 +3518,7 @@ for (j = nFirstCol; j <= nLastCol; j++) { if (cdmode & CDRF_NOTIFYITEMDRAW) - cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT); + cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, j, CDDS_ITEMPREPAINT); if (cditemmode & CDRF_SKIPDEFAULT) continue; rcItem = lpCols[j].rc; @@ -3481,20 +3528,21 @@ rcItem.bottom = rcItem.top + infoPtr->nItemHeight; /* Offset the Scroll Bar Pos */ - OffsetRect(&rcItem, ptOrig.x, 0); + OffsetRect(&rcItem, ptOrig.x, ptOrig.y); if (j == 0) - isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem); + isFocused = LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, rcItem); else - LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align); + LISTVIEW_DrawSubItem(infoPtr, hdc, i.nItem, j, rcItem, lpCols[j].align); if (cditemmode & CDRF_NOTIFYPOSTPAINT) - notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT); + notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT); } /* Adjust focus if we have it, and we are in full select */ if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect; } + iterator_destroy(&i); /* cleanup the mess */ set_text_attr(hdc, &oldTa); @@ -3515,48 +3563,37 @@ */ static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode) { - RECT rcItem; - INT i, j; - INT nItem; - INT nColumnCount; - INT nCountPerColumn; - INT nItemWidth = infoPtr->nItemWidth; - INT nItemHeight = infoPtr->nItemHeight; - INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; - DWORD cditemmode = CDRF_DODEFAULT; - - /* get number of fully visible columns */ - nColumnCount = nListWidth / nItemWidth; - if (nListWidth % nItemWidth) nColumnCount++; - nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); - nItem = ListView_GetTopIndex(infoPtr->hwndSelf); - TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n", - nColumnCount, nCountPerColumn, nItem); + DWORD cditemmode = CDRF_DODEFAULT; + POINT Origin, Position; + RECT rcItem; + ITERATOR i; - for (i = 0; i < nColumnCount; i++) - { - for (j = 0; j < nCountPerColumn; j++, nItem++) + /* Get scroll info once before loop */ + if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return; + + /* figure out what we need to draw */ + iterator_create_clippeditems(&i, infoPtr, hdc); + + while(iterator_next(&i)) { - if (nItem >= infoPtr->nItemCount) - return; - - if (cdmode & CDRF_NOTIFYITEMDRAW) - cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT); - if (cditemmode & CDRF_SKIPDEFAULT) - continue; + if (cdmode & CDRF_NOTIFYITEMDRAW) + cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, 0, CDDS_ITEMPREPAINT); + if (cditemmode & CDRF_SKIPDEFAULT) continue; - rcItem.top = j * nItemHeight; - rcItem.left = i * nItemWidth; - rcItem.bottom = rcItem.top + nItemHeight; - rcItem.right = rcItem.left + nItemWidth; + if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue; + rcItem.left = Position.x; + rcItem.top = Position.y; + rcItem.bottom = rcItem.top + infoPtr->nItemHeight; + rcItem.right = rcItem.left + infoPtr->nItemWidth; + OffsetRect(&rcItem, Origin.x, Origin.y); - LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem); + LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, rcItem); - if (cditemmode & CDRF_NOTIFYPOSTPAINT) - notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT); + if (cditemmode & CDRF_NOTIFYPOSTPAINT) + notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT); } - } + iterator_destroy(&i); } /*** @@ -3571,102 +3608,65 @@ * RETURN: * None */ -static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode) +static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode) { - POINT ptPosition; - RECT rcItem, rcClip, rcTemp; - INT i; - DWORD cditemmode = CDRF_DODEFAULT; - - TRACE("\n"); + DWORD cditemmode = CDRF_DODEFAULT; + POINT Origin, Position; + RECT rcItem, rcClip, rcTemp; + BOOL bDrawFocusedItem = FALSE; + ITERATOR i; - GetClipBox(hdc, &rcClip); + GetClipBox(hdc, &rcClip); /* FIXME: get rid of this */ + + /* Get scroll info once before loop */ + if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return; + + /* figure out what we need to draw */ + iterator_create_clippeditems(&i, infoPtr, hdc); + + while(iterator_next(&i)) + { + if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue; + rcItem.left = Position.x; + rcItem.top = Position.y; + rcItem.bottom = rcItem.top + infoPtr->nItemHeight; + rcItem.right = rcItem.left + infoPtr->nItemWidth; + OffsetRect(&rcItem, Origin.x, Origin.y); - /* Draw the visible non-selected items */ - for (i = 0; i < infoPtr->nItemCount; i++) - { - if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED)) - continue; + if (!IntersectRect(&rcTemp, &rcItem, &rcClip)) continue; - rcItem.left = LVIR_BOUNDS; - LISTVIEW_GetItemRect(infoPtr, i, &rcItem); - if (!IntersectRect(&rcTemp, &rcItem, &rcClip)) - continue; + /* FIXME: move this before the rcItem computation, when we no longer need rcClip */ + if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED)) + { + bDrawFocusedItem = TRUE; + continue; + } - if (cdmode & CDRF_NOTIFYITEMDRAW) - cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT); - if (cditemmode & CDRF_SKIPDEFAULT) - continue; + if (cdmode & CDRF_NOTIFYITEMDRAW) + cditemmode = notify_customdrawitem (infoPtr, hdc, i.nItem, 0, CDDS_ITEMPREPAINT); + if (cditemmode & CDRF_SKIPDEFAULT) continue; - LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition); + LISTVIEW_DrawLargeItem(infoPtr, hdc, i.nItem, rcItem); - if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top) - { - if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left) - { - if (ptPosition.y < infoPtr->rcList.bottom) - { - if (ptPosition.x < infoPtr->rcList.right) - { - rcItem.top = ptPosition.y; - rcItem.left = ptPosition.x; - rcItem.bottom = rcItem.top + infoPtr->nItemHeight; - rcItem.right = rcItem.left + infoPtr->nItemWidth; - - if (bSmall) - LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem); - else - LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem); - } - } - } + if (cditemmode & CDRF_NOTIFYPOSTPAINT) + notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT); } - if (cditemmode & CDRF_NOTIFYPOSTPAINT) - notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT); - } + iterator_destroy(&i); - /* Draw the visible selected items */ - for (i = 0; i < infoPtr->nItemCount; i++) - { - if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED)) - continue; - - rcItem.left = LVIR_BOUNDS; - LISTVIEW_GetItemRect(infoPtr, i, &rcItem); - if (!IntersectRect(&rcTemp, &rcItem, &rcClip)) - continue; - - if (cdmode & CDRF_NOTIFYITEMDRAW) - cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT); - if (cditemmode & CDRF_SKIPDEFAULT) - continue; - - LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition); - - if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top) + /* use the 'while' trick so we can break out of the loop */ + while (bDrawFocusedItem) { - if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left) - { - if (ptPosition.y < infoPtr->rcList.bottom) - { - if (ptPosition.x < infoPtr->rcList.right) - { - rcItem.top = ptPosition.y; - rcItem.left = ptPosition.x; - rcItem.bottom = rcItem.top + infoPtr->nItemHeight; - rcItem.right = rcItem.left + infoPtr->nItemWidth; - - if (bSmall) - LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem); - else - LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem); - } - } - } + if (!LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcItem, 0, 0, 0)) break; + if (cdmode & CDRF_NOTIFYITEMDRAW) + cditemmode = notify_customdrawitem (infoPtr, hdc, infoPtr->nFocusedItem, 0, CDDS_ITEMPREPAINT); + if (cditemmode & CDRF_SKIPDEFAULT) break; + + LISTVIEW_DrawLargeItem(infoPtr, hdc, infoPtr->nFocusedItem, rcItem); + + if (cditemmode & CDRF_NOTIFYPOSTPAINT) + notify_customdrawitem(infoPtr, hdc, i.nItem, 0, CDDS_ITEMPOSTPAINT); + bDrawFocusedItem = FALSE; } - if (cditemmode & CDRF_NOTIFYPOSTPAINT) - notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT); - } } /*** @@ -3704,12 +3704,12 @@ if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) LISTVIEW_RefreshOwnerDraw(infoPtr, hdc); - else if (uView == LVS_LIST) - LISTVIEW_RefreshList(infoPtr, hdc, cdmode); + else if (uView == LVS_ICON) + LISTVIEW_RefreshIcon(infoPtr, hdc, cdmode); else if (uView == LVS_REPORT) LISTVIEW_RefreshReport(infoPtr, hdc, cdmode); - else - LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode); + else /* LVS_LIST or LVS_SMALLICON */ + LISTVIEW_RefreshList(infoPtr, hdc, cdmode); /* if we have a focus rect, draw it */ if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED)) @@ -5338,7 +5338,7 @@ * [I] uFlags : relationship flag * * FIXME: - * This function is ver, very inefficient! Needs work. + * This function is very, very inefficient! Needs work. * * RETURN: * SUCCESS : item index @@ -5608,6 +5608,7 @@ bottomindex = infoPtr->nItemCount; } + /* FIXME iter */ for (i = topindex; i < bottomindex; i++) { rcItem.left = LVIR_BOUNDS; @@ -6982,6 +6983,7 @@ * be after the sort (otherwise, the list items move around, but * whatever is at the item's previous original position will be * selected instead) + * FIXME: can't this be made more efficient? */ selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL; for (i=0; i < infoPtr->nItemCount; i++)