📄 listview.c
字号:
/* Dump the LISTVIEW_INFO structure to the debug channel */
#define LISTVIEW_DUMP(iP) do { \
TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
} while(0)
static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
/*
* forward declarations
*/
static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
/******** Text handling functions *************************************/
/* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
* text string. The string may be ANSI or Unicode, in which case
* the boolean isW tells us the type of the string.
*
* The name of the function tell what type of strings it expects:
* W: Unicode, T: ANSI/Unicode - function of isW
*/
static inline BOOL is_textW(LPCWSTR text)
{
return text != NULL && text != LPSTR_TEXTCALLBACKW;
}
static inline BOOL is_textT(LPCWSTR text, BOOL isW)
{
/* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
return is_textW(text);
}
static inline int textlenT(LPCWSTR text, BOOL isW)
{
return !is_textT(text, isW) ? 0 :
isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
}
static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
{
if (isDestW)
if (isSrcW) lstrcpynW(dest, src, max);
else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
else
if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
}
static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
{
LPWSTR wstr = (LPWSTR)text;
if (!isW && is_textT(text, isW))
{
INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
wstr = Alloc(len * sizeof(WCHAR));
if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
}
TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
return wstr;
}
static inline void textfreeT(LPWSTR wstr, BOOL isW)
{
if (!isW && is_textT(wstr, isW)) Free (wstr);
}
/*
* dest is a pointer to a Unicode string
* src is a pointer to a string (Unicode if isW, ANSI if !isW)
*/
static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
{
BOOL bResult = TRUE;
if (src == LPSTR_TEXTCALLBACKW)
{
if (is_textW(*dest)) Free(*dest);
*dest = LPSTR_TEXTCALLBACKW;
}
else
{
LPWSTR pszText = textdupTtoW(src, isW);
if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
bResult = Str_SetPtrW(dest, pszText);
textfreeT(pszText, isW);
}
return bResult;
}
/*
* compares a Unicode to a Unicode/ANSI text string
*/
static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
{
if (!aw) return bt ? -1 : 0;
if (!bt) return aw ? 1 : 0;
if (aw == LPSTR_TEXTCALLBACKW)
return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
if (bt != LPSTR_TEXTCALLBACKW)
{
LPWSTR bw = textdupTtoW(bt, isW);
int r = bw ? lstrcmpW(aw, bw) : 1;
textfreeT(bw, isW);
return r;
}
return 1;
}
static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
{
int res;
n = min(min(n, strlenW(s1)), strlenW(s2));
res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
return res ? res - sizeof(WCHAR) : res;
}
/******** Debugging functions *****************************************/
static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
{
if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
}
static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
{
if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
n = min(textlenT(text, isW), n);
return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
}
static char* debug_getbuf(void)
{
static int index = 0;
static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
return buffers[index++ % DEBUG_BUFFERS];
}
static inline const char* debugrange(const RANGE *lprng)
{
if (!lprng) return "(null)";
return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
}
static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
{
char* buf = debug_getbuf(), *text = buf;
int len, size = DEBUG_BUFFER_SIZE;
if (pScrollInfo == NULL) return "(null)";
len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
if (len == -1) goto end; buf += len; size -= len;
if (pScrollInfo->fMask & SIF_RANGE)
len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (pScrollInfo->fMask & SIF_PAGE)
len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (pScrollInfo->fMask & SIF_POS)
len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (pScrollInfo->fMask & SIF_TRACKPOS)
len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
goto undo;
end:
buf = text + strlen(text);
undo:
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
return text;
}
static const char* debugnmlistview(const NMLISTVIEW *plvnm)
{
if (!plvnm) return "(null)";
return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
" uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
}
static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
{
char* buf = debug_getbuf(), *text = buf;
int len, size = DEBUG_BUFFER_SIZE;
if (lpLVItem == NULL) return "(null)";
len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
if (len == -1) goto end; buf += len; size -= len;
if (lpLVItem->mask & LVIF_STATE)
len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpLVItem->mask & LVIF_TEXT)
len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpLVItem->mask & LVIF_IMAGE)
len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpLVItem->mask & LVIF_PARAM)
len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpLVItem->mask & LVIF_INDENT)
len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
goto undo;
end:
buf = text + strlen(text);
undo:
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
return text;
}
static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
{
char* buf = debug_getbuf(), *text = buf;
int len, size = DEBUG_BUFFER_SIZE;
if (lpColumn == NULL) return "(null)";
len = snprintf(buf, size, "{");
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_SUBITEM)
len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_FMT)
len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_WIDTH)
len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_TEXT)
len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_IMAGE)
len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
if (lpColumn->mask & LVCF_ORDER)
len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
else len = 0;
if (len == -1) goto end; buf += len; size -= len;
goto undo;
end:
buf = text + strlen(text);
undo:
if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
return text;
}
static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
{
if (!lpht) return "(null)";
return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
}
/* Return the corresponding text for a given scroll value */
static inline LPCSTR debugscrollcode(int nScrollCode)
{
switch(nScrollCode)
{
case SB_LINELEFT: return "SB_LINELEFT";
case SB_LINERIGHT: return "SB_LINERIGHT";
case SB_PAGELEFT: return "SB_PAGELEFT";
case SB_PAGERIGHT: return "SB_PAGERIGHT";
case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
case SB_THUMBTRACK: return "SB_THUMBTRACK";
case SB_ENDSCROLL: return "SB_ENDSCROLL";
case SB_INTERNAL: return "SB_INTERNAL";
default: return "unknown";
}
}
/******** Notification functions i************************************/
static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
{
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
(WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
}
static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
{
LRESULT result;
TRACE("(code=%d)\n", code);
pnmh->hwndFrom = infoPtr->hwndSelf;
pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
pnmh->code = code;
result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
(WPARAM)pnmh->idFrom, (LPARAM)pnmh);
TRACE(" <= %ld\n", result);
return result;
}
static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
{
NMHDR nmh;
HWND hwnd = infoPtr->hwndSelf;
notify_hdr(infoPtr, code, &nmh);
return IsWindow(hwnd);
}
static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
{
NMITEMACTIVATE nmia;
LVITEMW item;
if (htInfo) {
nmia.uNewState = 0;
nmia.uOldState = 0;
nmia.uChanged = 0;
nmia.uKeyFlags = 0;
item.mask = LVIF_PARAM|LVIF_STATE;
item.iItem = htInfo->iItem;
item.iSubItem = 0;
if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
nmia.lParam = item.lParam;
nmia.uOldState = item.state;
nmia.uNewState = item.state | LVIS_ACTIVATING;
nmia.uChanged = LVIF_STATE;
}
nmia.iItem = htInfo->iItem;
nmia.iSubItem = htInfo->iSubItem;
nmia.ptAction = htInfo->pt;
if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
}
notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
}
static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
{
TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -