|
|
#include "ctlspriv.h"
#include "treeview.h"
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
//
// ----------------------------------------------------------------------------
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; }
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); }
|