This one fixes selection problem in OWNERDATA, OWNERDRAW listviews. Which means (among other things) Xnews now works better. Unfortunately, focus handling for OWNERDRAW is still broken. Next thing to look at. Alos, two critical performance problems were fixed: Xnews took >5 secs to do trivial things, like move focus, or select. ChangeLog Fix bug in OWNERDATA selection handling Fix critical performance bug in GetSelectedCount Fix critical performance bug in SetGroupSelection Fix problems for OWNERDRAW report Fix hidden/latent bugs in state handling Better debug messages. --- dlls/comctl32/listview.c.K9 Fri Oct 4 18:56:54 2002 +++ dlls/comctl32/listview.c Sat Oct 5 03:05:31 2002 @@ -1810,16 +1810,14 @@ #if 0 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr) { - RANGE *selection; - INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount; - INT i; + INT i; - TRACE("Selections are:\n"); - for (i = 0; i < topSelection; i++) - { - selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i); - TRACE(" %lu - %lu\n",selection->lower,selection->upper); - } + ERR("Selections are:\n"); + for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++) + { + RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i); + ERR(" [%d - %d]\n", selection->lower, selection->upper); + } } #endif @@ -2092,6 +2090,42 @@ /*** * DESCRIPTION: + * Retrieves the number of items that are marked as selected. + * + * PARAMETER(S): + * [I] infoPtr : valid pointer to the listview structure + * + * RETURN: + * Number of items selected. + */ +static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr) +{ + INT i, nSelectedCount = 0; + + if (infoPtr->uCallbackMask & LVIS_SELECTED) + { + INT i; + for (i = 0; i < GETITEMCOUNT(infoPtr); i++) + { + if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) + nSelectedCount++; + } + } + else + { + for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++) + { + RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i); + nSelectedCount += sel->upper - sel->lower + 1; + } + } + + TRACE("nSelectedCount=%d\n", nSelectedCount); + return nSelectedCount; +} + +/*** + * DESCRIPTION: * Manages the item focus. * * PARAMETER(S): @@ -2231,13 +2265,20 @@ static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) { UINT uView = LISTVIEW_GetType(infoPtr); - INT i, nFirst, nLast; + INT i; LVITEMW item; POINT ptItem; RECT rcSel; + LISTVIEW_RemoveAllSelections(infoPtr); + + item.state = LVIS_SELECTED; + item.stateMask = LVIS_SELECTED; + if ((uView == LVS_LIST) || (uView == LVS_REPORT)) { + INT nFirst, nLast; + if (infoPtr->nSelectionMark == -1) infoPtr->nSelectionMark = nFirst = nLast = nItem; else @@ -2245,6 +2286,8 @@ nFirst = min(infoPtr->nSelectionMark, nItem); nLast = max(infoPtr->nSelectionMark, nItem); } + for (i = nFirst; i < nLast; i++) + LISTVIEW_SetItemState(infoPtr, i, &item); } else { @@ -2255,22 +2298,14 @@ rcSelMark.left = LVIR_BOUNDS; if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return; UnionRect(&rcSel, &rcItem, &rcSelMark); - nFirst = nLast = -1; - } - - item.stateMask = LVIS_SELECTED; - - for (i = 0; i <= GETITEMCOUNT(infoPtr); i++) - { - if (nFirst > -1) - item.state = (i < nFirst) || (i > nLast) ? 0 : LVIS_SELECTED; - else + for (i = 0; i <= GETITEMCOUNT(infoPtr); i++) { LISTVIEW_GetItemPosition(infoPtr, i, &ptItem); - item.state = PtInRect(&rcSel, ptItem) ? LVIS_SELECTED : 0; + if (PtInRect(&rcSel, ptItem)) + LISTVIEW_SetItemState(infoPtr, i, &item); } - LISTVIEW_SetItemState(infoPtr, i, &item); } + LISTVIEW_SetItemFocus(infoPtr, nItem); } @@ -3314,20 +3349,23 @@ if (!LISTVIEW_GetItemW(infoPtr, &item)) continue; ZeroMemory(&dis, sizeof(dis)); - dis.hwndItem = infoPtr->hwndSelf; - dis.hDC = hdc; dis.CtlType = ODT_LISTVIEW; dis.CtlID = uID; dis.itemID = nItem; dis.itemAction = ODA_DRAWENTIRE; - dis.itemData = item.lParam; + if (item.state & LVIS_SELECTED) dis.itemAction |= ODA_SELECT; + if (item.state & LVIS_FOCUSED) dis.itemAction |= ODA_FOCUS; + /*dis.itemState = ODS_DEFAULT; */ if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED; if (item.state & LVIS_FOCUSED) dis.itemState |= ODS_FOCUS; + dis.hwndItem = infoPtr->hwndSelf; + dis.hDC = hdc; dis.rcItem.left = lpCols[0].rc.left; dis.rcItem.right = lpCols[nColumnCount - 1].rc.right; dis.rcItem.top = nDrawPosY; dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight; OffsetRect(&dis.rcItem, ptOrig.x, 0); + dis.itemData = item.lParam; TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem)); SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); @@ -4746,6 +4784,7 @@ if (lpLVItem->mask & LVIF_STATE) dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask; notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); + dispInfo.item.stateMask = lpLVItem->stateMask; *lpLVItem = dispInfo.item; TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW)); } @@ -4754,7 +4793,7 @@ if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE; /* if focus is handled by us, report it */ - if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) + if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) { lpLVItem->state &= ~LVIS_FOCUSED; if (infoPtr->nFocusedItem == lpLVItem->iItem) @@ -4762,11 +4801,10 @@ } /* and do the same for selection, if we handle it */ - if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) ) + if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) { lpLVItem->state &= ~LVIS_SELECTED; - if ((lpLVItem->stateMask & LVIS_SELECTED) && - is_item_selected(infoPtr, lpLVItem->iItem)) + if (is_item_selected(infoPtr, lpLVItem->iItem)) lpLVItem->state |= LVIS_SELECTED; } @@ -4866,17 +4904,16 @@ lpLVItem->state &= ~dispInfo.item.stateMask; lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask); } - if ( !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) + if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) { lpLVItem->state &= ~LVIS_FOCUSED; if (infoPtr->nFocusedItem == lpLVItem->iItem) lpLVItem->state |= LVIS_FOCUSED; } - if ( !(infoPtr->uCallbackMask & LVIS_SELECTED) ) + if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED ) { lpLVItem->state &= ~LVIS_SELECTED; - if ((lpLVItem->stateMask & LVIS_SELECTED) && - is_item_selected(infoPtr, lpLVItem->iItem)) + if (is_item_selected(infoPtr, lpLVItem->iItem)) lpLVItem->state |= LVIS_SELECTED; } } @@ -5519,31 +5556,6 @@ /*** * DESCRIPTION: - * Retrieves the number of items that are marked as selected. - * - * PARAMETER(S): - * [I] infoPtr : valid pointer to the listview structure - * - * RETURN: - * Number of items selected. - */ -static LRESULT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr) -{ -/* REDO THIS */ - INT nSelectedCount = 0; - INT i; - - for (i = 0; i < GETITEMCOUNT(infoPtr); i++) - { - if (ListView_GetItemState(infoPtr->hwndSelf, i, LVIS_SELECTED) & LVIS_SELECTED) - nSelectedCount++; - } - - return nSelectedCount; -} - -/*** - * DESCRIPTION: * Retrieves the width of a string. * * PARAMETER(S): @@ -7688,11 +7700,12 @@ infoPtr->bLButtonDown = TRUE; nItem = LISTVIEW_GetItemAtPt(infoPtr, pt); + TRACE("nItem=%d\n", nItem); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { if (lStyle & LVS_SINGLESEL) { - if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED) + if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) && infoPtr->nEditLabelItem == -1) infoPtr->nEditLabelItem = nItem; else @@ -7708,12 +7721,10 @@ { LVITEMW item; - item.state = LVIS_SELECTED; - item.stateMask = LVIS_SELECTED; + item.state = LVIS_SELECTED | LVIS_FOCUSED; + item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; LISTVIEW_SetItemState(infoPtr,nItem,&item); - - LISTVIEW_SetItemFocus(infoPtr, nItem); infoPtr->nSelectionMark = nItem; } } @@ -7723,11 +7734,9 @@ bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0); - item.state = bGroupSelect ? LVIS_SELECTED : 0; - item.stateMask = LVIS_SELECTED; + item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED; + item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; LISTVIEW_SetItemState(infoPtr, nItem, &item); - - LISTVIEW_SetItemFocus(infoPtr, nItem); infoPtr->nSelectionMark = nItem; } else if (wKey & MK_SHIFT) @@ -7736,8 +7745,7 @@ } else { - BOOL was_selected = - (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) & LVIS_SELECTED); + BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); /* set selection (clears other pre-existing selections) */ LISTVIEW_SetSelection(infoPtr, nItem);