📄 treeview.c
字号:
infoPtr->maxVisibleOrder = order;
for (item = start; item != NULL;
item = TREEVIEW_GetNextListItem(infoPtr, item))
{
TREEVIEW_ComputeItemRect(infoPtr, item);
}
}
/* Update metrics of all items in selected subtree.
* root must be expanded
*/
static VOID
TREEVIEW_UpdateSubTree(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *root)
{
TREEVIEW_ITEM *sibling;
HDC hdc;
HFONT hOldFont;
if (!root->firstChild || !(root->state & TVIS_EXPANDED))
return;
root->state &= ~TVIS_EXPANDED;
sibling = TREEVIEW_GetNextListItem(infoPtr, root);
root->state |= TVIS_EXPANDED;
hdc = GetDC(infoPtr->hwnd);
hOldFont = SelectObject(hdc, infoPtr->hFont);
for (; root != sibling;
root = TREEVIEW_GetNextListItem(infoPtr, root))
{
TREEVIEW_ComputeItemInternalMetrics(infoPtr, root);
if (root->callbackMask & TVIF_TEXT)
TREEVIEW_UpdateDispInfo(infoPtr, root, TVIF_TEXT);
if (root->textWidth == 0)
{
SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, root));
TREEVIEW_ComputeTextWidth(infoPtr, root, hdc);
}
}
SelectObject(hdc, hOldFont);
ReleaseDC(infoPtr->hwnd, hdc);
}
/* Item Allocation **********************************************************/
static TREEVIEW_ITEM *
TREEVIEW_AllocateItem(TREEVIEW_INFO *infoPtr)
{
TREEVIEW_ITEM *newItem = Alloc(sizeof(TREEVIEW_ITEM));
if (!newItem)
return NULL;
newItem->iImage = -1;
newItem->iSelectedImage = -1;
if (DPA_InsertPtr(infoPtr->items, INT_MAX, newItem) == -1)
{
Free(newItem);
return NULL;
}
return newItem;
}
/* Exact opposite of TREEVIEW_AllocateItem. In particular, it does not
* free item->pszText. */
static void
TREEVIEW_FreeItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
{
DPA_DeletePtr(infoPtr->items, DPA_GetPtrIndex(infoPtr->items, item));
Free(item);
if (infoPtr->selectedItem == item)
infoPtr->selectedItem = NULL;
if (infoPtr->hotItem == item)
infoPtr->hotItem = NULL;
if (infoPtr->focusedItem == item)
infoPtr->focusedItem = NULL;
if (infoPtr->firstVisible == item)
infoPtr->firstVisible = NULL;
if (infoPtr->dropItem == item)
infoPtr->dropItem = NULL;
if (infoPtr->insertMarkItem == item)
infoPtr->insertMarkItem = NULL;
}
/* Item Insertion *******************************************************/
/***************************************************************************
* This method inserts newItem before sibling as a child of parent.
* sibling can be NULL, but only if parent has no children.
*/
static void
TREEVIEW_InsertBefore(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
TREEVIEW_ITEM *parent)
{
assert(newItem != NULL);
assert(parent != NULL);
if (sibling != NULL)
{
assert(sibling->parent == parent);
if (sibling->prevSibling != NULL)
sibling->prevSibling->nextSibling = newItem;
newItem->prevSibling = sibling->prevSibling;
sibling->prevSibling = newItem;
}
else
newItem->prevSibling = NULL;
newItem->nextSibling = sibling;
if (parent->firstChild == sibling)
parent->firstChild = newItem;
if (parent->lastChild == NULL)
parent->lastChild = newItem;
}
/***************************************************************************
* This method inserts newItem after sibling as a child of parent.
* sibling can be NULL, but only if parent has no children.
*/
static void
TREEVIEW_InsertAfter(TREEVIEW_ITEM *newItem, TREEVIEW_ITEM *sibling,
TREEVIEW_ITEM *parent)
{
assert(newItem != NULL);
assert(parent != NULL);
if (sibling != NULL)
{
assert(sibling->parent == parent);
if (sibling->nextSibling != NULL)
sibling->nextSibling->prevSibling = newItem;
newItem->nextSibling = sibling->nextSibling;
sibling->nextSibling = newItem;
}
else
newItem->nextSibling = NULL;
newItem->prevSibling = sibling;
if (parent->lastChild == sibling)
parent->lastChild = newItem;
if (parent->firstChild == NULL)
parent->firstChild = newItem;
}
static BOOL
TREEVIEW_DoSetItemT(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem,
const TVITEMEXW *tvItem, BOOL isW)
{
UINT callbackClear = 0;
UINT callbackSet = 0;
TRACE("item %p\n", wineItem);
/* Do this first in case it fails. */
if (tvItem->mask & TVIF_TEXT)
{
wineItem->textWidth = 0; /* force width recalculation */
if (tvItem->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */
{
int len;
LPWSTR newText;
if (isW)
len = lstrlenW(tvItem->pszText) + 1;
else
len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, NULL, 0);
newText = ReAlloc(wineItem->pszText, len * sizeof(WCHAR));
if (newText == NULL) return FALSE;
callbackClear |= TVIF_TEXT;
wineItem->pszText = newText;
wineItem->cchTextMax = len;
if (isW)
lstrcpynW(wineItem->pszText, tvItem->pszText, len);
else
MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1,
wineItem->pszText, len);
TRACE("setting text %s, item %p\n", debugstr_w(wineItem->pszText), wineItem);
}
else
{
callbackSet |= TVIF_TEXT;
wineItem->pszText = ReAlloc(wineItem->pszText,
TEXT_CALLBACK_SIZE * sizeof(WCHAR));
wineItem->cchTextMax = TEXT_CALLBACK_SIZE;
TRACE("setting callback, item %p\n", wineItem);
}
}
if (tvItem->mask & TVIF_CHILDREN)
{
wineItem->cChildren = tvItem->cChildren;
if (wineItem->cChildren == I_CHILDRENCALLBACK)
callbackSet |= TVIF_CHILDREN;
else
callbackClear |= TVIF_CHILDREN;
}
if (tvItem->mask & TVIF_IMAGE)
{
wineItem->iImage = tvItem->iImage;
if (wineItem->iImage == I_IMAGECALLBACK)
callbackSet |= TVIF_IMAGE;
else
callbackClear |= TVIF_IMAGE;
}
if (tvItem->mask & TVIF_SELECTEDIMAGE)
{
wineItem->iSelectedImage = tvItem->iSelectedImage;
if (wineItem->iSelectedImage == I_IMAGECALLBACK)
callbackSet |= TVIF_SELECTEDIMAGE;
else
callbackClear |= TVIF_SELECTEDIMAGE;
}
if (tvItem->mask & TVIF_PARAM)
wineItem->lParam = tvItem->lParam;
/* If the application sets TVIF_INTEGRAL without
* supplying a TVITEMEX structure, it's toast. */
if (tvItem->mask & TVIF_INTEGRAL)
wineItem->iIntegral = tvItem->iIntegral;
if (tvItem->mask & TVIF_STATE)
{
TRACE("prevstate,state,mask:%x,%x,%x\n", wineItem->state, tvItem->state,
tvItem->stateMask);
wineItem->state &= ~tvItem->stateMask;
wineItem->state |= (tvItem->state & tvItem->stateMask);
}
wineItem->callbackMask |= callbackSet;
wineItem->callbackMask &= ~callbackClear;
return TRUE;
}
/* Note that the new item is pre-zeroed. */
static LRESULT
TREEVIEW_InsertItemT(TREEVIEW_INFO *infoPtr, const TVINSERTSTRUCTW *ptdi, BOOL isW)
{
const TVITEMEXW *tvItem = &ptdi->u.itemex;
HTREEITEM insertAfter;
TREEVIEW_ITEM *newItem, *parentItem;
BOOL bTextUpdated = FALSE;
if (ptdi->hParent == TVI_ROOT || ptdi->hParent == 0)
{
parentItem = infoPtr->root;
}
else
{
parentItem = ptdi->hParent;
if (!TREEVIEW_ValidItem(infoPtr, parentItem))
{
WARN("invalid parent %p\n", parentItem);
return (LRESULT)(HTREEITEM)NULL;
}
}
insertAfter = ptdi->hInsertAfter;
/* Validate this now for convenience. */
switch ((DWORD_PTR)insertAfter)
{
case (DWORD_PTR)TVI_FIRST:
case (DWORD_PTR)TVI_LAST:
case (DWORD_PTR)TVI_SORT:
break;
default:
if (!TREEVIEW_ValidItem(infoPtr, insertAfter) ||
insertAfter->parent != parentItem)
{
WARN("invalid insert after %p\n", insertAfter);
insertAfter = TVI_LAST;
}
}
TRACE("parent %p position %p: %s\n", parentItem, insertAfter,
(tvItem->mask & TVIF_TEXT)
? ((tvItem->pszText == LPSTR_TEXTCALLBACKW) ? "<callback>"
: (isW ? debugstr_w(tvItem->pszText) : debugstr_a((LPSTR)tvItem->pszText)))
: "<no label>");
newItem = TREEVIEW_AllocateItem(infoPtr);
if (newItem == NULL)
return (LRESULT)(HTREEITEM)NULL;
newItem->parent = parentItem;
newItem->iIntegral = 1;
if (!TREEVIEW_DoSetItemT(infoPtr, newItem, tvItem, isW))
return (LRESULT)(HTREEITEM)NULL;
/* After this point, nothing can fail. (Except for TVI_SORT.) */
infoPtr->uNumItems++;
switch ((DWORD_PTR)insertAfter)
{
case (DWORD_PTR)TVI_FIRST:
{
TREEVIEW_ITEM *originalFirst = parentItem->firstChild;
TREEVIEW_InsertBefore(newItem, parentItem->firstChild, parentItem);
if (infoPtr->firstVisible == originalFirst)
TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
}
break;
case (DWORD_PTR)TVI_LAST:
TREEVIEW_InsertAfter(newItem, parentItem->lastChild, parentItem);
break;
/* hInsertAfter names a specific item we want to insert after */
default:
TREEVIEW_InsertAfter(newItem, insertAfter, insertAfter->parent);
break;
case (DWORD_PTR)TVI_SORT:
{
TREEVIEW_ITEM *aChild;
TREEVIEW_ITEM *previousChild = NULL;
TREEVIEW_ITEM *originalFirst = parentItem->firstChild;
BOOL bItemInserted = FALSE;
aChild = parentItem->firstChild;
bTextUpdated = TRUE;
TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
/* Iterate the parent children to see where we fit in */
while (aChild != NULL)
{
INT comp;
TREEVIEW_UpdateDispInfo(infoPtr, aChild, TVIF_TEXT);
comp = lstrcmpW(newItem->pszText, aChild->pszText);
if (comp < 0) /* we are smaller than the current one */
{
TREEVIEW_InsertBefore(newItem, aChild, parentItem);
if (infoPtr->firstVisible == originalFirst &&
aChild == originalFirst)
TREEVIEW_SetFirstVisible(infoPtr, newItem, TRUE);
bItemInserted = TRUE;
break;
}
else if (comp > 0) /* we are bigger than the current one */
{
previousChild = aChild;
/* This will help us to exit if there is no more sibling */
aChild = (aChild->nextSibling == 0)
? NULL
: aChild->nextSibling;
/* Look at the next item */
continue;
}
else if (comp == 0)
{
/*
* An item with this name is already existing, therefore,
* we add after the one we found
*/
TREEVIEW_InsertAfter(newItem, aChild, parentItem);
bItemInserted = TRUE;
break;
}
}
/*
* we reach the end of the child list and the item has not
* yet been inserted, therefore, insert it after the last child.
*/
if ((!bItemInserted) && (aChild == NULL))
TREEVIEW_InsertAfter(newItem, previousChild, parentItem);
break;
}
}
TRACE("new item %p; parent %p, mask %x\n", newItem,
newItem->parent, tvItem->mask);
newItem->iLevel = newItem->parent->iLevel + 1;
if (newItem->parent->cChildren == 0)
newItem->parent->cChildren = 1;
if (infoPtr->dwStyle & TVS_CHECKBOXES)
{
if (STATEIMAGEINDEX(newItem->state) == 0)
newItem->state |= INDEXTOSTATEIMAGEMASK(1);
}
if (infoPtr->firstVisible == NULL)
infoPtr->firstVisible = newItem;
TREEVIEW_VerifyTree(infoPtr);
if (parentItem == infoPtr->root ||
(ISVISIBLE(parentItem) && parentItem->state & TVIS_EXPANDED))
{
TREEVIEW_ITEM *item;
TREEVIEW_ITEM *prev = TREEVIEW_GetPrevListItem(infoPtr, newItem);
TREEVIEW_RecalculateVisibleOrder(infoPtr, prev);
TREEVIEW_ComputeItemInternalMetrics(infoPtr, newItem);
if (!bTextUpdated)
TREEVIEW_UpdateDispInfo(infoPtr, newItem, TVIF_TEXT);
TREEVIEW_ComputeTextWidth(infoPtr, newItem, 0);
TREEVIEW_UpdateScrollBars(infoPtr);
/*
* if the item was inserted in a visible part of the tree,
* invalidate it, as well as those after it
*/
for (item = newItem;
item != NULL;
item = TREEVIEW_GetNextListItem(infoPtr, item))
TREEVIEW_Invalidate(infoPtr, item);
}
else
{
newItem->visibleOrder = -1;
/* refresh treeview if newItem is the first item inserted under parentItem */
if (ISVISIBLE(parentItem) && newItem->prevSibling == newItem->nextSibling)
{
/* parent got '+' - update it */
TREEVIEW_Invalidate(infoPtr, parentItem);
}
}
return (LRESULT)newItem;
}
/* Item Deletion ************************************************************/
static void
TREEVIEW_RemoveItem(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *wineItem);
static void
TREEVIEW_RemoveAllChildren(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *parentItem)
{
TREEVIEW_ITEM *kill = parentItem->firstChild;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -