⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 treeview.c

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 C
📖 第 1 页 / 共 5 页
字号:
}

/* 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 + -