Reimplement FindItem (cleaner, faster, more compliant with MSDN).

This commit is contained in:
Dimitrie O. Paun 2002-10-10 03:10:20 +00:00 committed by Alexandre Julliard
parent 1313431b1b
commit 0295e4fceb

View File

@ -267,7 +267,6 @@ DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
* forward declarations
*/
static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *, INT, LPRECT, LPRECT, LPRECT, LPRECT);
static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
@ -4359,45 +4358,6 @@ static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPart
return TRUE;
}
/***
* DESCRIPTION:
* Retrieves the nearest item, given a position and a direction.
*
* PARAMETER(S):
* [I] infoPtr : valid pointer to the listview structure
* [I] POINT : start position
* [I] UINT : direction
*
* RETURN:
* Item index if successdful, -1 otherwise.
*/
static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
{
LVHITTESTINFO ht;
RECT rcView;
TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
(vkDirection == VK_DOWN) ? "VK_DOWN" :
((vkDirection == VK_UP) ? "VK_UP" :
((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
ht.pt.x += pt.x;
ht.pt.y += pt.y;
if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
if (!PtInRect(&rcView, ht.pt)) return -1;
return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
}
/***
* DESCRIPTION:
* Searches for an item with specific characteristics.
@ -4414,101 +4374,109 @@ static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDire
static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
LPLVFINDINFOW lpFindInfo)
{
POINT ptItem;
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
LVITEMW lvItem;
BOOL bWrap = FALSE;
INT nItem = nStart;
INT nLast = infoPtr->nItemCount;
UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
BOOL bWrap = FALSE, bNearest = FALSE;
INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
ULONG xdist, ydist, dist, mindist = 0x7fffffff;
POINT Position, Destination;
LVITEMW lvItem;
if ((nItem >= -1) && (lpFindInfo != NULL))
{
if (!lpFindInfo || nItem < 0) return -1;
lvItem.mask = 0;
if (lpFindInfo->flags & LVFI_PARAM)
{
lvItem.mask |= LVIF_PARAM;
}
if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
{
lvItem.mask |= LVIF_TEXT;
lvItem.pszText = szDispText;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.mask |= LVIF_TEXT;
lvItem.pszText = szDispText;
lvItem.cchTextMax = DISP_TEXT_SIZE;
}
if (lpFindInfo->flags & LVFI_WRAP)
bWrap = TRUE;
bWrap = TRUE;
if (lpFindInfo->flags & LVFI_NEARESTXY)
if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
(uView == LVS_ICON || uView ==LVS_SMALLICON))
{
ptItem.x = lpFindInfo->pt.x;
ptItem.y = lpFindInfo->pt.y;
POINT Origin;
FIXME("LVFI_NEARESTXY is slow.\n");
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
Destination.x = lpFindInfo->pt.x + Origin.x;
Destination.y = lpFindInfo->pt.y + Origin.y;
switch(lpFindInfo->vkDirection)
{
case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
case VK_HOME: Destination.x = Destination.y = 0; break;
case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
}
bNearest = TRUE;
}
while (1)
/* if LVFI_PARAM is specified, all other flags are ignored */
if (lpFindInfo->flags & LVFI_PARAM)
{
while (nItem < nLast)
{
if (lpFindInfo->flags & LVFI_NEARESTXY)
{
nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
lpFindInfo->vkDirection);
if (nItem != -1)
{
/* get position of the new item index */
if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
return -1;
}
else
return -1;
}
else
{
nItem++;
}
lvItem.mask |= LVIF_PARAM;
bNearest = FALSE;
lvItem.mask &= ~LVIF_TEXT;
}
again:
for (; nItem < nLast; nItem++)
{
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
if (LISTVIEW_GetItemW(infoPtr, &lvItem))
{
if (lvItem.mask & LVIF_TEXT)
{
if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
return nItem;
if (lvItem.mask & LVIF_TEXT)
{
if (lpFindInfo->flags & LVFI_PARTIAL)
{
if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
continue;
if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
}
else
{
if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
continue;
if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
}
}
}
if (lvItem.mask & LVIF_PARAM)
{
if (lpFindInfo->lParam != lvItem.lParam)
continue;
}
if (!bNearest) return nItem;
/* This is very inefficient. To do a good job here,
* we need a sorted array of (x,y) item positions */
if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
return nItem;
}
}
/* compute the distance^2 to the destination */
xdist = Destination.x - Position.x;
ydist = Destination.y - Position.y;
dist = xdist * xdist + ydist * ydist;
if (bWrap)
{
nItem = -1;
nLast = nStart + 1;
bWrap = FALSE;
}
else
{
return -1;
}
/* remember the distance, and item if it's closer */
if (dist < mindist)
{
mindist = dist;
nNearestItem = nItem;
}
}
}
return -1;
if (bWrap)
{
nItem = 0;
nLast = min(nStart + 1, infoPtr->nItemCount);
bWrap = FALSE;
goto again;
}
return nNearestItem;
}
/***
@ -5568,124 +5536,6 @@ static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText
return stringSize.cx;
}
/***
* DESCRIPTION:
* Determines item if a hit or closest if not
*
* PARAMETER(S):
* [I] infoPtr : valid pointer to the listview structure
* [IO] lpht : hit test information
* [I] subitem : fill out iSubItem.
* [I] bNearItem : return the nearest item
*
* RETURN:
* SUCCESS : item index of hit
* FAILURE : -1
*/
static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
{
LONG lStyle = infoPtr->dwStyle;
UINT uView = lStyle & LVS_TYPEMASK;
INT i,j,topindex,bottomindex,nearestItem;
RECT rcItem,rcSubItem;
DWORD xterm, yterm, dist, mindist;
TRACE("(lpht->pt=%s, subitem=%d\n", debugpoint(&lpht->pt), subitem);
nearestItem = -1;
mindist = -1;
/* FIXME: get the visible range */
topindex = LISTVIEW_GetTopIndex(infoPtr);
if (uView == LVS_REPORT)
{
bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
bottomindex = min(bottomindex,infoPtr->nItemCount);
}
else
{
bottomindex = infoPtr->nItemCount;
}
/* FIXME iter */
for (i = topindex; i < bottomindex; i++)
{
rcItem.left = LVIR_BOUNDS;
if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
{
if (PtInRect(&rcItem, lpht->pt))
{
rcSubItem = rcItem;
rcItem.left = LVIR_ICON;
if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
{
if (PtInRect(&rcItem, lpht->pt))
{
lpht->flags = LVHT_ONITEMICON;
lpht->iItem = i;
goto set_subitem;
}
}
rcItem.left = LVIR_LABEL;
if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
{
if (PtInRect(&rcItem, lpht->pt))
{
lpht->flags = LVHT_ONITEMLABEL;
lpht->iItem = i;
goto set_subitem;
}
}
lpht->flags = LVHT_ONITEMSTATEICON;
lpht->iItem = i;
set_subitem:
if (subitem)
{
INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
lpht->iSubItem = 0;
rcSubItem.right = rcSubItem.left;
for (j = 0; j < nColumnCount; j++)
{
rcSubItem.left = rcSubItem.right;
rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
if (PtInRect(&rcSubItem, lpht->pt))
{
lpht->iSubItem = j;
break;
}
}
}
TRACE("hit on item %d\n", i);
return i;
}
else if (bNearItem)
{
/*
* Now compute distance from point to center of boundary
* box. Since we are only interested in the relative
* distance, we can skip the nasty square root operation
*/
xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
dist = xterm * xterm + yterm * yterm;
if (mindist < 0 || dist < mindist)
{
mindist = dist;
nearestItem = i;
}
}
}
}
lpht->flags = LVHT_NOWHERE;
return bNearItem ? nearestItem : -1;
}
/***
* DESCRIPTION:
* Determines which listview item is located at the specified position.