windows-nt/Source/XPSP1/NT/sdktools/winobj/treectl.c
2020-09-26 16:20:57 +08:00

2222 lines
68 KiB
C

/****************************************************************************/
/* */
/* TREECTL.C - */
/* */
/* Windows Directory Tree Window Proc Routines */
/* */
/****************************************************************************/
#define PUBLIC // avoid collision with shell.h
#include "winfile.h"
#include "treectl.h"
#include "lfn.h"
#include "winnet.h"
#include "wfcopy.h"
#define WS_TREESTYLE (WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | WS_HSCROLL | LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | LBS_DISABLENOSCROLL)
WORD cNodes;
// BOOL bCancelTree; .......... moved to winfile.c
VOID RectTreeItem(HWND hwndLB, register INT iItem, BOOL bFocusOn);
VOID GetTreePathIndirect(PDNODE pNode, register LPSTR szDest);
VOID GetTreePath(PDNODE pNode, register LPSTR szDest);
VOID ScanDirLevel(PDNODE pParentNode, LPSTR szPath, DWORD view);
INT InsertDirectory(HWND hwndTreeCtl, PDNODE pParentNode, WORD iParentNode, LPSTR szName, PDNODE *ppNode);
BOOL ReadDirLevel(HWND hwndTreeCtl, PDNODE pParentNode, LPSTR szPath,
WORD nLevel, INT iParentNode, DWORD dwAttribs, BOOL bFullyExpand, LPSTR szAutoExpand);
VOID FillTreeListbox(HWND hwndTreeCtl, LPSTR szDefaultDir, BOOL bFullyExpand, BOOL bDontSteal);
WORD FindItemFromPath(HWND hwndLB, LPSTR lpszPath, BOOL bReturnParent, PDNODE *ppNode);
VOID APIENTRY CheckEscapes(LPSTR);
/*--------------------------------------------------------------------------*/
/* */
/* GetTreePathIndirect() - */
/* */
/* build a complete path for a given node in the tree by recursivly */
/* traversing the tree structure */
/* */
/*--------------------------------------------------------------------------*/
VOID
GetTreePathIndirect(
PDNODE pNode,
register LPSTR szDest
)
{
register PDNODE pParent;
pParent = pNode->pParent;
if (pParent)
GetTreePathIndirect(pParent, szDest);
lstrcat(szDest, pNode->szName);
if (pParent)
lstrcat(szDest, "\\");
}
/*--------------------------------------------------------------------------*/
/* */
/* GetTreePath() - */
/* */
/* build a complete path for a given node in the tree */
/* */
/*--------------------------------------------------------------------------*/
VOID
GetTreePath(
PDNODE pNode,
register LPSTR szDest
)
{
szDest[0] = 0L;
GetTreePathIndirect(pNode, szDest);
/* Remove the last backslash (unless it is the root directory). */
if (pNode->pParent)
szDest[lstrlen(szDest)-1] = 0L;
}
/*--------------------------------------------------------------------------*/
/* */
/* ScanDirLevel() - */
/* */
/* look down to see if this node has any sub directories */
/*
/*--------------------------------------------------------------------------*/
// szPath is ANSI
VOID
ScanDirLevel(
PDNODE pParentNode,
LPSTR szPath,
DWORD view
)
{
BOOL bFound;
LFNDTA lfndta;
ENTER("ScanDirLevel");
/* Add '*.*' to the current path. */
lstrcpy(szMessage, szPath);
AddBackslash(szMessage);
lstrcat(szMessage, szStarDotStar);
/* Search for the first subdirectory on this level. */
// FixAnsiPathForDos(szMessage);
bFound = WFFindFirst(&lfndta, szMessage, ATTR_DIR | view);
while (bFound) {
/* Is this not a '.' or '..' directory? */
if ((lfndta.fd.cFileName[0] != '.') && (lfndta.fd.dwFileAttributes & ATTR_DIR)) {
pParentNode->wFlags |= TF_HASCHILDREN;
bFound = FALSE;
} else
/* Search for the next subdirectory. */
bFound = WFFindNext(&lfndta);
}
WFFindClose(&lfndta);
LEAVE("ScanDirLevel");
}
// wizzy cool recursive path compare routine
//
// p1 and p2 must be on the same level (p1->nLevels == p2->nLevels)
INT
ComparePath(
PDNODE p1,
PDNODE p2
)
{
INT ret;
if (p1 == p2) {
return 0; // equal (base case)
} else {
ret = ComparePath(p1->pParent, p2->pParent);
if (ret == 0) {
// parents are equal
ret = lstrcmp(p1->szName, p2->szName);
#if 0
{
CHAR buf[200];
wsprintf(buf, "Compare(%s, %s) -> %d\r\n", (LPSTR)p1->szName, (LPSTR)p2->szName, ret);
OutputDebugString(buf);
}
#endif
}
// not equal parents, propagate up the call tree
return ret;
}
}
INT
CompareNodes(
PDNODE p1,
PDNODE p2
)
{
PDNODE p1save, p2save;
INT ret;
ENTER("CompareNodes");
ASSERT(p1 && p2);
PRINT(BF_PARMTRACE, "IN: p1=%s", p1->szName);
PRINT(BF_PARMTRACE, "IN: p2=%s", p2->szName);
p1save = p1;
p2save = p2;
// get p1 and p2 to the same level
while (p1->nLevels > p2->nLevels)
p1 = p1->pParent;
while (p2->nLevels > p1->nLevels)
p2 = p2->pParent;
// compare those paths
ret = ComparePath(p1, p2);
if (ret == 0)
ret = (INT)p1save->nLevels - (INT)p2save->nLevels;
LEAVE("CompareNodes");
return ret;
}
//
// InsertDirectory()
//
// wizzy quick n log n binary insert code!
//
// creates and inserts a new node in the tree, this also sets
// the TF_LASTLEVELENTRY bits to mark a branch as being the last
// for a given level as well as marking parents with
// TF_HASCHILDREN | TF_EXPANDED to indicate they have been expanded
// and have children.
//
// Returns iNode and fills ppNode with pNode.
//
INT
InsertDirectory(
HWND hwndTreeCtl,
PDNODE pParentNode,
WORD iParentNode,
LPSTR szName,
PDNODE *ppNode
)
{
WORD len, x;
PDNODE pNode, pMid;
HWND hwndLB;
INT iMin;
INT iMax;
INT iMid;
ENTER("InsertDirectory");
PRINT(BF_PARMTRACE, "IN: pParentNode=%lx", pParentNode);
PRINT(BF_PARMTRACE, "IN: iParentNode=%d", iParentNode);
PRINT(BF_PARMTRACE, "IN: szName=%s", szName);
len = (WORD)lstrlen(szName);
pNode = (PDNODE)LocalAlloc(LPTR, sizeof(DNODE)+len);
if (!pNode) {
if (ppNode) {
*ppNode = NULL;
}
return 0;
}
pNode->pParent = pParentNode;
pNode->nLevels = pParentNode ? (pParentNode->nLevels + (BYTE)1) : (BYTE)0;
pNode->wFlags = (BYTE)NULL;
pNode->iNetType = -1;
if (IsLFN(szName)) {
pNode->wFlags |= TF_LFN;
}
lstrcpy(pNode->szName, szName);
if (pParentNode)
pParentNode->wFlags |= TF_HASCHILDREN | TF_EXPANDED; // mark the parent
hwndLB = GetDlgItem(hwndTreeCtl, IDCW_TREELISTBOX);
// computing the real text extent is too slow so we aproximate
// with the following (note, we don't keep this on a per tree
// basis so it is kinda bogus anyway)
x = (WORD)(len + 2 * pNode->nLevels) * (WORD)dxText;
if (x > xTreeMax) {
xTreeMax = x;
}
iMax = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
if (iMax > 0) {
// do a binary insert
iMin = iParentNode + 1;
iMax--; // last index
do {
iMid = (iMax + iMin) / 2;
SendMessage(hwndLB, LB_GETTEXT, iMid, (LPARAM)&pMid);
if (CompareNodes(pNode, pMid) > 0)
iMin = iMid + 1;
else
iMax = iMid - 1;
} while (iMax > iMin);
SendMessage(hwndLB, LB_GETTEXT, iMax, (LPARAM)&pMid);
if (CompareNodes(pNode, pMid) > 0)
iMax++; // insert after this one
}
// now reset the TF_LASTLEVEL flags as appropriate
// look for the first guy on our level above us and turn off
// his TF_LASTLEVELENTRY flag so he draws a line down to us
iMid = iMax - 1;
while (iMid >= 0) {
SendMessage(hwndLB, LB_GETTEXT, iMid--, (LPARAM)&pMid);
if (pMid->nLevels == pNode->nLevels) {
pMid->wFlags &= ~TF_LASTLEVELENTRY;
break;
} else if (pMid->nLevels < pNode->nLevels)
break;
}
// if no one below me or the level of the guy below is less, then
// this is the last entry for this level
if (((INT)SendMessage(hwndLB, LB_GETTEXT, iMax, (LPARAM)&pMid) == LB_ERR) ||
(pMid->nLevels < pNode->nLevels))
pNode->wFlags |= TF_LASTLEVELENTRY;
SendMessage(hwndLB, LB_INSERTSTRING, iMax, (LPARAM)pNode);
if (ppNode) {
*ppNode = pNode;
}
LEAVE("InsertDirectory");
return iMax;
}
// this yeilds control to other apps and allows us to process
// messages and user input. to avoid overrunning the stack
// from multiple tree reads being initiated at the same time
// we check how much space we have on the stack before we yield
extern WORD end; // C compiler end of static data symbol
extern WORD pStackTop;
WORD
StackAvail(VOID)
{
#ifdef LATER
_asm mov ax,sp
_asm sub ax,pStackTop
if (0) return 0; // get rid of warning, optimized out
#endif
return 0x7fff; // Hack. shouldn't really matter. StackAvail in NT is a NOP
}
VOID
APIENTRY
wfYield()
{
MSG msg;
#ifdef LATER
WORD free_stack;
free_stack = StackAvail();
#endif
#if 0
{
CHAR buf[30];
wsprintf(buf, "free stack: %d\r\n", free_stack);
OutputDebugString(buf);
}
#endif
#if LATER
if (free_stack < 1024*4) {
CHAR buf[40];
wsprintf(buf, "not enough stack %d\r\n", free_stack);
OutputDebugString(buf);
return;
}
#endif
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (!TranslateMDISysAccel(hwndMDIClient, &msg) &&
(!hwndFrame || !TranslateAccelerator(hwndFrame, hAccel, &msg))) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
// INT iReadLevel = 0; ...............Moved to winfile.c
//--------------------------------------------------------------------------
//
// ReadDirLevel() -
//
// this does a depth first search of the dir tree. note, a bredth
// first implementation did not perform any better.
//
// szPath a directory path that MUST EXIST long enough
// to hold the full path to the largest directory
// that will be found (MAXPATHLEN). this is an
// ANSI string. (ie C:\ and C:\FOO are valid)
// nLevel level in the tree
// iParentNode index of parent node
// dwAttribs attributes to filter with
// bFullyExpand TRUE means expand this node fully
// szAutoExpand list of directories to autoexpand ANSI
// (eg. for "C:\foo\bar\stuff"
// "foo" NULL "bar" NULL "stuff" NULL NULL)
//
// returns:
// TRUE tree read sucessful
// FALSE user abort or bogus tree read
//--------------------------------------------------------------------------
BOOL
ReadDirLevel(
HWND hwndTreeCtl,
PDNODE pParentNode,
LPSTR szPath,
WORD nLevel,
INT iParentNode,
DWORD dwAttribs,
BOOL bFullyExpand,
LPSTR szAutoExpand
)
{
LPSTR szEndPath;
LFNDTA lfndta;
INT iNode;
BOOL bFound;
PDNODE pNode;
BOOL bAutoExpand;
BOOL bResult = TRUE;
WORD view;
HWND hwndParent;
HWND hwndDir;
HANDLE hDTA;
LPMYDTA lpmydta;
INT count;
RECT rc;
ENTER("ReadDirLevel");
PRINT(BF_PARMTRACE, "IN: szPath=%s", szPath);
PRINT(BF_PARMTRACE, "IN: nLevel=%d", (LPSTR)nLevel);
PRINT(BF_PARMTRACE, "IN: bFullyExpand=%d", IntToPtr(bFullyExpand));
PRINT(BF_PARMTRACE, "IN: szAutoExpand=%s", szAutoExpand);
if (StackAvail() < 1024*2)
return(TRUE);
hwndParent = GetParent(hwndTreeCtl);
view = (WORD)GetWindowLong(hwndParent, GWL_VIEW);
// we optimize the tree read if we are not adding pluses and
// we find a directory window that already has read all the
// directories for the path we are about to search. in this
// case we look through the DTA structure in the dir window
// to get all the directories (instead of calling FindFirst/FindNext).
// in this case we have to disable yielding since the user could
// potentialy close the dir window that we are reading, or change
// directory.
hDTA = NULL;
if (!(view & VIEW_PLUSES)) {
if ((hwndDir = HasDirWindow(hwndParent)) &&
(GetWindowLong(hwndParent, GWL_ATTRIBS) & ATTR_DIR)) {
SendMessage(hwndDir, FS_GETDIRECTORY, sizeof(szMessage), (LPARAM)szMessage);
StripBackslash(szMessage);
if (!lstrcmpi(szMessage, szPath)) {
SendMessage(hwndDir, FS_GETFILESPEC, sizeof(szMessage), (LPARAM)szMessage);
if (!lstrcmp(szMessage, szStarDotStar)) {
hDTA = (HANDLE)GetWindowLongPtr(hwndDir, GWLP_HDTA);
lpmydta = (LPMYDTA)LocalLock(hDTA);
count = (INT)lpmydta->my_nFileSizeLow; // holds number of entries, NOT size.
}
}
}
}
SetWindowLong(hwndTreeCtl, GWL_READLEVEL, GetWindowLong(hwndTreeCtl, GWL_READLEVEL) + 1);
iReadLevel++; // global for menu code
szEndPath = (LPSTR)(szPath + lstrlen(szPath));
/* Add '\*.*' to the current path. */
AddBackslash(szPath);
lstrcat(szPath, szStarDotStar);
if (hDTA) {
// steal the entry from the dir window
lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
// search for any "real" directories
while (count > 0 && (!(lpmydta->my_dwAttrs & ATTR_DIR) || (lpmydta->my_dwAttrs & ATTR_PARENT))) {
lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
count--;
}
if (count > 0) {
bFound = TRUE;
memcpy(&(lfndta.fd.dwFileAttributes), &(lpmydta->my_dwAttrs), IMPORTANT_DTA_SIZE);
lstrcpy(lfndta.fd.cFileName, lpmydta->my_cFileName);
} else
bFound = FALSE;
} else {
// get first file from DOS
lstrcpy(szMessage, szPath);
FixAnsiPathForDos(szMessage);
bFound = WFFindFirst(&lfndta, szMessage, dwAttribs);
}
// for net drive case where we can't actually see what is in these
// direcotries we will build the tree automatically
if (!bFound && *szAutoExpand) {
LPSTR p;
p = szAutoExpand;
szAutoExpand += lstrlen(szAutoExpand) + 1;
iNode = InsertDirectory(hwndTreeCtl, pParentNode, (WORD)iParentNode, p, &pNode);
pParentNode->wFlags |= TF_DISABLED;
/* Construct the path to this new subdirectory. */
*szEndPath = 0; // remove old stuff
AddBackslash(szPath);
lstrcat(szPath, p);
if (pNode)
ReadDirLevel(hwndTreeCtl, pNode, szPath, (WORD)(nLevel+1), iNode, dwAttribs, bFullyExpand, szAutoExpand);
}
while (bFound) {
wfYield();
if (bCancelTree) {
bResult = FALSE;
if (bCancelTree == 2)
PostMessage(hwndFrame, WM_COMMAND, IDM_EXIT, 0L);
goto DONE;
}
/* Is this not a '.' or '..' directory? */
if ((lfndta.fd.cFileName[0] != '.') && (lfndta.fd.dwFileAttributes & ATTR_DIR)) {
if (!hDTA)
OemToAnsi(lfndta.fd.cFileName, lfndta.fd.cFileName);
// we will try to auto expand this node if it matches
if (*szAutoExpand && !lstrcmpi(szAutoExpand, lfndta.fd.cFileName)) {
bAutoExpand = TRUE;
szAutoExpand += lstrlen(szAutoExpand) + 1;
} else {
bAutoExpand = FALSE;
}
iNode = InsertDirectory(hwndTreeCtl, pParentNode, (WORD)iParentNode, lfndta.fd.cFileName, &pNode);
if (bStatusBar && ((cNodes % 7) == 0)) {
// make sure we are the active window before we
// update the status bar
if (hwndParent == (HWND)SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L)) {
wsprintf(szStatusTree, szDirsRead, cNodes);
// stomp over the status bar!
GetClientRect(hwndFrame, &rc);
rc.top = rc.bottom - dyStatus;
InvalidateRect(hwndFrame, &rc, FALSE);
// force the paint because we don't yield
UpdateWindow(hwndFrame);
}
}
cNodes++;
/* Construct the path to this new subdirectory. */
*szEndPath = 0L;
AddBackslash(szPath);
lstrcat(szPath, lfndta.fd.cFileName); // cFileName is ANSI now
// either recurse or add pluses
if (pNode) {
if (bFullyExpand || bAutoExpand) {
if (!ReadDirLevel(hwndTreeCtl, pNode, szPath, (WORD)(nLevel+1), iNode, dwAttribs, bFullyExpand, szAutoExpand)) {
bResult = FALSE;
goto DONE;
}
} else if (view & VIEW_PLUSES) {
ScanDirLevel(pNode, szPath, dwAttribs & ATTR_HS);
}
}
}
if (hDTA) { // short cut, steal data from dir window
count--;
lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
while (count > 0 && (!(lpmydta->my_dwAttrs & ATTR_DIR) || (lpmydta->my_dwAttrs & ATTR_PARENT))) {
lpmydta = GETDTAPTR(lpmydta, lpmydta->wSize);
count--;
}
if (count > 0) {
bFound = TRUE;
memcpy(&(lfndta.fd.dwFileAttributes), &(lpmydta->my_dwAttrs), IMPORTANT_DTA_SIZE);
lstrcpy(lfndta.fd.cFileName, lpmydta->my_cFileName);
} else
bFound = FALSE;
} else {
bFound = WFFindNext(&lfndta); // get it from dos
}
}
*szEndPath = 0L; // clean off any stuff we left on the end of the path
DONE:
if (!hDTA) {
WFFindClose(&lfndta);
} else {
LocalUnlock(hDTA);
}
SetWindowLong(hwndTreeCtl, GWL_READLEVEL, GetWindowLong(hwndTreeCtl, GWL_READLEVEL) - 1);
iReadLevel--;
LEAVE("ReadDirLevel");
return bResult;
}
// this is used by StealTreeData() to avoid alias problems where
// the nodes in one tree point to parents in the other tree.
// basically, as we are duplicating the tree data structure we
// have to find the parent node that coorisponds with the parent
// of the tree we are copying from in the tree that we are building.
// since the tree is build in order we run up the listbox, looking
// for the parent (matched by it's level being one smaller than
// the level of the node being inserted). when we find that we
// return the pointer to that node.
PDNODE
FindParent(
INT iLevelParent,
INT iStartInd,
HWND hwndLB
)
{
PDNODE pNode;
while (TRUE) {
if (SendMessage(hwndLB, LB_GETTEXT, iStartInd, (LPARAM)&pNode) == LB_ERR)
return NULL;
if (pNode->nLevels == (BYTE)iLevelParent) {
SendMessage(hwndLB, LB_GETTEXT, iStartInd, (LPARAM)&pNode);
return pNode;
}
iStartInd--;
}
}
BOOL
StealTreeData(
HWND hwndTC,
HWND hwndLB,
LPSTR szDir
)
{
HWND hwndSrc, hwndT;
CHAR szSrc[MAXPATHLEN];
WORD wView;
DWORD dwAttribs;
ENTER("StealTreeData");
// we need to match on these attributes as well as the name
wView = (WORD)(GetWindowLong(GetParent(hwndTC), GWL_VIEW) & VIEW_PLUSES);
dwAttribs = (DWORD)GetWindowLong(GetParent(hwndTC), GWL_ATTRIBS) & ATTR_HS;
// get the dir of this new window for compare below
for (hwndSrc = GetWindow(hwndMDIClient, GW_CHILD); hwndSrc;
hwndSrc = GetWindow(hwndSrc, GW_HWNDNEXT)) {
// avoid finding ourselves, make sure has a tree
// and make sure the tree attributes match
if ((hwndT = HasTreeWindow(hwndSrc)) &&
(hwndT != hwndTC) &&
!GetWindowLong(hwndT, GWL_READLEVEL) &&
(wView == (WORD)(GetWindowLong(hwndSrc, GWL_VIEW) & VIEW_PLUSES)) &&
(dwAttribs == (DWORD)(GetWindowLong(hwndSrc, GWL_ATTRIBS) & ATTR_HS))) {
SendMessage(hwndSrc, FS_GETDIRECTORY, sizeof(szSrc), (LPARAM)szSrc);
StripBackslash(szSrc);
if (!lstrcmpi(szDir, szSrc)) // are they the same?
break; // yes, do stuff below
}
}
if (hwndSrc) {
HWND hwndLBSrc;
PDNODE pNode, pNewNode, pLastParent;
INT i;
hwndLBSrc = GetDlgItem(hwndT, IDCW_TREELISTBOX);
// don't seal from a tree that hasn't been read yet!
if ((INT)SendMessage(hwndLBSrc, LB_GETCOUNT, 0, 0L) == 0) {
LEAVE("StealTreeData");
return FALSE;
}
pLastParent = NULL;
for (i = 0; SendMessage(hwndLBSrc, LB_GETTEXT, i, (LPARAM)&pNode) != LB_ERR; i++) {
if (pNewNode = (PDNODE)LocalAlloc(LPTR, sizeof(DNODE)+lstrlen(pNode->szName))) {
*pNewNode = *pNode; // dup the node
lstrcpy(pNewNode->szName, pNode->szName); // and the name
// accelerate the case where we are on the same level to avoid
// slow linear search!
if (pLastParent && pLastParent->nLevels == (pNode->nLevels - (BYTE)1)) {
pNewNode->pParent = pLastParent;
} else {
pNewNode->pParent = pLastParent = FindParent(pNode->nLevels-1, i-1, hwndLB);
}
PRINT(BF_PARMTRACE, "(stolen)Inserting...0x%lx", pNewNode);
PRINT(BF_PARMTRACE, " at %d", IntToPtr(i));
SendMessage(hwndLB, LB_INSERTSTRING, i, (LPARAM)pNewNode);
ASSERT((PDNODE)SendMessage(hwndLB, LB_GETITEMDATA, i, 0L) == pNewNode);
}
}
LEAVE("StealTreeData");
return TRUE; // successful steal
}
LEAVE("StealTreeData");
return FALSE;
}
VOID
FreeAllTreeData(
HWND hwndLB
)
{
INT nIndex;
PDNODE pNode;
ENTER("FreeAllTreeData");
// Free up the old tree (if any)
nIndex = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
while (--nIndex >= 0) {
SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&pNode);
LocalFree((HANDLE)pNode);
}
SendMessage(hwndLB, LB_RESETCONTENT, 0, 0L);
LEAVE("FreeAllTreeData");
}
/*--------------------------------------------------------------------------*/
/* */
/* FillTreeListbox() - */
/* */
/*--------------------------------------------------------------------------*/
// szDefaultDir is ANSI
VOID
FillTreeListbox(
HWND hwndTC,
LPSTR szDefaultDir,
BOOL bFullyExpand,
BOOL bDontSteal
)
{
PDNODE pNode;
INT iNode;
DWORD dwAttribs;
CHAR szTemp[MAXPATHLEN];
CHAR szExpand[MAXPATHLEN];
LPSTR p;
HWND hwndLB;
ENTER("FillTreeListbox");
hwndLB = GetDlgItem(hwndTC, IDCW_TREELISTBOX);
FreeAllTreeData(hwndLB);
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
bDontSteal = TRUE; // Force recalc for now
if (bDontSteal || bFullyExpand || !StealTreeData(hwndTC, hwndLB, szDefaultDir)) {
wsprintf(szTemp, "\\", DRIVEID(szDefaultDir) + 'A');
iNode = InsertDirectory(hwndTC, NULL, 0, szTemp, &pNode);
if (pNode) {
dwAttribs = ATTR_DIR | (GetWindowLong(GetParent(hwndTC), GWL_ATTRIBS) & ATTR_HS);
cNodes = 0;
bCancelTree = FALSE;
if (szDefaultDir) {
lstrcpy(szExpand, szDefaultDir+3); // skip "X:\"
p = szExpand;
while (*p) { // null out all slashes
while (*p && *p != '\\')
p = AnsiNext(p);
if (*p)
*p++ = 0L;
}
p++;
*p = 0L; // double null terminated
} else
*szExpand = 0;
if (!ReadDirLevel(hwndTC, pNode, szTemp, 1, 0, dwAttribs, bFullyExpand, szExpand)) {
lFreeSpace = -2L;
}
}
}
SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
if (szDefaultDir) {
FindItemFromPath(hwndLB, szDefaultDir, FALSE, &pNode);
}
SendMessage(hwndLB, LB_SELECTSTRING, -1, (LPARAM)pNode);
UpdateStatus(GetParent(hwndTC)); // Redraw the Status Bar
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hwndLB, NULL, TRUE);
UpdateWindow(hwndLB); // make this look a bit better
LEAVE("FillTreeListbox");
}
//
// FindItemFromPath()
//
// find the PDNODE and LBIndex for a given path
//
// in:
// hwndLB listbox of tree
// lpszPath path to search for (ANSI)
// bReturnParent TRUE if you want the parent, not the node
//
//
// returns:
// listbox index (0xFFFF if not found)
// *ppNode is filled with pNode of node, or pNode of parent if bReturnParent is TRUE
//
WORD
FindItemFromPath(
HWND hwndLB,
LPSTR lpszPath,
BOOL bReturnParent,
PDNODE *ppNode
)
{
register WORD i;
register LPSTR p;
PDNODE pNode;
PDNODE pPreviousNode;
CHAR szElement[1+MAXFILENAMELEN+1];
ENTER("FindItemFromPath");
if (lstrlen(lpszPath) < 3) {
LEAVE("FindItemFromPath");
return -1;
}
if (IsDBCSLeadByte( lpszPath[0] ) || lpszPath[1] != ':') {
LEAVE("FindItemFromPath");
return -1;
}
i = 0;
pPreviousNode = NULL;
while (*lpszPath) {
/* NULL out szElement[1] so the backslash hack isn't repeated with
* a first level directory of length 1.
*/
szElement[1] = 0L;
/* Copy the next section of the path into 'szElement' */
p = szElement;
while (*lpszPath && *lpszPath != '\\') {
*p++ = *lpszPath;
if (IsDBCSLeadByte( *lpszPath ))
*p++ = lpszPath[1]; // copy 2nd byte of DBCS char.
lpszPath = AnsiNext( lpszPath );
}
/* Add a backslash for the Root directory. */
if ( !IsDBCSLeadByte( szElement[0] ) && szElement[1] == ':' )
*p++ = '\\';
/* NULL terminate 'szElement' */
*p = 0L;
/* Skip over the path's next Backslash. */
if (*lpszPath)
lpszPath = AnsiNext(lpszPath);
else if (bReturnParent) {
/* We're at the end of a path which includes a filename. Return
* the previously found parent.
*/
if (ppNode) {
*ppNode = pPreviousNode;
}
LEAVE("FindItemFromPath");
return i;
}
while (TRUE) {
/* Out of LB items? Not found. */
if (SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode) == LB_ERR)
return -1;
if (pNode->pParent == pPreviousNode) {
if (!lstrcmpi(szElement, pNode->szName)) {
/* We've found the element... */
pPreviousNode = pNode;
break;
}
}
i++;
}
}
if (ppNode) {
*ppNode = pPreviousNode;
}
LEAVE("FindItemFromPath");
return i;
}
/*--------------------------------------------------------------------------*/
/* */
/* RectTreeItem() - */
/* */
/*--------------------------------------------------------------------------*/
VOID
RectTreeItem(
HWND hwndLB,
INT iItem,
BOOL bFocusOn
)
{
INT dx;
INT len;
HDC hdc;
RECT rc;
RECT rcClip;
BOOL bSel;
WORD wColor;
PDNODE pNode;
HBRUSH hBrush;
HFONT hOld;
CHAR szPath[MAXPATHLEN];
ENTER("RectTreeItem");
if (iItem == -1) {
LEAVE("RectTreeItem");
return;
}
/* Are we over ourselves? (i.e. a selected item in the source listbox) */
bSel = (BOOL)SendMessage(hwndLB, LB_GETSEL, iItem, 0L);
if (bSel && (hwndDragging == hwndLB)) {
LEAVE("RectTreeItem");
return;
}
SendMessage(hwndLB, LB_GETTEXT, iItem, (LPARAM)&pNode);
SendMessage(hwndLB, LB_GETITEMRECT, iItem, (LPARAM)&rc);
hdc = GetDC(hwndLB);
len = lstrlen(pNode->szName);
lstrcpy(szPath, pNode->szName);
if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
AnsiLower(szPath);
hOld = SelectObject(hdc, hFont);
MGetTextExtent(hdc, szPath, len, &dx, NULL);
dx += dyBorder;
if (hOld)
SelectObject(hdc, hOld);
rc.left = pNode->nLevels * dxText * 2;
rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
GetClientRect(hwndLB, &rcClip);
IntersectRect(&rc, &rc, &rcClip);
if (bFocusOn) {
if (bSel) {
wColor = COLOR_WINDOW;
InflateRect(&rc, -dyBorder, -dyBorder);
} else
wColor = COLOR_WINDOWFRAME;
if (hBrush = CreateSolidBrush(GetSysColor(wColor))) {
FrameRect(hdc, &rc, hBrush);
DeleteObject(hBrush);
}
} else {
InvalidateRect(hwndLB, &rc, TRUE);
UpdateWindow(hwndLB);
}
ReleaseDC(hwndLB, hdc);
LEAVE("RectTreeItem");
}
// return the drive of the first window to respond to the FS_GETDRIVE
// message. this usually starts from the source or dest of a drop
// and travels up until we find a drive or hit the MDI client
INT
APIENTRY
GetDrive(
HWND hwnd,
POINT pt
)
{
CHAR chDrive;
chDrive = 0L;
while (hwnd && (hwnd != hwndMDIClient)) {
chDrive = (CHAR)SendMessage(hwnd, FS_GETDRIVE, 0, MAKELONG((WORD)pt.x, (WORD)pt.y));
if (chDrive)
return chDrive;
hwnd = GetParent(hwnd); // try the next higher up
}
return 0;
}
BOOL
IsNetPath(
PDNODE pNode
)
{
CHAR szPath[MAXPATHLEN];
INT i;
if (pNode->iNetType == -1) {
GetTreePath(pNode, szPath);
if (WNetGetDirectoryType((LPSTR)szPath, (LPDWORD)&i, TRUE) == WN_SUCCESS)
pNode->iNetType = i;
else
pNode->iNetType = 0;
}
return pNode->iNetType;
}
VOID
TCWP_DrawItem(
LPDRAWITEMSTRUCT lpLBItem,
HWND hwndLB,
HWND hWnd
)
{
INT x, y, dx, dy;
INT nLevel;
HDC hdc;
WORD len;
RECT rc;
BOOL bHasFocus, bDrawSelected;
PDNODE pNode, pNTemp;
DWORD rgbText;
DWORD rgbBackground;
HBRUSH hBrush, hOld;
INT iBitmap;
WORD view;
CHAR szPath[MAXPATHLEN];
ENTER("TCWP_DrawItem");
if (lpLBItem->itemID == (DWORD)-1) {
return;
}
hdc = lpLBItem->hDC;
pNode = (PDNODE)lpLBItem->itemData;
lstrcpy(szPath, pNode->szName);
if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
AnsiLower(szPath);
len = (WORD)lstrlen(szPath);
MGetTextExtent(hdc, szPath, len, &dx, NULL);
dx += dyBorder;
rc = lpLBItem->rcItem;
rc.left = pNode->nLevels * dxText * 2;
rc.right = rc.left + dxFolder + dx + 4 * dyBorderx2;
if (lpLBItem->itemAction & (ODA_DRAWENTIRE | ODA_SELECT)) {
// draw the branches of the tree first
nLevel = pNode->nLevels;
x = (nLevel * dxText * 2) - dxText + dyBorderx2;
dy = lpLBItem->rcItem.bottom - lpLBItem->rcItem.top;
y = lpLBItem->rcItem.top + (dy/2);
if (hBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT))) {
hOld = SelectObject(hdc, hBrush);
if (pNode->pParent) {
/* Draw the horizontal line over to the (possible) folder. */
PatBlt(hdc, x, y, dyText, dyBorder, PATCOPY);
/* Draw the top part of the vertical line. */
PatBlt(hdc, x, lpLBItem->rcItem.top, dyBorder, dy/2, PATCOPY);
/* If not the end of a node, draw the bottom part... */
if (!(pNode->wFlags & TF_LASTLEVELENTRY))
PatBlt(hdc, x, y+dyBorder, dyBorder, dy/2, PATCOPY);
/* Draw the verticals on the left connecting other nodes. */
pNTemp = pNode->pParent;
while (pNTemp) {
nLevel--;
if (!(pNTemp->wFlags & TF_LASTLEVELENTRY))
PatBlt(hdc, (nLevel * dxText * 2) - dxText + dyBorderx2,
lpLBItem->rcItem.top, dyBorder,dy, PATCOPY);
pNTemp = pNTemp->pParent;
}
}
if (hOld)
SelectObject(hdc, hOld);
DeleteObject(hBrush);
}
bDrawSelected = (lpLBItem->itemState & ODS_SELECTED);
bHasFocus = (GetFocus() == lpLBItem->hwndItem);
// draw text with the proper background or rect
if (bHasFocus && bDrawSelected) {
rgbText = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
rgbBackground = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
}
ExtTextOut(hdc, x + dxText + dxFolder + 2 * dyBorderx2,
y-(dyText/2), ETO_OPAQUE, &rc,
szPath, len, NULL);
// draw the bitmaps as needed
// HACK: Don't draw the bitmap when moving
if (fShowSourceBitmaps || (hwndDragging != hwndLB) || !bDrawSelected) {
// Blt the proper folder bitmap
view = (WORD)GetWindowLong(GetParent(hWnd), GWL_VIEW);
if (bNetAdmin && IsNetPath(pNode)) {
// we need this bitmap from lisa
if (bDrawSelected)
iBitmap = BM_IND_OPENDFS;
else
iBitmap = BM_IND_CLOSEDFS;
} else if (!(view & VIEW_PLUSES) || !(pNode->wFlags & TF_HASCHILDREN)) {
if (bDrawSelected)
iBitmap = BM_IND_OPEN;
else
iBitmap = BM_IND_CLOSE;
} else {
if (pNode->wFlags & TF_EXPANDED) {
if (bDrawSelected)
iBitmap = BM_IND_OPENMINUS;
else
iBitmap = BM_IND_CLOSEMINUS;
} else {
if (bDrawSelected)
iBitmap = BM_IND_OPENPLUS;
else
iBitmap = BM_IND_CLOSEPLUS;
}
}
BitBlt(hdc, x + dxText + dyBorder, y-(dyFolder/2), dxFolder, dyFolder,
hdcMem, iBitmap * dxFolder, (bHasFocus && bDrawSelected) ? dyFolder : 0, SRCCOPY);
}
// restore text stuff and draw rect as required
if (bDrawSelected) {
if (bHasFocus) {
SetTextColor(hdc, rgbText);
SetBkColor(hdc, rgbBackground);
} else {
HBRUSH hbr;
if (hbr = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT))) {
FrameRect(hdc, &rc, hbr);
DeleteObject(hbr);
}
}
}
}
if (lpLBItem->itemAction == ODA_FOCUS)
DrawFocusRect(hdc, &rc);
}
/* A helper for both ExpandLevel and TreeCtlWndProc.TC_COLLAPSELEVEL.
* Code moved from TreeCtlWndProc to be shared. EDH 13 Oct 91
*/
VOID
CollapseLevel(
HWND hwndLB,
PDNODE pNode,
INT nIndex
)
{
DWORD_PTR dwTemp;
PDNODE pParentNode = pNode;
INT nIndexT = nIndex;
/* Disable redrawing early. */
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
nIndexT++;
/* Remove all subdirectories. */
while (TRUE) {
/* Make sure we don't run off the end of the listbox. */
if (SendMessage(hwndLB, LB_GETTEXT, nIndexT, (LPARAM)&dwTemp) == LB_ERR)
break;
pNode = (PDNODE)dwTemp;
if (pNode->nLevels <= pParentNode->nLevels)
break;
LocalFree((HANDLE)pNode);
SendMessage(hwndLB, LB_DELETESTRING, nIndexT, 0L);
}
pParentNode->wFlags &= ~TF_EXPANDED;
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hwndLB, NULL, TRUE);
}
VOID
ExpandLevel(
HWND hWnd,
WORD wParam,
INT nIndex,
PSTR szPath
)
{
HWND hwndLB;
DWORD_PTR dwTemp;
PDNODE pNode;
INT iNumExpanded;
INT iBottomIndex;
INT iTopIndex;
INT iNewTopIndex;
INT iExpandInView;
INT iCurrentIndex;
RECT rc;
if (GetWindowLong(hWnd, GWL_READLEVEL))
return;
hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
if (nIndex == -1)
if ((nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)) == LB_ERR)
return;
SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&dwTemp);
pNode = (PDNODE)dwTemp;
// collapse the current contents so we avoid doubling existing "plus" dirs
if (pNode->wFlags & TF_EXPANDED) {
if (wParam)
CollapseLevel(hwndLB, pNode, nIndex);
else
return;
}
GetTreePath(pNode, szPath);
StripBackslash(szPath); // remove the slash
cNodes = 0;
bCancelTree = FALSE;
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L); // Disable redrawing.
iCurrentIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
iTopIndex = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
GetClientRect(hwndLB, &rc);
iBottomIndex = iTopIndex + (rc.bottom+1) / dyFileName;
if (IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
ReadDirLevel(hWnd, pNode, szPath, (WORD)(pNode->nLevels + 1), nIndex,
(DWORD)(ATTR_DIR | (GetWindowLong(GetParent(hWnd), GWL_ATTRIBS) & ATTR_HS)),
(BOOL)wParam, szNULL);
// this is how many will be in view
iExpandInView = (iBottomIndex - (INT)iCurrentIndex);
iNumExpanded = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - iNumExpanded;
if (iNumExpanded >= iExpandInView) {
iNewTopIndex = min((INT)iCurrentIndex, iTopIndex + iNumExpanded - iExpandInView + 1);
SendMessage(hwndLB, LB_SETTOPINDEX, (WORD)iNewTopIndex, 0L);
}
SendMessage(hwndLB, LB_SETHORIZONTALEXTENT, xTreeMax, 0L);
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
if (iNumExpanded)
InvalidateRect(hwndLB, NULL, TRUE);
// Redraw the Status Bar
UpdateStatus(GetParent(hWnd));
}
/*--------------------------------------------------------------------------*/
/* */
/* TreeControlWndProc() - */
/* */
/*--------------------------------------------------------------------------*/
/* WndProc for the directory tree control. */
INT_PTR
APIENTRY
TreeControlWndProc(
register HWND hWnd,
UINT wMsg,
WPARAM wParam,
LPARAM lParam
)
{
WORD iSel;
INT i, j;
WPARAM nIndex;
DWORD dwTemp;
PDNODE pNode, pNodeNext;
HWND hwndLB;
CHAR szPath[MAXPATHLEN];
STKCHK();
hwndLB = GetDlgItem(hWnd, IDCW_TREELISTBOX);
switch (wMsg) {
case FS_GETDRIVE:
MSG("TreeControlWndProc", "FS_GETDRIVE");
return (GetWindowLong(GetParent(hWnd), GWL_TYPE) + 'A');
case TC_COLLAPSELEVEL:
MSG("TreeControlWndProc", "TC_COLLAPSELEVEL");
{
PDNODE pParentNode;
if (wParam)
nIndex = wParam;
else {
nIndex = SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
if (nIndex == LB_ERR)
break;
}
SendMessage(hwndLB, LB_GETTEXT, nIndex, (LPARAM)&pParentNode);
// short circuit if we are already in this state
if (!(pParentNode->wFlags & TF_EXPANDED))
break;
CollapseLevel(hwndLB, pParentNode, (int)nIndex);
break;
}
case TC_EXPANDLEVEL:
MSG("TreeControlWndProc", "TC_EXPANDLEVEL");
ExpandLevel(hWnd, (WORD)wParam, (INT)-1, szPath);
break;
case TC_TOGGLELEVEL:
MSG("TreeControlWndProc", "TC_TOGGLELEVEL");
// don't do anything while the tree is being built
if (GetWindowLong(hWnd, GWL_READLEVEL))
return 1;
SendMessage(hwndLB, LB_GETTEXT, (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L), (LPARAM)&pNode);
if (pNode->wFlags & TF_EXPANDED)
wMsg = TC_COLLAPSELEVEL;
else
wMsg = TC_EXPANDLEVEL;
SendMessage(hWnd, wMsg, FALSE, 0L);
break;
case TC_GETDIR:
// get a full path for a particular dir
// wParam is the listbox index of path to get
// lParam LOWORD is PSTR to buffer to fill in
MSG("TreeControlWndProc", "TC_GETDIR");
SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
GetTreePath(pNode, (LPSTR)lParam);
break;
case TC_SETDIRECTORY:
MSG("TreeControlWndProc", "TC_SETDIRECTORY");
// set the selection in the tree to that for a given path
{
INT i;
i = (INT)FindItemFromPath(hwndLB, (LPSTR)lParam, wParam ? TRUE : FALSE, NULL);
if (i != -1)
SendMessage(hwndLB, LB_SETCURSEL, i, 0L);
break;
}
case TC_SETDRIVE:
#define fFullyExpand LOBYTE(wParam)
#define fDontSteal HIBYTE(wParam)
#define szDir (LPSTR)lParam // NULL -> default == window text.
MSG("TreeControlWndProc", "TC_SETDRIVE");
{
RECT rc;
if (GetWindowLong(hWnd, GWL_READLEVEL))
break;
// is the drive/dir specified?
if (szDir) {
lstrcpy(szPath, szDir); // yes, use it
} else {
SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath); // no, use current
StripBackslash(szPath);
}
AnsiUpperBuff(szPath, 1); // make sure
SetWindowLong(GetParent(hWnd), GWL_TYPE, 2);
// resize for new vol label
GetClientRect(GetParent(hWnd), &rc);
SendMessage(GetParent(hWnd), WM_SIZE, SIZENOMDICRAP, MAKELONG(rc.right, rc.bottom));
// ensure the disk is available if the whole dir structure is
// to be expanded
if (!fFullyExpand || IsTheDiskReallyThere(hWnd, szPath, FUNC_EXPAND))
FillTreeListbox(hWnd, szPath, fFullyExpand, fDontSteal);
// and force the dir half to update with a fake SELCHANGE message
SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(IDCW_TREELISTBOX, hWnd, LBN_SELCHANGE));
break;
#undef fFullyExpand
#undef fDontSteal
#undef szDir
}
case WM_CHARTOITEM:
MSG("TreeControlWndProc", "WM_CHARTOITEM");
{
WORD w;
CHAR szB[2];
INT cItems;
CHAR ch;
PDNODE pNode;
if (GET_WM_CHARTOITEM_CHAR(wParam, lParam) == '\\') // backslash means the root
return 0L;
cItems = (INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L);
i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
ch = GET_WM_CHARTOITEM_CHAR(wParam, lParam);
if (i < 0 || ch <= ' ') // filter all other control chars
return -2L;
szB[1] = 0L;
ch &= 255;
for (j=1; j < cItems; j++) {
SendMessage(hwndLB, LB_GETTEXT, (i+j) % cItems, (LPARAM)&pNode);
szB[0] = pNode->szName[0];
/* Do it this way to be case insensitive. */
w = ch;
if (!lstrcmpi((LPSTR)&w, szB))
break;
}
if (j == cItems)
return -2L;
SendMessage(hwndLB, LB_SETTOPINDEX, (i+j) % cItems, 0L);
return((i+j) % cItems);
}
case WM_DESTROY:
MSG("TreeControlWndProc", "WM_DESTROY");
if (hwndLB == GetFocus()) {
HWND hwnd;
if (hwnd = HasDirWindow(GetParent(hWnd)))
SetFocus(hwnd);
else
SetFocus(HasDrivesWindow(GetParent(hWnd)));
}
FreeAllTreeData(hwndLB);
break;
case WM_CREATE:
TRACE(BF_WM_CREATE, "TreeControlWndProc - WM_CREATE");
// create the owner draw list box for the tree
{
HWND hwnd;
hwnd = CreateWindowEx(0L, szListbox, NULL, WS_TREESTYLE | WS_BORDER,
0, 0, 0, 0, hWnd, (HMENU)IDCW_TREELISTBOX, hAppInstance, NULL);
if (!hwnd)
return -1L;
SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont, 0L);
SetWindowLong(hWnd, GWL_READLEVEL, 0);
break;
}
case WM_DRAWITEM:
MSG("TreeControlWndProc", "WM_DRAWITEM");
TCWP_DrawItem((LPDRAWITEMSTRUCT)lParam, hwndLB, hWnd);
break;
case WM_FILESYSCHANGE:
MSG("TreeControlWndProc", "WM_FILESYSCHANGE");
{
HWND hwndParent;
PDNODE pNodePrev;
PDNODE pNodeT;
if (!lParam || wParam == FSC_REFRESH)
break;
nIndex = FindItemFromPath(hwndLB, (LPSTR)lParam, wParam == FSC_MKDIR, &pNode);
if (nIndex == 0xFFFF) /* Did we find it? */
break;
lstrcpy(szPath, (LPSTR)lParam);
StripPath(szPath);
switch (wParam) {
case FSC_MKDIR:
// auto expand the branch so they can see the new
// directory just created
if (!(pNode->wFlags & TF_EXPANDED) &&
(nIndex == (WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L)))
SendMessage(hWnd, TC_EXPANDLEVEL, FALSE, 0L);
// make sure this node isn't already here
if (FindItemFromPath(hwndLB, (LPSTR)lParam, FALSE, NULL) != 0xFFFF)
break;
// Insert it into the tree listbox
dwTemp = InsertDirectory(hWnd, pNode, (WORD)nIndex, szPath, &pNodeT);
// Add a plus if necessary
hwndParent = GetParent(hWnd);
if (GetWindowLong(hwndParent, GWL_VIEW) & VIEW_PLUSES) {
lstrcpy(szPath, (LPSTR)lParam);
ScanDirLevel((PDNODE)pNodeT, szPath, ATTR_DIR |
(GetWindowLong(hwndParent, GWL_ATTRIBS) & ATTR_HS));
// Invalidate the window so the plus gets drawn if needed
if (((PDNODE)pNodeT)->wFlags & TF_HASCHILDREN)
InvalidateRect(hWnd, NULL, FALSE);
}
// if we are inserting before or at the current selection
// push the current selection down
nIndex = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
if ((INT)LOWORD(dwTemp) <= nIndex) {
SendMessage(hwndLB, LB_SETCURSEL, nIndex + 1, 0L);
}
break;
case FSC_RMDIR:
if (nIndex == 0) /* NEVER delete the Root Dir! */
break;
if (pNode->wFlags & TF_LASTLEVELENTRY) {
// We are deleting the last subdirectory.
// If there are previous sibling directories, mark one
// as the last, else mark the parent as empty and unexpanded.
// It is necessary to do these checks if this bit
// is set, since if it isn't, there is another sibling
// with TF_LASTLEVELENTRY set, and so the parent is nonempty.
//
// Find the previous entry which has a level not deeper than
// the level of that being deleted.
i = (int)nIndex;
do {
SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodePrev);
} while (pNodePrev->nLevels > pNode->nLevels);
if (pNodePrev->nLevels == pNode->nLevels) {
// The previous directory is a sibling... it becomes
// the new last level entry.
pNodePrev->wFlags |= TF_LASTLEVELENTRY;
} else {
// In order to find this entry, the parent must have
// been expanded, so if the parent of the deleted dir
// has no listbox entries under it, it may be assumed that
// the directory has no children.
pNodePrev->wFlags &= ~(TF_HASCHILDREN | TF_EXPANDED);
}
}
// Are we deleting the current selection?
// if so we move the selection to the item above the current.
// this should work in all cases because you can't delete
// the root.
if ((WPARAM)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L) == nIndex) {
SendMessage(hwndLB, LB_SETCURSEL, nIndex - 1, 0L);
SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, 0, LBN_SELCHANGE));
}
SendMessage(hWnd, TC_COLLAPSELEVEL, nIndex, 0L);
SendMessage(hwndLB, LB_DELETESTRING, nIndex, 0L);
LocalFree((HANDLE)pNode);
break;
}
break;
}
case WM_COMMAND:
{
WORD id;
id = GET_WM_COMMAND_ID(wParam, lParam);
switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
case LBN_SELCHANGE:
MSG("TreeControlWndProc", "LBN_SELCHANGE");
{
HWND hwndParent;
HWND hwndDir;
INT CurSel;
hwndParent = GetParent(hWnd);
CurSel = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
SendMessage(hWnd, TC_GETDIR, CurSel,(LPARAM)szPath);
AddBackslash(szPath);
SendMessage(hwndParent, FS_GETFILESPEC, sizeof(szPath) - lstrlen(szPath), (LPARAM)szPath+lstrlen(szPath));
if (hwndDir = HasDirWindow(hwndParent)) {
// update the dir window
id = CD_PATH;
// don't allow abort on first or last directories
if (CurSel > 0 &&
CurSel != ((INT)SendMessage(hwndLB, LB_GETCOUNT, 0, 0L) - 1)) {
id = CD_PATH | CD_ALLOWABORT;
}
SendMessage(hwndDir, FS_CHANGEDISPLAY, id, (LPARAM)szPath);
} else {
SetMDIWindowText(hwndParent, szPath);
}
UpdateStatus(hwndParent);
break;
}
case LBN_DBLCLK:
MSG("TreeControlWndProc", "LBN_DBLCLK");
SendMessage(hwndFrame, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_OPEN, 0, 0));
break;
case LBN_SETFOCUS:
MSG("TreeControlWndProc", "LBN_SETFOCUS");
SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
UpdateStatus(GetParent(hWnd)); // update the status bar
break;
case LBN_KILLFOCUS:
MSG("TreeControlWndProc", "LBN_KILLFOCUS");
SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, 0);
UpdateSelection(GET_WM_COMMAND_HWND(wParam, lParam));
SetWindowLongPtr(GetParent(hWnd), GWLP_LASTFOCUS, (LPARAM)GET_WM_COMMAND_HWND(wParam, lParam));
break;
}
}
break;
case WM_LBTRACKPOINT:
MSG("TreeControlWndProc", "WM_LBTRACKPOINT");
// wParam is the listbox index that we are over
// lParam is the mouse point
/* Return 0 to do nothing, 1 to abort everything, or 2 to abort just dblclicks. */
{
HDC hdc;
INT dx;
INT xNode;
MSG msg;
RECT rc;
HFONT hOld;
POINT pt;
DRAGOBJECTDATA dodata;
/* Someone clicked somewhere in the listbox. */
// don't do anything while the tree is being built
if (GetWindowLong(hWnd, GWL_READLEVEL))
return 1;
/* Get the node they clicked on. */
SendMessage(hwndLB, LB_GETTEXT, wParam, (LPARAM)&pNode);
lstrcpy(szPath, pNode->szName);
if ((wTextAttribs & TA_LOWERCASE) && !(pNode->wFlags & TF_LFN))
AnsiLower(szPath);
// if (pNode->wFlags | TF_DISABLED)
// return 2L;
// too FAR to the left?
i = LOWORD(lParam);
xNode = pNode->nLevels * dxText * 2;
if (i < xNode)
return 2; // yes, get out now
// too FAR to the right?
hdc = GetDC(hwndLB);
hOld = SelectObject(hdc, hFont);
MGetTextExtent(hdc, szPath, lstrlen(szPath), &dx, NULL);
dx += (dyBorderx2*2);
if (hOld)
SelectObject(hdc, hOld);
ReleaseDC(hwndLB, hdc);
if (i > xNode + dxFolder + dx + 4 * dyBorderx2)
return 2; // yes
// Emulate a SELCHANGE notification and notify our parent
SendMessage(hwndLB, LB_SETCURSEL, wParam, 0L);
SendMessage(hWnd, WM_COMMAND, GET_WM_COMMAND_MPS(0, hwndLB, LBN_SELCHANGE));
// make sure mouse still down
if (!(GetKeyState(VK_LBUTTON) & 0x8000))
return 1;
MPOINT2POINT(MAKEMPOINT(lParam), pt);
ClientToScreen(hwndLB, (LPPOINT)&pt);
ScreenToClient(hWnd, (LPPOINT)&pt);
SetRect(&rc, pt.x - dxClickRect, pt.y - dyClickRect,
pt.x + dxClickRect, pt.y + dyClickRect);
SetCapture(hWnd);
while (GetMessage(&msg, NULL, 0, 0)) {
DispatchMessage(&msg);
if (msg.message == WM_LBUTTONUP)
break;
MPOINT2POINT(MAKEMPOINT(msg.lParam), pt);
if (GetCapture() != hWnd) {
msg.message = WM_LBUTTONUP;
break;
}
if ((msg.message == WM_MOUSEMOVE) && !(PtInRect(&rc, pt)))
break;
}
ReleaseCapture();
/* Did the guy NOT drag anything? */
if (msg.message == WM_LBUTTONUP)
return 1;
/* Enter Danger Mouse's BatCave. */
SendMessage(GetParent(hWnd), FS_GETDIRECTORY, sizeof(szPath), (LPARAM)szPath);
StripBackslash(szPath);
hwndDragging = hwndLB;
iCurDrag = SINGLECOPYCURSOR;
dodata.pch = szPath;
dodata.hMemGlobal = 0;
DragObject(hwndMDIClient, hWnd, (UINT)DOF_DIRECTORY, (DWORD)(ULONG_PTR)&dodata, LoadCursor(hAppInstance, MAKEINTRESOURCE(iCurDrag)));
hwndDragging = NULL;
fShowSourceBitmaps = TRUE;
InvalidateRect(hwndLB, NULL, FALSE);
return 2;
}
case WM_DRAGSELECT:
MSG("TreeControlWndProc", "WM_DRAGSELECT");
/* WM_DRAGSELECT is sent whenever a new window returns TRUE to a
* QUERYDROPOBJECT.
*/
iSelHilite = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
RectTreeItem(hwndLB, iSelHilite, (BOOL)wParam);
break;
case WM_DRAGMOVE:
MSG("TreeControlWndProc", "WM_DRAGMOVE");
/* WM_DRAGMOVE is sent when two consequetive TRUE QUERYDROPOBJECT
* messages come from the same window.
*/
/* Get the subitem we are over. */
iSel = LOWORD(((LPDROPSTRUCT)lParam)->dwControlData);
/* Is it a new one? */
if (iSel == (WORD)iSelHilite)
break;
/* Yup, un-select the old item. */
RectTreeItem(hwndLB, iSelHilite, FALSE);
/* Select the new one. */
iSelHilite = iSel;
RectTreeItem(hwndLB, iSel, TRUE);
break;
case WM_DRAGLOOP:
MSG("TreeControlWndProc", "WM_DRAGLOOP");
// wParam TRUE on dropable target
// FALSE not dropable target
// lParam lpds
{
BOOL bCopy;
#define lpds ((LPDROPSTRUCT)lParam)
/* Are we over a drop-able sink? */
if (wParam) {
if (GetKeyState(VK_CONTROL) < 0) // CTRL
bCopy = TRUE;
else if (GetKeyState(VK_MENU)<0 || GetKeyState(VK_SHIFT)<0) // ALT || SHIFT
bCopy = FALSE;
else
bCopy = (GetDrive(lpds->hwndSink, lpds->ptDrop) != GetDrive(lpds->hwndSource, lpds->ptDrop));
} else {
bCopy = TRUE;
}
if (bCopy != fShowSourceBitmaps) {
RECT rc;
fShowSourceBitmaps = bCopy;
iSel = (WORD)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
if (!(BOOL)SendMessage(hwndLB, LB_GETITEMRECT, iSel, (LPARAM)&rc))
break;
InvalidateRect(hwndLB, &rc, FALSE);
UpdateWindow(hwndLB);
// hack, set the cursor to match the move/copy state
if (wParam)
SetCursor(GetMoveCopyCursor());
}
break;
}
case WM_QUERYDROPOBJECT:
MSG("TreeControlWndProc", "WM_QUERYDROPOBJECT");
// wParam TRUE on NC area
// FALSE on client area
// lParam lpds
// Do nothing
return(FALSE);
#define lpds ((LPDROPSTRUCT)lParam)
/* Check for valid format. */
switch (lpds->wFmt) {
case DOF_EXECUTABLE:
case DOF_DOCUMENT:
case DOF_DIRECTORY:
case DOF_MULTIPLE:
if (fShowSourceBitmaps)
i = iCurDrag | 1; // copy
else
i = iCurDrag & 0xFFFE;
break;
default:
return FALSE;
}
/* Must be dropping on the listbox client area. */
if (lpds->hwndSink != hwndLB)
return FALSE;
if (LOWORD(lpds->dwControlData) == 0xFFFF)
return FALSE;
return (INT_PTR)GetMoveCopyCursor();
case WM_DROPOBJECT: // tree being dropped on do your thing
#define lpds ((LPDROPSTRUCT)lParam) // BUG: WM_DROPOBJECT structure packing!
// Do nothing
return(TRUE);
MSG("TreeControlWndProc", "WM_DROPOBJECT");
// dir (search) drop on tree:
// HIWORD(dwData) 0
// LOWORD(dwData) LPSTR to files being dragged
//
// tree drop on tree:
// HIWORD(dwData) index of source drag
// LOWORD(dwData) LPSTR to path
{
LPSTR pFrom;
nIndex = LOWORD(lpds->dwControlData);
pFrom = (LPSTR)(((LPDRAGOBJECTDATA)(lpds->dwData))->pch);
// Get the destination
SendMessage(hWnd, TC_GETDIR, nIndex, (LPARAM)szPath);
CheckEscapes(szPath);
// if source and dest are the same make this a NOP
if (!lstrcmpi(szPath, pFrom))
return TRUE;
AddBackslash(szPath);
lstrcat(szPath, szStarDotStar);
DMMoveCopyHelper(pFrom, szPath, fShowSourceBitmaps);
RectTreeItem(hwndLB, (int)nIndex, FALSE);
}
return TRUE;
#undef lpds
case WM_MEASUREITEM:
MSG("TreeControlWndProc", "WM_MEASUREITEM");
#define pLBMItem ((LPMEASUREITEMSTRUCT)lParam)
pLBMItem->itemHeight = (WORD)dyFileName;
break;
case WM_VKEYTOITEM:
MSG("TreeControlWndProc", "WM_VKEYTOITEM");
if (wParam == VK_ESCAPE) {
bCancelTree = TRUE;
return -2L;
}
i = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
if (i < 0)
return -2L;
j = 1;
SendMessage(hwndLB, LB_GETTEXT, i, (LPARAM)&pNode);
switch (GET_WM_VKEYTOITEM_ITEM(wParam, lParam)) {
case VK_LEFT:
while (SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext) != LB_ERR) {
if (pNode == pNode->pParent)
return(i);
}
goto SameSelection;
case VK_RIGHT:
if ((SendMessage(hwndLB, LB_GETTEXT, i+1, (LPARAM)&pNodeNext) == LB_ERR)
|| (pNodeNext->pParent != pNode)) {
goto SameSelection;
}
return(i+1);
case VK_UP:
j = -1;
/** FALL THRU ***/
case VK_DOWN:
/* If the control key is not down, use default behavior. */
if (GetKeyState(VK_CONTROL) >= 0)
return(-1L);
while (SendMessage(hwndLB, LB_GETTEXT, i += j, (LPARAM)&pNodeNext) != LB_ERR) {
if (pNodeNext->pParent == pNode->pParent)
return(i);
}
SameSelection:
MessageBeep(0);
return(-2L);
case VK_F6: // like excel
case VK_TAB:
{
HWND hwndDir, hwndDrives;
BOOL bDir;
GetTreeWindows(GetParent(hWnd), NULL, &hwndDir, &hwndDrives);
// Check to see if we can change to the directory window
if (hwndDir) {
HWND hwndLB; /* Local scope ONLY */
hwndLB = GetDlgItem (hwndDir,IDCW_LISTBOX);
if (hwndLB) {
SendMessage (hwndLB,LB_GETTEXT,0, (LPARAM) &pNode);
bDir = pNode ? TRUE : FALSE;
}
}
if (GetKeyState(VK_SHIFT) < 0)
SetFocus(hwndDrives);
else
if (bDir)
SetFocus (hwndDir);
else
SetFocus (hwndDrives);
return -2L; // I dealt with this!
}
case VK_BACK:
{
BYTE nStartLevel;
if (i <= 0)
return -2L; // root case
nStartLevel = pNode->nLevels;
do {
SendMessage(hwndLB, LB_GETTEXT, --i, (LPARAM)&pNodeNext);
} while (i > 0 && pNodeNext->nLevels >= nStartLevel);
return i;
}
default:
if (GetKeyState(VK_CONTROL) < 0)
return SendMessage(GetDlgItem(GetParent(hWnd), IDCW_DRIVES), wMsg, wParam, lParam);
return -1L;
}
break;
case WM_SETFOCUS:
case WM_LBUTTONDOWN:
MSG("TreeControlWndProc", "WM_LBUTTONDOWN");
SetFocus(hwndLB);
break;
case WM_SIZE:
MSG("TreeControlWndProc", "WM_SIZE");
if (!IsIconic(GetParent(hWnd))) {
INT iMax;
MoveWindow(hwndLB, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
iMax = (INT)SendMessage(hwndLB, LB_GETCURSEL, 0, 0L);
if (iMax >= 0) {
RECT rc;
INT top, bottom;
GetClientRect(hwndLB, &rc);
top = (INT)SendMessage(hwndLB, LB_GETTOPINDEX, 0, 0L);
bottom = top + rc.bottom / dyFileName;
if (iMax < top || iMax > bottom)
SendMessage(hwndLB, LB_SETTOPINDEX, iMax - ((bottom - top) / 2), 0L);
}
}
break;
default:
DEFMSG("TreeControlWndProc", (WORD)wMsg);
return DefWindowProc(hWnd, wMsg, wParam, lParam);
}
return 0L;
}