2218 lines
68 KiB
C
2218 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)
|
|
OemToCharBuff(lfndta.fd.cFileName, lfndta.fd.cFileName, sizeof(lfndta.fd.cFileName)/sizeof(lfndta.fd.cFileName[0]));
|
|
|
|
// 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)) {
|
|
|
|
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
|
|
|
|
{
|
|
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;
|
|
|
|
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 L_hwndLB; /* Local scope ONLY */
|
|
|
|
L_hwndLB = GetDlgItem (hwndDir,IDCW_LISTBOX);
|
|
if (L_hwndLB) {
|
|
SendMessage (L_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;
|
|
}
|