📄 listview.c
字号:
}
static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
{
NMLISTVIEW nmlv;
LVITEMW item;
HWND hwnd = infoPtr->hwndSelf;
TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
ZeroMemory(&nmlv, sizeof(nmlv));
nmlv.iItem = lvht->iItem;
nmlv.iSubItem = lvht->iSubItem;
nmlv.ptAction = lvht->pt;
item.mask = LVIF_PARAM;
item.iItem = lvht->iItem;
item.iSubItem = 0;
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
notify_listview(infoPtr, code, &nmlv);
return IsWindow(hwnd);
}
static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
{
NMLISTVIEW nmlv;
LVITEMW item;
HWND hwnd = infoPtr->hwndSelf;
ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
nmlv.iItem = nItem;
item.mask = LVIF_PARAM;
item.iItem = nItem;
item.iSubItem = 0;
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
return IsWindow(hwnd);
}
static int get_ansi_notification(INT unicodeNotificationCode)
{
switch (unicodeNotificationCode)
{
case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
}
ERR("unknown notification %x\n", unicodeNotificationCode);
assert(FALSE);
return 0;
}
/*
Send notification. depends on dispinfoW having same
structure as dispinfoA.
infoPtr : listview struct
notificationCode : *Unicode* notification code
pdi : dispinfo structure (can be unicode or ansi)
isW : TRUE if dispinfo is Unicode
*/
static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
{
BOOL bResult = FALSE;
BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
LPWSTR pszTempBuf = NULL, savPszText = NULL;
if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
{
convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
}
if (convertToAnsi || convertToUnicode)
{
if (notificationCode != LVN_GETDISPINFOW)
{
cchTempBufMax = convertToUnicode ?
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
}
else
{
cchTempBufMax = pdi->item.cchTextMax;
*pdi->item.pszText = 0; /* make sure we don't process garbage */
}
pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
if (!pszTempBuf) return FALSE;
if (convertToUnicode)
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
pszTempBuf, cchTempBufMax);
else
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
cchTempBufMax, NULL, NULL);
savCchTextMax = pdi->item.cchTextMax;
savPszText = pdi->item.pszText;
pdi->item.pszText = pszTempBuf;
pdi->item.cchTextMax = cchTempBufMax;
}
if (infoPtr->notifyFormat == NFR_ANSI)
realNotifCode = get_ansi_notification(notificationCode);
else
realNotifCode = notificationCode;
TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
if (convertToUnicode || convertToAnsi)
{
if (convertToUnicode) /* note : pointer can be changed by app ! */
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
savCchTextMax, NULL, NULL);
else
MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
savPszText, savCchTextMax);
pdi->item.pszText = savPszText; /* restores our buffer */
pdi->item.cchTextMax = savCchTextMax;
Free (pszTempBuf);
}
return bResult;
}
static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
const RECT *rcBounds, const LVITEMW *lplvItem)
{
ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
lpnmlvcd->nmcd.hdc = hdc;
lpnmlvcd->nmcd.rc = *rcBounds;
lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
lpnmlvcd->clrText = infoPtr->clrText;
if (!lplvItem) return;
lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
lpnmlvcd->iSubItem = lplvItem->iSubItem;
if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
}
static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
{
BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
DWORD result;
lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
return result;
}
static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
{
/* apprently, for selected items, we have to override the returned values */
if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
{
if (infoPtr->bFocus)
{
lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
lpnmlvcd->clrText = comctl32_color.clrHighlightText;
}
else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
{
lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
lpnmlvcd->clrText = comctl32_color.clrBtnText;
}
}
/* Set the text attributes */
if (lpnmlvcd->clrTextBk != CLR_NONE)
{
SetBkMode(hdc, OPAQUE);
if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
SetBkColor(hdc, infoPtr->clrTextBkDefault);
else
SetBkColor(hdc,lpnmlvcd->clrTextBk);
}
else
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, lpnmlvcd->clrText);
}
static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
{
return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
}
/******** Item iterator functions **********************************/
static RANGES ranges_create(int count);
static void ranges_destroy(RANGES ranges);
static BOOL ranges_add(RANGES ranges, RANGE range);
static BOOL ranges_del(RANGES ranges, RANGE range);
static void ranges_dump(RANGES ranges);
static inline BOOL ranges_additem(RANGES ranges, INT nItem)
{
RANGE range = { nItem, nItem + 1 };
return ranges_add(ranges, range);
}
static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
{
RANGE range = { nItem, nItem + 1 };
return ranges_del(ranges, range);
}
/***
* ITERATOR DOCUMENTATION
*
* The iterator functions allow for easy, and convenient iteration
* over items of iterest in the list. Typically, you create a
* iterator, use it, and destroy it, as such:
* ITERATOR i;
*
* iterator_xxxitems(&i, ...);
* while (iterator_{prev,next}(&i)
* {
* //code which uses i.nItem
* }
* iterator_destroy(&i);
*
* where xxx is either: framed, or visible.
* Note that it is important that the code destroys the iterator
* after it's done with it, as the creation of the iterator may
* allocate memory, which thus needs to be freed.
*
* You can iterate both forwards, and backwards through the list,
* by using iterator_next or iterator_prev respectively.
*
* Lower numbered items are draw on top of higher number items in
* LVS_ICON, and LVS_SMALLICON (which are the only modes where
* items may overlap). So, to test items, you should use
* iterator_next
* which lists the items top to bottom (in Z-order).
* For drawing items, you should use
* iterator_prev
* which lists the items bottom to top (in Z-order).
* If you keep iterating over the items after the end-of-items
* marker (-1) is returned, the iterator will start from the
* beginning. Typically, you don't need to test for -1,
* because iterator_{next,prev} will return TRUE if more items
* are to be iterated over, or FALSE otherwise.
*
* Note: the iterator is defined to be bidirectional. That is,
* any number of prev followed by any number of next, or
* five versa, should leave the iterator at the same item:
* prev * n, next * n = next * n, prev * n
*
* The iterator has a notion of an out-of-order, special item,
* which sits at the start of the list. This is used in
* LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
* which needs to be first, as it may overlap other items.
*
* The code is a bit messy because we have:
* - a special item to deal with
* - simple range, or composite range
* - empty range.
* If you find bugs, or want to add features, please make sure you
* always check/modify *both* iterator_prev, and iterator_next.
*/
/****
* This function iterates through the items in increasing order,
* but prefixed by the special item, then -1. That is:
* special, 1, 2, 3, ..., n, -1.
* Each item is listed only once.
*/
static inline BOOL iterator_next(ITERATOR* i)
{
if (i->nItem == -1)
{
i->nItem = i->nSpecial;
if (i->nItem != -1) return TRUE;
}
if (i->nItem == i->nSpecial)
{
if (i->ranges) i->index = 0;
goto pickarange;
}
i->nItem++;
testitem:
if (i->nItem == i->nSpecial) i->nItem++;
if (i->nItem < i->range.upper) return TRUE;
pickarange:
if (i->ranges)
{
if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
else goto end;
}
else if (i->nItem >= i->range.upper) goto end;
i->nItem = i->range.lower;
if (i->nItem >= 0) goto testitem;
end:
i->nItem = -1;
return FALSE;
}
/****
* This function iterates through the items in decreasing order,
* followed by the special item, then -1. That is:
* n, n-1, ..., 3, 2, 1, special, -1.
* Each item is listed only once.
*/
static inline BOOL iterator_prev(ITERATOR* i)
{
BOOL start = FALSE;
if (i->nItem == -1)
{
start = TRUE;
if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
goto pickarange;
}
if (i->nItem == i->nSpecial)
{
i->nItem = -1;
return FALSE;
}
testitem:
i->nItem--;
if (i->nItem == i->nSpecial) i->nItem--;
if (i->nItem >= i->range.lower) return TRUE;
pickarange:
if (i->ranges)
{
if (i->index > 0)
i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
else goto end;
}
else if (!start && i->nItem < i->range.lower) goto end;
i->nItem = i->range.upper;
if (i->nItem > 0) goto testitem;
end:
return (i->nItem = i->nSpecial) != -1;
}
static RANGE iterator_range(ITERATOR* i)
{
RANGE range;
if (!i->ranges) return i->range;
if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
{
range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
}
else range.lower = range.upper = 0;
return range;
}
/***
* Releases resources associated with this ierator.
*/
static inline void iterator_destroy(ITERATOR* i)
{
ranges_destroy(i->ranges);
}
/***
* Create an empty iterator.
*/
static inline BOOL iterator_empty(ITERATOR* i)
{
ZeroMemory(i, sizeof(*i));
i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
return TRUE;
}
/***
* Create an iterator over a range.
*/
static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
{
iterator_empty(i);
i->range = range;
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -