The 'we like speed too' patch. This is getting exciting :) ChangeLog Draw _only_ the items that are invalidated Fix serious bug when deleting from a range Pass in a range to the ranges_{add,del} functions Tidy up the RefreshIcon function Assorted cleanups. --- dlls/comctl32/listview.c.M12 Wed Oct 9 11:05:12 2002 +++ dlls/comctl32/listview.c Wed Oct 9 13:53:06 2002 @@ -272,6 +272,7 @@ static void LISTVIEW_AlignTop(LISTVIEW_INFO *); static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT); static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *); +static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *, INT, LPPOINT); static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT); static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT); static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *); @@ -718,7 +719,9 @@ /******** Item iterator functions **********************************/ -static BOOL iterator_create_frameditems(ITERATOR*, LISTVIEW_INFO*, const RECT*, HRGN); +static BOOL ranges_add(HDPA ranges, RANGE range); +static BOOL ranges_del(HDPA ranges, RANGE range); +static void ranges_dump(HDPA ranges); static inline BOOL iterator_next(ITERATOR* i) { @@ -757,51 +760,25 @@ if (i->ranges) DPA_Destroy(i->ranges); } -static inline BOOL iterator_create_empty(ITERATOR* i) +static inline BOOL iterator_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) +static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc) { 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 (!iterator_empty(i)) return FALSE; if (uView == LVS_ICON || uView == LVS_SMALLICON) { i->range.lower = 0; - i->range.upper = infoPtr->nItemCount; + i->range.upper = infoPtr->nItemCount - 1; return TRUE; } @@ -813,9 +790,54 @@ 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); + i->range.upper = min(i->range.lower + nPerCol * nPerRow, infoPtr->nItemCount - 1); return TRUE; } + +static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc) +{ + POINT Origin, Position; + RECT rcItem, rcClip; + INT nItem, rgntype; + + rgntype = GetClipBox(hdc, &rcClip); + if (rgntype == NULLREGION) return iterator_empty(i); + if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE; + if (rgntype == SIMPLEREGION) return TRUE; + + /* if we can't deal with the region, we'll just go with the simple range */ + if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE; + if (!(i->ranges = DPA_Create(10))) return TRUE; + if (!ranges_add(i->ranges, i->range)) + { + DPA_Destroy(i->ranges); + i->ranges = 0; + return TRUE; + } + + /* no delete the invisible items from the list */ + for (nItem = i->range.lower; nItem <= i->range.upper; nItem++) + { + if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue; + rcItem.left = Position.x + Origin.x; + rcItem.top = Position.y + Origin.y; + rcItem.right = rcItem.left + infoPtr->nItemWidth; + rcItem.bottom = rcItem.top + infoPtr->nItemHeight; + if (!RectVisible(hdc, &rcItem)) + { + RANGE item_range = { nItem, nItem }; + ranges_del(i->ranges, item_range); + } + } + + return TRUE; +} + +static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr) +{ + return iterator_frameditems(i, infoPtr, &infoPtr->rcList); +} + /******** Misc helper functions ************************************/ static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg, @@ -1263,7 +1285,7 @@ { ITERATOR i; - iterator_create_visibleitems(&i, infoPtr); + iterator_visibleitems(&i, infoPtr); while(iterator_next(&i)) { if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)) @@ -1885,19 +1907,9 @@ } #if 0 -static void ranges_dump(HDPA ranges) -{ - INT i; - - ERR("Selections are:\n"); - for (i = 0; i < ranges->nItemCount; i++) - { - RANGE *selection = DPA_GetPtr(ranges, i); - ERR(" [%d - %d]\n", selection->lower, selection->upper); - } -} static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr) { + ERR("Selections are:\n"); ranges_dump(infoPtr->hdpaSelectionRanges); } #endif @@ -1925,6 +1937,17 @@ return 0; } +static void ranges_dump(HDPA ranges) +{ + INT i; + + for (i = 0; i < ranges->nItemCount; i++) + { + RANGE *selection = DPA_GetPtr(ranges, i); + ERR(" [%d - %d]\n", selection->lower, selection->upper); + } +} + static inline BOOL ranges_contain(HDPA ranges, INT nItem) { RANGE srchrng = { nItem, nItem }; @@ -1954,16 +1977,17 @@ return TRUE; } -static BOOL ranges_add(HDPA ranges, INT lower, INT upper) +static BOOL ranges_add(HDPA ranges, RANGE range) { RANGE srchrgn; INT index; - TRACE("range (%i - %i)\n", lower, upper); + TRACE("range=(%i - %i)\n", range.lower, range.upper); + if (TRACE_ON(listview)) ranges_dump(ranges); /* try find overlapping regions first */ - srchrgn.lower = lower - 1; - srchrgn.upper = upper + 1; + srchrgn.lower = range.lower - 1; + srchrgn.upper = range.upper + 1; index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0); if (index == -1) @@ -1973,8 +1997,7 @@ /* create the brand new range to insert */ newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE)); if(!newrgn) return FALSE; - newrgn->lower = lower; - newrgn->upper = upper; + *newrgn = range; /* figure out where to insert it */ index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER); @@ -1993,8 +2016,8 @@ TRACE("Merge with index %i (%d - %d)\n", index, chkrgn->lower, chkrgn->upper); - chkrgn->lower = min(lower, chkrgn->lower); - chkrgn->upper = max(upper, chkrgn->upper); + chkrgn->lower = min(range.lower, chkrgn->lower); + chkrgn->upper = max(range.upper, chkrgn->upper); TRACE("New range %i (%d - %d)\n", index, chkrgn->lower, chkrgn->upper); @@ -2030,17 +2053,15 @@ return TRUE; } -static BOOL ranges_del(HDPA ranges, INT lower, INT upper) +static BOOL ranges_del(HDPA ranges, RANGE range) { RANGE remrgn, tmprgn, *chkrgn; BOOL done = FALSE; INT index; - remrgn.lower = lower; - remrgn.upper = upper; - - TRACE("range: (%d - %d)\n", remrgn.lower, remrgn.upper); - + TRACE("range: (%d - %d)\n", range.lower, range.upper); + + remrgn = range; do { index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0); @@ -2066,14 +2087,14 @@ DPA_DeletePtr(ranges, index); } /* case 3: overlap upper */ - else if ( (chkrgn->upper < remrgn.upper) && + else if ( (chkrgn->upper <= remrgn.upper) && (chkrgn->lower < remrgn.lower) ) { chkrgn->upper = remrgn.lower - 1; } /* case 4: overlap lower */ else if ( (chkrgn->upper > remrgn.upper) && - (chkrgn->lower > remrgn.lower) ) + (chkrgn->lower >= remrgn.lower) ) { chkrgn->lower = remrgn.upper + 1; } @@ -2100,10 +2121,11 @@ */ static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only) { + RANGE range = { lower, upper }; LVITEMW lvItem; INT i; - if (!ranges_del(infoPtr->hdpaSelectionRanges, lower, upper)) return FALSE; + if (!ranges_del(infoPtr->hdpaSelectionRanges, range)) return FALSE; if (adj_sel_only) return TRUE; /* reset the selection on items */ @@ -2119,10 +2141,11 @@ */ static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only) { + RANGE range = { lower, upper }; LVITEMW lvItem; INT i; - if (!ranges_add(infoPtr->hdpaSelectionRanges, lower, upper)) return FALSE; + if (!ranges_add(infoPtr->hdpaSelectionRanges, range)) return FALSE; if (adj_sel_only) return TRUE; /* set the selection on items */ @@ -2394,7 +2417,7 @@ rcSelMark.left = LVIR_BOUNDS; if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return; UnionRect(&rcSel, &rcItem, &rcSelMark); - iterator_create_frameditems(&i, infoPtr, &rcSel, 0); + iterator_frameditems(&i, infoPtr, &rcSel); while(iterator_next(&i)) { LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem); @@ -2503,7 +2526,7 @@ ITERATOR i; RECT rcItem; - iterator_create_visibleitems(&i, infoPtr); + iterator_visibleitems(&i, infoPtr); while(iterator_next(&i)) { rcItem.left = LVIR_SELECTBOUNDS; @@ -3366,7 +3389,7 @@ if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return; /* figure out what we need to draw */ - iterator_create_clippeditems(&i, infoPtr, hdc); + iterator_clippeditems(&i, infoPtr, hdc); /* send cache hint notification */ if (infoPtr->dwStyle & LVS_OWNERDATA) @@ -3474,7 +3497,7 @@ oldTa.fgColor = GetTextColor(hdc); /* figure out what we need to draw */ - iterator_create_clippeditems(&i, infoPtr, hdc); + iterator_clippeditems(&i, infoPtr, hdc); /* a last few bits before we start drawing */ bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT; @@ -3571,7 +3594,7 @@ if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return; /* figure out what we need to draw */ - iterator_create_clippeditems(&i, infoPtr, hdc); + iterator_clippeditems(&i, infoPtr, hdc); while(iterator_next(&i)) { @@ -3611,20 +3634,20 @@ { DWORD cditemmode = CDRF_DODEFAULT; POINT Origin, Position; - RECT rcItem, rcClip, rcTemp; - BOOL bDrawFocusedItem = FALSE; + RECT rcItem; ITERATOR i; - 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); + iterator_clippeditems(&i, infoPtr, hdc); while(iterator_next(&i)) { + if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED)) + continue; + if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue; rcItem.left = Position.x; rcItem.top = Position.y; @@ -3632,15 +3655,6 @@ rcItem.right = rcItem.left + infoPtr->nItemWidth; OffsetRect(&rcItem, Origin.x, Origin.y); - 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.nItem, 0, CDDS_ITEMPREPAINT); if (cditemmode & CDRF_SKIPDEFAULT) continue; @@ -3652,20 +3666,19 @@ } iterator_destroy(&i); - /* use the 'while' trick so we can break out of the loop */ - while (bDrawFocusedItem) - { - 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); + /* draw the focused item last, in case it's oversized */ + if (!LISTVIEW_GetItemMeasures(infoPtr, infoPtr->nFocusedItem, &rcItem, 0, 0, 0)) return; + if (!RectVisible(hdc, &rcItem)) return; + + if (cdmode & CDRF_NOTIFYITEMDRAW) + cditemmode = notify_customdrawitem (infoPtr, hdc, infoPtr->nFocusedItem, 0, CDDS_ITEMPREPAINT); + if (cditemmode & CDRF_SKIPDEFAULT) + return; + + 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.nItem, 0, CDDS_ITEMPOSTPAINT); } /*** @@ -5597,7 +5610,7 @@ rcSearch.right = lpht->pt.x + 1; rcSearch.bottom = lpht->pt.y + 1; - iterator_create_frameditems(&i, infoPtr, &rcSearch, 0); + iterator_frameditems(&i, infoPtr, &rcSearch); while(iterator_next(&i)) { if (!LISTVIEW_GetItemMeasures(infoPtr, i.nItem, &rcBounds, 0, 0, 0)) continue;