Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

907 lines
25 KiB

#include "ctlspriv.h"
#include "treeview.h"
#if defined(MAINWIN)
#include <mainwin.h>
#endif
void TV_ScrollItems(PTREE pTree, int nItems, int iTopShownIndex, BOOL fDown);
// in:
// hItem item to delete
// flags controls how/what to delete
// TVDI_NORMAL delete this node and all children
// TVDI_NONOTIFY don't send notify messages
// TVDI_CHILDRENONLY just delete the kids (not the item)
void TV_DeleteItemRecurse(PTREE pTree, TREEITEM * hItem, UINT flags)
{
TREEITEM *hKid;
TREEITEM *hNext;
TREEITEM *hParent;
int i;
DBG_ValidateTreeItem(hItem, 0);
//
// We do this from DeleteItemRecurse(), kind of like how USER sends
// Destroy notifications from its FreeWindow() code, so that we get
// deletes for parent and children both.
//
NotifyWinEvent(EVENT_OBJECT_DESTROY, pTree->ci.hwnd, OBJID_CLIENT,
TV_GetAccId(hItem));
//
// While the item is still valid, clean up if it's the insertion point.
// The item needs to be valid because we're going to call other
// functions that validate their parameters...
//
if (hItem == pTree->htiInsert)
{
TV_SetInsertMark(pTree, NULL, FALSE);
ASSERT(pTree->htiInsert == NULL);
}
// remove all kids (and their kids)
for (hKid = hItem->hKids; hKid; hKid = hNext) {
hNext = hKid->hNext;
// recurse on each child
TV_DeleteItemRecurse(pTree, hKid, flags & ~TVDI_CHILDRENONLY);
}
if ((flags & TVDI_CHILDRENONLY) || !hItem->hParent)
return;
if (!(flags & TVDI_NONOTIFY))
{
NM_TREEVIEW nm;
// Let the app clean up after itself
nm.itemOld.hItem = hItem;
nm.itemOld.lParam = hItem->lParam;
nm.itemNew.mask = 0;
nm.itemOld.mask = (TVIF_HANDLE | TVIF_PARAM);
CCSendNotify(&pTree->ci, TVN_DELETEITEM, &nm.hdr);
}
//
// If anybody has a watch on our item, let him know that it's gone.
//
i = DPA_GetPtrCount(pTree->hdpaWatch);
while (--i >= 0)
{
PTVWATCHEDITEM pwi = DPA_FastGetPtr(pTree->hdpaWatch, i);
ASSERT(pwi);
if (pwi->hti == hItem) {
pwi->hti = hItem->hNext;
pwi->fStale = TRUE;
}
}
hParent = hItem->hParent;
ASSERT(hParent);
// unlink ourselves from the parent child chain
if (hParent->hKids == hItem) {
hParent->hKids = hItem->hNext;
hKid = NULL;
} else {
// not the first child, find our previous item (linear search!)
hKid = TV_GetNextItem(pTree, hItem, TVGN_PREVIOUS);
ASSERT(hKid);
hKid->hNext = hItem->hNext;
}
pTree->cItems--;
TV_ScrollBarsAfterRemove(pTree, hItem);
// reset tooltip after unlink from the parent child chain
if (pTree->hToolTip == hItem)
TV_SetToolTipTarget(pTree, NULL);
Str_Set(&hItem->lpstr, NULL);
TV_MarkAsDead(hItem);
// be careful from here down. hItem is unlinked but
// still has some valid fields
// Check to see if the user has deleted one of the
// special items that is stored in the main tree structure.
if (hItem == pTree->htiEdit)
pTree->htiEdit = NULL;
if (hItem == pTree->hDropTarget)
pTree->hDropTarget = NULL;
if (hItem == pTree->hOldDrop)
pTree->hOldDrop = NULL;
if (hItem == pTree->hHot )
pTree->hHot = NULL;
if (hItem == pTree->htiSearch )
pTree->htiSearch = NULL;
// if the caret escaped the collapsed area and landed on us, push it away
if (pTree->hCaret == hItem) {
HTREEITEM hTemp;
if (hItem->hNext)
hTemp = hItem->hNext;
else {
hTemp = VISIBLE_PARENT(hItem);
if (!hTemp)
hTemp = hKid; // set above when we unlinked from the previous item
}
// Reset the caret to NULL as to not try to reference our
// invalidated item.
pTree->hCaret = NULL;
TV_SelectItem(pTree, TVGN_CARET, hTemp, (flags & TVDI_NOSELCHANGE) ? 0 : TVSIFI_NOTIFY, 0);
ASSERT(pTree->hCaret != hItem);
}
ASSERT(pTree->hItemPainting != hItem);
ControlFree(pTree->hheap, hItem);
}
// ----------------------------------------------------------------------------
//
// Removes the given item and all children from the tree.
// Special case: if the given item is the hidden root, all children are
// removed, but the hidden root is NOT removed.
//
// sets cItems
//
// ----------------------------------------------------------------------------
BOOL TV_DeleteItem(PTREE pTree, TREEITEM * hItem, UINT flags)
{
if (hItem == TVI_ROOT || !hItem)
hItem = pTree->hRoot;
// BUGUBG: send TVN_DELETEALLITEMS and TVDI_NONOTIFY if they respond
// if (hItem == pTree->hRoot)
// etc.
if (!ValidateTreeItem(hItem, 0))
return FALSE;
// Collapse first to speed things up (not as much scroll bar recalcs) and
// to set the top index correctly after the remove.
if (hItem != pTree->hRoot)
TV_Expand(pTree, TVE_COLLAPSE, hItem, FALSE);
else
{
// TV_Expand punts on the root item, so manually iterate through it's kids
TREEITEM *hKid = hItem->hKids;
while (hKid)
{
TV_Expand(pTree, TVE_COLLAPSE, hKid, FALSE);
if (!ValidateTreeItem(hKid, 0)) break; // callback during collapse could delete
hKid = hKid->hNext;
}
}
// Invalidate everything below this item; must be done AFTER setting the
// selection
if (hItem->hParent == pTree->hRoot || hItem == pTree->hRoot || ITEM_VISIBLE(hItem->hParent)) {
if (pTree->fRedraw) {
InvalidateRect(pTree->ci.hwnd, NULL, TRUE);
}
} else {
TV_ScrollBelow(pTree, hItem->hParent, FALSE, FALSE);
}
// We can pass in the root to clear all items
if (hItem == pTree->hRoot)
flags |= TVDI_CHILDRENONLY;
TV_DeleteItemRecurse(pTree, hItem, flags);
ASSERT(pTree->hRoot); // didn't go too far, did we?
// maybe everything's gone...
// check out our cleanup job
if (!pTree->hRoot->hKids) {
// the tree itself
ASSERT(pTree->cItems == 0);
pTree->cItems = 0; // just removed it all, didn't we?
pTree->hTop = NULL;
AssertMsg(pTree->hCaret == NULL, TEXT("hCaret not NULL, but empty tree"));
pTree->hCaret = NULL;
pTree->fNameEditPending = FALSE;
pTree->cxMax = 0;
pTree->xPos = 0;
// the invisible root
ASSERT(pTree->hRoot->hNext == NULL);
pTree->hRoot->hNext = NULL;
ASSERT(pTree->hRoot->hParent == NULL);
pTree->hRoot->hParent = NULL;
ASSERT(pTree->hRoot->hKids == NULL);
pTree->hRoot->hKids = NULL;
ASSERT(pTree->hRoot->state & TVIS_EXPANDED);
pTree->hRoot->state |= (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
ASSERT(pTree->hRoot->iLevel == (BYTE)-1);
pTree->hRoot->iLevel = (BYTE) -1;
ASSERT(pTree->hRoot->iShownIndex == (WORD)-1);
pTree->hRoot->iShownIndex = (WORD) -1;
}
return TRUE;
}
// ----------------------------------------------------------------------------
//
// Creates the hidden root node for the tree -- all items will trace up to
// this root, and the first child of the root is the first item in the tree.
//
// sets hRoot
//
// ----------------------------------------------------------------------------
BOOL TV_CreateRoot(PTREE pTree)
{
TREEITEM * hRoot = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
if (!hRoot)
return FALSE;
// hRoot->hNext = NULL;
// hRoot->hKids = NULL;
// hRoot->hParent = NULL;
hRoot->iLevel = (BYTE) -1;
hRoot->state = (TVIS_EXPANDED | TVIS_EXPANDEDONCE);
hRoot->iShownIndex = (WORD)-1;
hRoot->wSignature = TV_SIG;
hRoot->dwAccId = pTree->dwLastAccId++;
pTree->hRoot = hRoot;
// OLEACC asks for the text of the root item (d'oh!)
Str_Set(&hRoot->lpstr, c_szNULL);
return TRUE;
}
#ifdef DEBUG
void DumpItem(TREEITEM *hItem)
{
LPTSTR p;
if (hItem->lpstr == LPSTR_TEXTCALLBACK)
p = TEXT("(callback)");
else if (hItem->lpstr == NULL)
p = TEXT("(null)");
else
p = hItem->lpstr;
TraceMsg(TF_TREEVIEW, "%s", p);
TraceMsg(TF_TREEVIEW, "\tstate:%4.4x show index:%3d level:%2d kids:%ld lparam:%4.4x",
hItem->state, hItem->iShownIndex,
hItem->iLevel, hItem->fKids, hItem->lParam);
}
#else
#define DumpItem(hItem)
#endif
// ----------------------------------------------------------------------------
//
// Adds the item described by the given arguments to the tree.
//
// sets hTop, cItems
//
// ----------------------------------------------------------------------------
#ifdef UNICODE
TREEITEM * TV_InsertItemA(PTREE pTree, LPTV_INSERTSTRUCTA lpis) {
LPSTR pszA = NULL;
TREEITEM *ptvi;
//HACK Alert! This code assumes that TV_INSERTSTRUCTA is exactly the same
// as TV_INSERTSTRUCTW except for the text pointer in the TVITEM
COMPILETIME_ASSERT(sizeof(TV_INSERTSTRUCTA) == sizeof(TV_INSERTSTRUCTW));
if (!IsFlagPtr(lpis) && (lpis->DUMMYUNION_MEMBER(item).mask & TVIF_TEXT) && !IsFlagPtr(lpis->DUMMYUNION_MEMBER(item).pszText)) {
pszA = lpis->DUMMYUNION_MEMBER(item).pszText;
lpis->DUMMYUNION_MEMBER(item).pszText = (LPSTR)ProduceWFromA(pTree->ci.uiCodePage, lpis->DUMMYUNION_MEMBER(item).pszText);
if (lpis->DUMMYUNION_MEMBER(item).pszText == NULL) {
lpis->DUMMYUNION_MEMBER(item).pszText = pszA;
return NULL;
}
}
ptvi = TV_InsertItem( pTree, (LPTV_INSERTSTRUCTW)lpis );
if (pszA) {
FreeProducedString(lpis->DUMMYUNION_MEMBER(item).pszText);
lpis->DUMMYUNION_MEMBER(item).pszText = pszA;
}
return ptvi;
}
#endif
TREEITEM * TV_InsertItem(PTREE pTree, LPTV_INSERTSTRUCT lpis)
{
TREEITEM *hNewItem, *hItem;
TREEITEM *hParent;
TREEITEM *hInsertAfter;
UINT mask;
if (!lpis)
return NULL; //Bug#94345: Validate LPTV_INSERTSTRUCT
// initialize _after_ the check for NULL!
hParent = lpis->hParent;
hInsertAfter = lpis->hInsertAfter;
mask = lpis->DUMMYUNION_MEMBER(item).mask;
// don't allow undefined bits
AssertMsg((lpis->DUMMYUNION_MEMBER(item).mask & ~TVIF_ALL) == 0, TEXT("Invalid TVIF mask specified"));
if (mask & ~TVIF_ALL) {
// if they used bogus bits,
// restrict to win95 bits only
// I'd like to fail completely, but for win95 compat, we can't
//
// this fixes QuaterDesk's CleanSweep which has bogus garbage on the stack for a mask
mask = (TVIF_WIN95 & mask);
}
TV_DismissEdit(pTree, FALSE);
//
// Zillions of apps pass garbage for hInsertAfter, so don't fail if
// it's invalid. Fortunately, we never dereference hInsertAfter, so
// garbage is okay.
if (!ValidateTreeItem(hParent, VTI_NULLOK)) // NULL means TVI_ROOT
return NULL;
DBG_ValidateTreeItem(hInsertAfter, 0);
hNewItem = ControlAlloc(pTree->hheap, sizeof(TREEITEM));
if (!hNewItem)
{
TraceMsg(TF_ERROR, "TreeView: Out of memory");
return NULL;
}
hNewItem->wSignature = TV_SIG;
if (mask & TVIF_TEXT)
{
//
// We will setup the text string next, before we link our self in
// as to handle the case where we run out of memory and need to
// destroy ourself without having to unlink.
//
if (!lpis->DUMMYUNION_MEMBER(item).pszText)
{
hNewItem->lpstr = LPSTR_TEXTCALLBACK;
}
else
{
if (!Str_Set(&hNewItem->lpstr, lpis->DUMMYUNION_MEMBER(item).pszText))
{
// Memory allocation failure...
TraceMsg(TF_ERROR, "TreeView: Out of memory");
TV_MarkAsDead(hNewItem);
ControlFree(pTree->hheap, hNewItem);
return NULL;
}
}
}
else
{
Str_Set(&hNewItem->lpstr, c_szNULL);
}
AssertMsg(hNewItem->lpstr != NULL, TEXT("Item added with NULL text"));
if ((hParent == NULL) || (hParent == TVI_ROOT))
{
hParent = pTree->hRoot;
if (!pTree->hTop)
pTree->hTop = hNewItem;
}
else if (!pTree->hRoot->hKids)
{
TV_MarkAsDead(hNewItem);
ControlFree(pTree->hheap, hNewItem);
return NULL;
}
// We will do the sort later, so we can handle TEXTCALLBACK things
if ((hInsertAfter == TVI_FIRST || hInsertAfter == TVI_SORT) || !hParent->hKids)
{
hNewItem->hNext = hParent->hKids;
hParent->hKids = hNewItem;
}
else
{
// Bug#94348: we should cache the last insert after pointer to try to
// catch the case of consecutive adds to the end of a node
if (hInsertAfter == TVI_LAST)
for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
;
else
{
for (hItem = hParent->hKids; hItem->hNext; hItem = hItem->hNext)
if (hItem == hInsertAfter)
break;
}
hNewItem->hNext = hItem->hNext;
hItem->hNext = hNewItem;
}
// hNewItem->hKids = NULL;
hNewItem->hParent = hParent;
hNewItem->iLevel = hParent->iLevel + 1;
// hNewItem->iWidth = 0;
// hNewItem->state = 0;
if ((mask & TVIF_INTEGRAL) &&
LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral) > 0)
{
hNewItem->iIntegral = LOWORD(lpis->DUMMYUNION_MEMBER(itemex).iIntegral);
}
else
{
hNewItem->iIntegral = 1;
}
if (pTree->hTop == hNewItem)
hNewItem->iShownIndex = 0; // calc me please!
else
hNewItem->iShownIndex = (WORD)-1; // calc me please!
if (mask & TVIF_IMAGE)
hNewItem->iImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iImage;
if (mask & TVIF_SELECTEDIMAGE)
hNewItem->iSelectedImage = (WORD) lpis->DUMMYUNION_MEMBER(item).iSelectedImage;
if (mask & TVIF_PARAM)
hNewItem->lParam = lpis->DUMMYUNION_MEMBER(item).lParam;
if (mask & TVIF_STATE)
hNewItem->state = lpis->DUMMYUNION_MEMBER(item).state & lpis->DUMMYUNION_MEMBER(item).stateMask;
// if we're in check box mode, inforce that it has a check box
if (pTree->ci.style & TVS_CHECKBOXES)
{
if ((hNewItem->state & TVIS_STATEIMAGEMASK) == 0)
{
hNewItem->state |= INDEXTOSTATEIMAGEMASK(1);
}
}
if ((hNewItem->state & TVIS_BOLD) && !pTree->hFontBold) //$BOLD
TV_CreateBoldFont(pTree); //$BOLD
// TraceMsg(TF_TRACE, "Tree: Inserting i = %d state = %d", TV_StateIndex(&lpis->item), lpis->item.state);
if (mask & TVIF_CHILDREN)
{
switch (lpis->DUMMYUNION_MEMBER(item).cChildren)
{
case I_CHILDRENCALLBACK:
hNewItem->fKids = KIDS_CALLBACK;
break;
case I_CHILDRENAUTO:
hNewItem->fKids = KIDS_COMPUTE;
break;
case 0:
hNewItem->fKids = KIDS_FORCE_NO;
break;
default:
hNewItem->fKids = KIDS_FORCE_YES;
break;
}
}
hNewItem->dwAccId = pTree->dwLastAccId++;
// accept state bits on create?
// mask & TVIF_STATE
pTree->cItems++;
// I don't want to do any callbacks until the item is completed
// so sorting waits until the end
// special case an only child for speed
// (hKids && hKids->hNext means more than one child)
if ((hInsertAfter == TVI_SORT) && hParent->hKids && hParent->hKids->hNext)
{
TVITEMEX sThisItem, sNextItem;
TCHAR szThis[64], szNext[64];
sThisItem.pszText = szThis;
sThisItem.cchTextMax = ARRAYSIZE(szThis);
TV_GetItem(pTree, hNewItem, TVIF_TEXT, &sThisItem);
// We know that the first kid of hParent is hNewItem
for (hItem = hNewItem->hNext; hItem; hItem = hItem->hNext)
{
sNextItem.pszText = szNext;
sNextItem.cchTextMax = ARRAYSIZE(szNext);
TV_GetItem(pTree, hItem, TVIF_TEXT, &sNextItem);
if (lstrcmpi(sThisItem.pszText, sNextItem.pszText) < 0)
break;
hInsertAfter = hItem;
}
// Check if this is still the first item
if (hInsertAfter != TVI_SORT)
{
// Move this item from the beginning to where it
// should be
hParent->hKids = hNewItem->hNext;
hNewItem->hNext = hInsertAfter->hNext;
hInsertAfter->hNext = hNewItem;
}
}
if ((hNewItem->hNext == pTree->hTop) && !pTree->fVert)
{
// there's no scrollbars and we got added before the top
// item. we're now the top.
hNewItem->iShownIndex = 0;
pTree->hTop = hNewItem;
}
if (pTree->fRedraw)
{
BOOL fVert = pTree->fVert;
RECT rc;
RECT rc2;
if (TV_ScrollBarsAfterAdd(pTree, hNewItem))
{
// scroll everything down one
if (ITEM_VISIBLE(hNewItem))
{
int iTop = hNewItem->iShownIndex - pTree->hTop->iShownIndex;
// if there wasn't a scrollbar and we're the 0th item,
// TV_ScrollBarsAfterAdd already scrolled us
if (iTop > 0 || !fVert)
TV_ScrollItems(pTree, hNewItem->iIntegral, iTop + hNewItem->iIntegral - 1, TRUE);
}
}
// connect the lines, add the buttons, etc. on the item above
// TV_GetPrevVisItem only works after TV_Scroll* stuff is done
if (TV_GetItemRect(pTree, hNewItem, &rc, FALSE))
{
// find the previous sibling or the parent if no prev sib.
if (hParent->hKids == hNewItem)
{
hItem = hParent;
}
else
{
hItem = hParent->hKids;
while ( hItem->hNext != hNewItem )
{
ASSERT(hItem->hNext);
hItem = hItem->hNext;
}
}
// invalidate from there to the new one
if (TV_GetItemRect(pTree, hItem, &rc2, FALSE))
{
rc.top = rc2.top;
}
RedrawWindow(pTree->ci.hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
}
}
// DumpItem(hNewItem);
NotifyWinEvent(EVENT_OBJECT_CREATE, pTree->ci.hwnd, OBJID_CLIENT, TV_GetAccId(hNewItem));
if (pTree->hToolTip)
{
TV_PopBubble(pTree);
}
return hNewItem;
}
void TV_DeleteHotFonts(PTREE pTree)
{
if (pTree->hFontHot)
DeleteObject(pTree->hFontHot);
if (pTree->hFontBoldHot)
DeleteObject(pTree->hFontBoldHot);
pTree->hFontHot = pTree->hFontBoldHot = NULL;
}
// ----------------------------------------------------------------------------
//
// Frees all allocated memory and objects associated with the tree.
//
// ----------------------------------------------------------------------------
void TV_DestroyTree(PTREE pTree)
{
HWND hwnd = pTree->ci.hwnd;
ASSERT(pTree->hRoot);
pTree->fRedraw = FALSE;
TV_OnSetBkColor(pTree, (COLORREF)-1);
if (pTree->hbrLine)
{
DeleteObject(pTree->hbrLine);
}
if (pTree->hbrText)
{
DeleteObject(pTree->hbrText);
}
if (pTree->hCurHot)
{
DestroyCursor(pTree->hCurHot);
}
if (IsWindow(pTree->hwndToolTips))
{
DestroyWindow(pTree->hwndToolTips);
}
pTree->hwndToolTips = NULL;
if (IsWindow(pTree->hwndEdit))
{
DestroyWindow(pTree->hwndEdit);
}
pTree->hwndEdit = NULL;
TV_DeleteItem(pTree, pTree->hRoot, TVDI_CHILDRENONLY | TVDI_NOSELCHANGE);
if (pTree->hRoot)
{
Str_Set(&pTree->hRoot->lpstr, NULL);
// No point in marking dead since the entire control is going away
ControlFree(pTree->hheap, pTree->hRoot);
}
if (pTree->hdcBits)
{
if (pTree->hBmp)
{
SelectObject(pTree->hdcBits, pTree->hStartBmp);
DeleteObject(pTree->hBmp);
}
DeleteDC(pTree->hdcBits);
}
if (pTree->fCreatedFont && pTree->hFont)
{
DeleteObject(pTree->hFont);
}
if (pTree->hFontBold)
{
DeleteObject(pTree->hFontBold);
}
Str_Set(&pTree->pszTip, NULL);
if (pTree->pszTipA)
{
LocalFree(pTree->pszTipA);
}
TV_DeleteHotFonts(pTree);
if (pTree->hdpaWatch)
{
DPA_Destroy(pTree->hdpaWatch);
}
IncrementSearchFree(&pTree->is);
if (pTree->hTheme)
{
CloseThemeData(pTree->hTheme);
}
NearFree(pTree);
// Don't try to use this var when window is destroyed...
SetWindowInt(hwnd, 0, 0);
}
void TV_CreateToolTips(PTREE pTree);
void TV_InitThemeMetrics(PTREE pTree, HTHEME hTheme)
{
COLORREF cr;
HRESULT hr = GetThemeColor(hTheme, 0, 0, TMT_COLOR, &cr);
if (SUCCEEDED(hr))
SendMessage(pTree->ci.hwnd, TVM_SETBKCOLOR, 0, cr);
// Line color
hr = GetThemeColor(hTheme, TVP_BRANCH, 0, TMT_COLOR, &cr);
if (SUCCEEDED(hr))
SendMessage(pTree->ci.hwnd, TVM_SETLINECOLOR, 0, cr);
}
// ----------------------------------------------------------------------------
//
// Allocates space for the tree and initializes the tree's data
//
// ----------------------------------------------------------------------------
LRESULT TV_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreate)
{
HTHEME hTheme;
HRESULT hr = E_FAIL;
PTREE pTree = NearAlloc(sizeof(TREE));
if (!pTree)
return -1; // fail the create window
pTree->hheap = GetProcessHeap();
if (!TV_CreateRoot(pTree))
{
NearFree((HLOCAL)pTree);
return -1; // fail the create window
}
pTree->hdpaWatch = DPA_Create(8);
if (!pTree->hdpaWatch)
{
// No point in marking dead since the entire control is going away
ControlFree(pTree->hheap, pTree->hRoot);
NearFree((HLOCAL)pTree);
return -1; // fail the create window
}
SetWindowPtr(hwnd, 0, pTree);
CIInitialize(&pTree->ci, hwnd, lpCreate);
if (lpCreate->dwExStyle & WS_EX_RTLREADING)
pTree->ci.style |= TVS_RTLREADING;
pTree->fRedraw = TRUE;
pTree->clrim = CLR_DEFAULT;
pTree->clrText = (COLORREF)-1;
pTree->clrBkNonTheme = pTree->clrBk = (COLORREF)-1;
pTree->clrLineNonTheme = pTree->clrLine = CLR_DEFAULT;
pTree->hbrLine = g_hbrGrayText;
pTree->cxBorder = 3;
hTheme = OpenThemeData(pTree->ci.hwnd, L"TreeView");
if (hTheme)
{
TV_InitThemeMetrics(pTree, hTheme);
}
pTree->hTheme = hTheme;
pTree->hbrText = g_hbrWindowText;
// pTree->fHorz = FALSE;
// pTree->fVert = FALSE;
// pTree->fFocus = FALSE;
// pTree->fNameEditPending = FALSE;
// pTree->cxMax = 0;
// pTree->cxWnd = 0;
// pTree->cyWnd = 0;
// pTree->hTop = NULL;
// pTree->hCaret = NULL;
// pTree->hDropTarget = NULL;
// pTree->hOldDrop = NULL;
// pTree->cItems = 0;
// pTree->cShowing = 0;
pTree->cFullVisible = 1;
// pTree->hdcBits = NULL;
// pTree->hBmp = NULL;
// pTree->hbrBk = NULL;
// pTree->xPos = 0;
// pTree->cxIndent = 0; // init this for real in TV_OnSetFont()
// pTree->dwCDDepth = 0;
pTree->uMaxScrollTime = SSI_DEFAULT;
pTree->dwLastAccId = 1; // Start at 1 because 0 means self
// pTree->dwExStyle = 0;
// pTree->fInTextCallback = FALSE;
TV_OnSetFont(pTree, NULL, TRUE);
// You cannot combine TVS_HASLINES and TVS_FULLROWSELECT
// because it doesn't work
if (pTree->ci.style & TVS_HASLINES) {
if (pTree->ci.style & TVS_FULLROWSELECT) {
DebugMsg(DM_ERROR, TEXT("Cannot combine TVS_HASLINES and TVS_FULLROWSELECT"));
}
pTree->ci.style &= ~TVS_FULLROWSELECT;
}
if (!(pTree->ci.style & TVS_NOTOOLTIPS)) {
TV_CreateToolTips(pTree);
}
SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
return 0; // success
}
void TV_CreateToolTips(PTREE pTree)
{
DWORD exStyle = 0;
if(pTree->ci.style & TVS_RTLREADING)
{
exStyle |= WS_EX_RTLREADING;
}
pTree->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT | exStyle, c_szSToolTipsClass, NULL,
WS_POPUP | TTS_NOPREFIX,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
pTree->ci.hwnd, NULL, HINST_THISDLL,
NULL);
if (pTree->hwndToolTips)
{
TOOLINFO ti;
ti.cbSize = sizeof(ti);
ti.uFlags = TTF_IDISHWND | TTF_TRANSPARENT;
ti.hwnd = pTree->ci.hwnd;
ti.uId = (UINT_PTR)pTree->ci.hwnd;
ti.lpszText = LPSTR_TEXTCALLBACK;
ti.lParam = 0;
SendMessage(pTree->hwndToolTips, TTM_ADDTOOL, 0,
(LPARAM)(LPTOOLINFO)&ti);
SendMessage(pTree->hwndToolTips, WM_SETFONT, (WPARAM)pTree->hFont, (LPARAM)TRUE);
SendMessage(pTree->hwndToolTips, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM)500);
}
else
pTree->ci.style |= (TVS_NOTOOLTIPS);
}