📄 treeview.c
字号:
}
/* Notifications ************************************************************/
static INT get_notifycode(TREEVIEW_INFO *infoPtr, INT code)
{
if (!infoPtr->bNtfUnicode) {
switch (code) {
case TVN_SELCHANGINGW: return TVN_SELCHANGINGA;
case TVN_SELCHANGEDW: return TVN_SELCHANGEDA;
case TVN_GETDISPINFOW: return TVN_GETDISPINFOA;
case TVN_SETDISPINFOW: return TVN_SETDISPINFOA;
case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA;
case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA;
case TVN_BEGINDRAGW: return TVN_BEGINDRAGA;
case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA;
case TVN_DELETEITEMW: return TVN_DELETEITEMA;
case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA;
case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA;
case TVN_GETINFOTIPW: return TVN_GETINFOTIPA;
}
}
return code;
}
static LRESULT
TREEVIEW_SendRealNotify(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
TRACE("wParam=%d, lParam=%ld\n", wParam, lParam);
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, wParam, lParam);
}
static BOOL
TREEVIEW_SendSimpleNotify(TREEVIEW_INFO *infoPtr, UINT code)
{
NMHDR nmhdr;
HWND hwnd = infoPtr->hwnd;
TRACE("%d\n", code);
nmhdr.hwndFrom = hwnd;
nmhdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
nmhdr.code = get_notifycode(infoPtr, code);
return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
}
static VOID
TREEVIEW_TVItemFromItem(TREEVIEW_INFO *infoPtr, UINT mask, TVITEMW *tvItem, TREEVIEW_ITEM *item)
{
tvItem->mask = mask;
tvItem->hItem = item;
tvItem->state = item->state;
tvItem->stateMask = 0;
tvItem->iImage = item->iImage;
tvItem->iSelectedImage = item->iSelectedImage;
tvItem->cChildren = item->cChildren;
tvItem->lParam = item->lParam;
if(mask & TVIF_TEXT)
{
if (!infoPtr->bNtfUnicode)
{
tvItem->cchTextMax = WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, NULL, 0, NULL, NULL );
tvItem->pszText = Alloc (tvItem->cchTextMax);
WideCharToMultiByte( CP_ACP, 0, item->pszText, -1, (LPSTR)tvItem->pszText, tvItem->cchTextMax, 0, 0 );
}
else
{
tvItem->cchTextMax = item->cchTextMax;
tvItem->pszText = item->pszText;
}
}
else
{
tvItem->cchTextMax = 0;
tvItem->pszText = NULL;
}
}
static BOOL
TREEVIEW_SendTreeviewNotify(TREEVIEW_INFO *infoPtr, UINT code, UINT action,
UINT mask, HTREEITEM oldItem, HTREEITEM newItem)
{
HWND hwnd = infoPtr->hwnd;
NMTREEVIEWW nmhdr;
BOOL ret;
TRACE("code:%d action:%x olditem:%p newitem:%p\n",
code, action, oldItem, newItem);
ZeroMemory(&nmhdr, sizeof(NMTREEVIEWW));
nmhdr.hdr.hwndFrom = hwnd;
nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
nmhdr.hdr.code = get_notifycode(infoPtr, code);
nmhdr.action = action;
if (oldItem)
TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemOld, oldItem);
if (newItem)
TREEVIEW_TVItemFromItem(infoPtr, mask, &nmhdr.itemNew, newItem);
nmhdr.ptDrag.x = 0;
nmhdr.ptDrag.y = 0;
ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)nmhdr.hdr.idFrom,
(LPARAM)&nmhdr);
if (!infoPtr->bNtfUnicode)
{
Free(nmhdr.itemOld.pszText);
Free(nmhdr.itemNew.pszText);
}
return ret;
}
static BOOL
TREEVIEW_SendTreeviewDnDNotify(TREEVIEW_INFO *infoPtr, UINT code,
HTREEITEM dragItem, POINT pt)
{
HWND hwnd = infoPtr->hwnd;
NMTREEVIEWW nmhdr;
TRACE("code:%d dragitem:%p\n", code, dragItem);
nmhdr.hdr.hwndFrom = hwnd;
nmhdr.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
nmhdr.hdr.code = get_notifycode(infoPtr, code);
nmhdr.action = 0;
nmhdr.itemNew.mask = TVIF_STATE | TVIF_PARAM | TVIF_HANDLE;
nmhdr.itemNew.hItem = dragItem;
nmhdr.itemNew.state = dragItem->state;
nmhdr.itemNew.lParam = dragItem->lParam;
nmhdr.ptDrag.x = pt.x;
nmhdr.ptDrag.y = pt.y;
return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)nmhdr.hdr.idFrom,
(LPARAM)&nmhdr);
}
static BOOL
TREEVIEW_SendCustomDrawNotify(TREEVIEW_INFO *infoPtr, DWORD dwDrawStage,
HDC hdc, RECT rc)
{
HWND hwnd = infoPtr->hwnd;
NMTVCUSTOMDRAW nmcdhdr;
LPNMCUSTOMDRAW nmcd;
TRACE("drawstage:%x hdc:%p\n", dwDrawStage, hdc);
nmcd = &nmcdhdr.nmcd;
nmcd->hdr.hwndFrom = hwnd;
nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
nmcd->hdr.code = NM_CUSTOMDRAW;
nmcd->dwDrawStage = dwDrawStage;
nmcd->hdc = hdc;
nmcd->rc = rc;
nmcd->dwItemSpec = 0;
nmcd->uItemState = 0;
nmcd->lItemlParam = 0;
nmcdhdr.clrText = infoPtr->clrText;
nmcdhdr.clrTextBk = infoPtr->clrBk;
nmcdhdr.iLevel = 0;
return (BOOL)TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)nmcd->hdr.idFrom,
(LPARAM)&nmcdhdr);
}
/* FIXME: need to find out when the flags in uItemState need to be set */
static BOOL
TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
TREEVIEW_ITEM *wineItem, UINT uItemDrawState,
NMTVCUSTOMDRAW *nmcdhdr)
{
HWND hwnd = infoPtr->hwnd;
LPNMCUSTOMDRAW nmcd;
DWORD dwDrawStage;
DWORD_PTR dwItemSpec;
UINT uItemState;
INT retval;
dwDrawStage = CDDS_ITEM | uItemDrawState;
dwItemSpec = (DWORD_PTR)wineItem;
uItemState = 0;
if (wineItem->state & TVIS_SELECTED)
uItemState |= CDIS_SELECTED;
if (wineItem == infoPtr->selectedItem)
uItemState |= CDIS_FOCUS;
if (wineItem == infoPtr->hotItem)
uItemState |= CDIS_HOT;
nmcd = &nmcdhdr->nmcd;
nmcd->hdr.hwndFrom = hwnd;
nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
nmcd->hdr.code = NM_CUSTOMDRAW;
nmcd->dwDrawStage = dwDrawStage;
nmcd->hdc = hdc;
nmcd->rc = wineItem->rect;
nmcd->dwItemSpec = dwItemSpec;
nmcd->uItemState = uItemState;
nmcd->lItemlParam = wineItem->lParam;
nmcdhdr->iLevel = wineItem->iLevel;
TRACE("drawstage:%x hdc:%p item:%lx, itemstate:%x, lItemlParam:%lx\n",
nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
nmcd->uItemState, nmcd->lItemlParam);
retval = TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)nmcd->hdr.idFrom,
(LPARAM)nmcdhdr);
return (BOOL)retval;
}
static BOOL
TREEVIEW_BeginLabelEditNotify(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *editItem)
{
HWND hwnd = infoPtr->hwnd;
NMTVDISPINFOW tvdi;
BOOL ret;
tvdi.hdr.hwndFrom = hwnd;
tvdi.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
tvdi.hdr.code = get_notifycode(infoPtr, TVN_BEGINLABELEDITW);
TREEVIEW_TVItemFromItem(infoPtr, TVIF_HANDLE | TVIF_STATE | TVIF_PARAM | TVIF_TEXT,
&tvdi.item, editItem);
ret = (BOOL)TREEVIEW_SendRealNotify(infoPtr, tvdi.hdr.idFrom, (LPARAM)&tvdi);
if (!infoPtr->bNtfUnicode)
Free(tvdi.item.pszText);
return ret;
}
static void
TREEVIEW_UpdateDispInfo(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
UINT mask)
{
NMTVDISPINFOW callback;
HWND hwnd = infoPtr->hwnd;
TRACE("mask %x callbackMask %x\n", mask, wineItem->callbackMask);
mask &= wineItem->callbackMask;
if (mask == 0) return;
callback.hdr.hwndFrom = hwnd;
callback.hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
callback.hdr.code = get_notifycode(infoPtr, TVN_GETDISPINFOW);
/* 'state' always contains valid value, as well as 'lParam'.
* All other parameters are uninitialized.
*/
callback.item.pszText = wineItem->pszText;
callback.item.cchTextMax = wineItem->cchTextMax;
callback.item.mask = mask;
callback.item.hItem = wineItem;
callback.item.state = wineItem->state;
callback.item.lParam = wineItem->lParam;
/* If text is changed we need to recalculate textWidth */
if (mask & TVIF_TEXT)
wineItem->textWidth = 0;
TREEVIEW_SendRealNotify(infoPtr,
(WPARAM)callback.hdr.idFrom, (LPARAM)&callback);
/* It may have changed due to a call to SetItem. */
mask &= wineItem->callbackMask;
if ((mask & TVIF_TEXT) && callback.item.pszText != wineItem->pszText)
{
/* Instead of copying text into our buffer user specified its own */
if (!infoPtr->bNtfUnicode) {
LPWSTR newText;
int buflen;
int len = MultiByteToWideChar( CP_ACP, 0,
(LPSTR)callback.item.pszText, -1,
NULL, 0);
buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
newText = (LPWSTR)ReAlloc(wineItem->pszText, buflen);
TRACE("returned str %s, len=%d, buflen=%d\n",
debugstr_a((LPSTR)callback.item.pszText), len, buflen);
if (newText)
{
wineItem->pszText = newText;
MultiByteToWideChar( CP_ACP, 0,
(LPSTR)callback.item.pszText, -1,
wineItem->pszText, buflen/sizeof(WCHAR));
wineItem->cchTextMax = buflen/sizeof(WCHAR);
}
/* If ReAlloc fails we have nothing to do, but keep original text */
}
else {
int len = max(lstrlenW(callback.item.pszText) + 1,
TEXT_CALLBACK_SIZE);
LPWSTR newText = ReAlloc(wineItem->pszText, len);
TRACE("returned wstr %s, len=%d\n",
debugstr_w(callback.item.pszText), len);
if (newText)
{
wineItem->pszText = newText;
strcpyW(wineItem->pszText, callback.item.pszText);
wineItem->cchTextMax = len;
}
/* If ReAlloc fails we have nothing to do, but keep original text */
}
}
else if (mask & TVIF_TEXT) {
/* User put text into our buffer, that is ok unless A string */
if (!infoPtr->bNtfUnicode) {
LPWSTR newText;
LPWSTR oldText = NULL;
int buflen;
int len = MultiByteToWideChar( CP_ACP, 0,
(LPSTR)callback.item.pszText, -1,
NULL, 0);
buflen = max((len)*sizeof(WCHAR), TEXT_CALLBACK_SIZE);
newText = (LPWSTR)Alloc(buflen);
TRACE("same buffer str %s, len=%d, buflen=%d\n",
debugstr_a((LPSTR)callback.item.pszText), len, buflen);
if (newText)
{
oldText = wineItem->pszText;
wineItem->pszText = newText;
MultiByteToWideChar( CP_ACP, 0,
(LPSTR)callback.item.pszText, -1,
wineItem->pszText, buflen/sizeof(WCHAR));
wineItem->cchTextMax = buflen/sizeof(WCHAR);
if (oldText)
Free(oldText);
}
}
}
if (mask & TVIF_IMAGE)
wineItem->iImage = callback.item.iImage;
if (mask & TVIF_SELECTEDIMAGE)
wineItem->iSelectedImage = callback.item.iSelectedImage;
if (mask & TVIF_CHILDREN)
wineItem->cChildren = callback.item.cChildren;
/* These members are now permanently set. */
if (callback.item.mask & TVIF_DI_SETITEM)
wineItem->callbackMask &= ~callback.item.mask;
}
/***************************************************************************
* This function uses cChildren field to decide whether the item has
* children or not.
* Note: if this returns TRUE, the child items may not actually exist,
* they could be virtual.
*
* Just use wineItem->firstChild to check for physical children.
*/
static BOOL
TREEVIEW_HasChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem)
{
TREEVIEW_UpdateDispInfo(infoPtr, wineItem, TVIF_CHILDREN);
return wineItem->cChildren > 0;
}
/* Item Position ********************************************************/
/* Compute linesOffset, stateOffset, imageOffset, textOffset of an item. */
static VOID
TREEVIEW_ComputeItemInternalMetrics(TREEVIEW_INFO *infoPtr,
TREEVIEW_ITEM *item)
{
/* Same effect, different optimisation. */
#if 0
BOOL lar = ((infoPtr->dwStyle & TVS_LINESATROOT)
&& (infoPtr->dwStyle & (TVS_HASLINES|TVS_HASBUTTONS)));
#else
BOOL lar = ((infoPtr->dwStyle
& (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
> TVS_LINESATROOT);
#endif
item->linesOffset = infoPtr->uIndent * (item->iLevel + lar - 1)
- infoPtr->scrollX;
item->stateOffset = item->linesOffset + infoPtr->uIndent;
item->imageOffset = item->stateOffset
+ (STATEIMAGEINDEX(item->state) ? infoPtr->stateImageWidth : 0);
item->textOffset = item->imageOffset + infoPtr->normalImageWidth;
}
static VOID
TREEVIEW_ComputeTextWidth(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC hDC)
{
HDC hdc;
HFONT hOldFont=0;
SIZE sz;
/* DRAW's OM docker creates items like this */
if (item->pszText == NULL)
{
item->textWidth = 0;
return;
}
if (hDC != 0)
{
hdc = hDC;
}
else
{
hdc = GetDC(infoPtr->hwnd);
hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
}
GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz);
item->textWidth = sz.cx;
if (hDC == 0)
{
SelectObject(hdc, hOldFont);
ReleaseDC(0, hdc);
}
}
static VOID
TREEVIEW_ComputeItemRect(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
{
item->rect.top = infoPtr->uItemHeight *
(item->visibleOrder - infoPtr->firstVisible->visibleOrder);
item->rect.bottom = item->rect.top
+ infoPtr->uItemHeight * item->iIntegral - 1;
item->rect.left = 0;
item->rect.right = infoPtr->clientWidth;
}
/* We know that only items after start need their order updated. */
static void
TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
{
TREEVIEW_ITEM *item;
int order;
if (!start)
{
start = infoPtr->root->firstChild;
order = 0;
}
else
order = start->visibleOrder;
for (item = start; item != NULL;
item = TREEVIEW_GetNextListItem(infoPtr, item))
{
item->visibleOrder = order;
order += item->iIntegral;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -