You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1139 lines
34 KiB
1139 lines
34 KiB
/****************************************************************************\
|
|
*
|
|
* LBOXCTL3.C -
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Directory List Box Routines
|
|
*
|
|
* ??-???-???? ianja Ported from Win 3.0 sources
|
|
* 14-Feb-1991 mikeke Added Revalidation code
|
|
\****************************************************************************/
|
|
|
|
#define CTLMGR
|
|
#define LSTRING
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define DATESEPARATOR TEXT('-')
|
|
#define TIMESEPARATOR TEXT(':')
|
|
#define TABCHAR TEXT('\t')
|
|
|
|
#define MAXDIGITSINSIZE 9
|
|
|
|
void LB_CreateLBLine(LPWIN32_FIND_DATA, LPWSTR);
|
|
|
|
#define DDL_PRIVILEGES (DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_ARCHIVE)
|
|
#define DDL_TYPE (DDL_DRIVES | DDL_DIRECTORY | DDL_POSTMSGS)
|
|
|
|
/***************************************************************************\
|
|
* ChopText
|
|
*
|
|
* Chops the given path at 'lpchBuffer' + CCH_CHOPTEXT_EXTRA to fit in the
|
|
* field of the static control with id 'idStatic' in the dialog box 'hwndDlg'.
|
|
* If the path is too long, an ellipsis prefix is added to the beginning of the
|
|
* chopped text ("x:\...\")
|
|
*
|
|
* If the supplied path does not fit and the last directory appended to
|
|
* ellipsis (i.e. "c:\...\eee" in the case of "c:\aaa\bbb\ccc\ddd\eee")
|
|
* does not fit, then "x:\..." is returned.
|
|
*
|
|
* Pathological case:
|
|
* "c:\SW\MW\R2\LIB\SERVICES\NT" almost fits into static control, while
|
|
* "c:\...\MW\R2\LIB\SERVICES\NT" does fit - although it is more characters.
|
|
* In this case, ChopText substitutes the first 'n' characters of the path with
|
|
* a prefix containing MORE than 'n' characters! The extra characters will
|
|
* be put in front of lpch, so there must be space reserved for them or they
|
|
* will trash the stack. lpch contains CCH_CHOPTEXT_EXTRA chars followed by
|
|
* the path.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
/*
|
|
* In practice CCH_CHOPTEXT_EXTRA probably never has to be more than 1 or 2,
|
|
* but in case the font is weird, set it to the number of chars in the prefix.
|
|
* This guarantees enough space to prepend the prefix.
|
|
*/
|
|
#define CCH_CHOPTEXT_EXTRA 7
|
|
|
|
LPWSTR ChopText(
|
|
PWND pwndDlg,
|
|
int idStatic,
|
|
LPWSTR lpchBuffer)
|
|
{
|
|
#define AWCHLEN(a) ((sizeof(a)/sizeof(a[0])) - 1)
|
|
|
|
/*
|
|
* Declaring szPrefix this way ensures CCH_CHOPTEXT_EXTRA is big enough
|
|
*/
|
|
WCHAR szPrefix[CCH_CHOPTEXT_EXTRA + 1] = L"x:\\...\\";
|
|
int cxField;
|
|
RECT rc;
|
|
SIZE size;
|
|
PWND pwndStatic;
|
|
PSTAT pstat;
|
|
HDC hdc;
|
|
HFONT hOldFont;
|
|
int cchPath;
|
|
PWCHAR lpch;
|
|
PWCHAR lpchPath;
|
|
|
|
/*
|
|
* Get length of static field.
|
|
*/
|
|
pwndStatic = _GetDlgItem(pwndDlg, idStatic);
|
|
if (pwndStatic == NULL)
|
|
return NULL;
|
|
|
|
_GetClientRect(pwndStatic, &rc);
|
|
cxField = rc.right - rc.left;
|
|
|
|
/*
|
|
* Set up DC appropriately for the static control.
|
|
*/
|
|
hdc = NtUserGetDC(HWq(pwndStatic));
|
|
|
|
/*
|
|
* Only assume this is a static window if this window uses the static
|
|
* window wndproc.
|
|
*/
|
|
hOldFont = NULL;
|
|
if (GETFNID(pwndStatic) == FNID_STATIC) {
|
|
pstat = ((PSTATWND)pwndStatic)->pstat;
|
|
if (pstat != NULL && pstat != (PSTAT)-1 && pstat->hFont)
|
|
hOldFont = SelectObject(hdc, pstat->hFont);
|
|
}
|
|
|
|
/*
|
|
* Check horizontal extent of string.
|
|
*/
|
|
lpch = lpchPath = lpchBuffer + CCH_CHOPTEXT_EXTRA;
|
|
cchPath = wcslen(lpchPath);
|
|
GetTextExtentPoint(hdc, lpchPath, cchPath, &size);
|
|
if (size.cx > cxField) {
|
|
|
|
/*
|
|
* String is too long to fit in the static control; chop it.
|
|
* Set up new prefix and determine remaining space in control.
|
|
*/
|
|
szPrefix[0] = *lpchPath;
|
|
GetTextExtentPoint(hdc, szPrefix, AWCHLEN(szPrefix), &size);
|
|
|
|
/*
|
|
* If the field is too small to display all of the prefix,
|
|
* copy only the prefix.
|
|
*/
|
|
if (cxField < size.cx) {
|
|
RtlCopyMemory(lpch, szPrefix, sizeof(szPrefix));
|
|
goto DoneChop;
|
|
} else
|
|
cxField -= size.cx;
|
|
|
|
/*
|
|
* Advance a directory at a time until the remainder of the
|
|
* string fits into the static control after the "x:\...\" prefix.
|
|
*/
|
|
while (TRUE) {
|
|
int cchT;
|
|
while (*lpch && (*lpch++ != L'\\')) {
|
|
;
|
|
}
|
|
cchT = cchPath - (int)(lpch - lpchPath);
|
|
GetTextExtentPoint(hdc, lpch, cchT, &size);
|
|
if (*lpch == 0 || size.cx <= cxField) {
|
|
|
|
if (*lpch == 0) {
|
|
|
|
/*
|
|
* Nothing could fit after the prefix; remove the
|
|
* final "\" from the prefix
|
|
*/
|
|
szPrefix[AWCHLEN(szPrefix) - 1] = 0;
|
|
}
|
|
|
|
/*
|
|
* rest of string fits -- back up and stick prefix on front
|
|
* We are guaranteed to have at least CCH_CHOPTEXT_EXTRA chars
|
|
* backing up space, so we won't trash any stack. #26453
|
|
*/
|
|
lpch -= AWCHLEN(szPrefix);
|
|
|
|
UserAssert(lpch >= lpchBuffer);
|
|
|
|
RtlCopyMemory(lpch, szPrefix, sizeof(szPrefix) - sizeof(WCHAR));
|
|
goto DoneChop;
|
|
}
|
|
}
|
|
}
|
|
|
|
DoneChop:
|
|
if (hOldFont)
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
ReleaseDC(HWq(pwndStatic), hdc);
|
|
|
|
return lpch;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDlgDirListHelper
|
|
*
|
|
* NOTE: If idStaticPath is < 0, then that parameter contains the details
|
|
* about what should be in each line of the list box
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
BOOL xxxDlgDirListHelper(
|
|
PWND pwndDlg,
|
|
LPWSTR lpszPathSpec,
|
|
LPBYTE lpszPathSpecClient,
|
|
int idListBox,
|
|
int idStaticPath,
|
|
UINT attrib,
|
|
BOOL fListBox) /* Listbox or ComboBox? */
|
|
{
|
|
PWND pwndLB;
|
|
TL tlpwndLB;
|
|
BOOL fDir = TRUE;
|
|
BOOL fRoot, bRet;
|
|
BOOL fPostIt;
|
|
INT cch;
|
|
WCHAR ch;
|
|
WCHAR szStaticPath[CCH_CHOPTEXT_EXTRA + MAX_PATH];
|
|
PWCHAR pszCurrentDir;
|
|
UINT wDirMsg;
|
|
LPWSTR lpchFile;
|
|
LPWSTR lpchDirectory;
|
|
PLBIV plb;
|
|
BOOL fWasVisible = FALSE;
|
|
BOOL fWin40Compat;
|
|
PCBOX pcbox;
|
|
|
|
CheckLock(pwndDlg);
|
|
|
|
/*
|
|
* Strip the private bit DDL_NOFILES out - KidPix passes it in my mistake!
|
|
*/
|
|
if (attrib & ~DDL_VALID) {
|
|
RIPERR2(ERROR_INVALID_FLAGS, RIP_WARNING, "Invalid flags, %x & ~%x != 0",
|
|
attrib, DDL_VALID);
|
|
return FALSE;
|
|
}
|
|
|
|
if (attrib & DDL_NOFILES) {
|
|
RIPMSG0(RIP_WARNING, "DlgDirListHelper: stripping DDL_NOFILES");
|
|
attrib &= ~DDL_NOFILES;
|
|
}
|
|
|
|
/*
|
|
* Case:Works is an app that calls DlgDirList with a NULL has hwndDlg;
|
|
* This is allowed because he uses NULL for idStaticPath and idListBox.
|
|
* So, the validation layer has been modified to allow a NULL for hwndDlg.
|
|
* But, we catch the bad apps with the following check.
|
|
* Fix for Bug #11864 --SANKAR-- 08/22/91 --
|
|
*/
|
|
if (!pwndDlg && (idStaticPath || idListBox)) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
|
|
plb = NULL;
|
|
|
|
/*
|
|
* Do we need to add date, time, size or attribute info?
|
|
* Windows checks the Atom but misses if the class has been sub-classed
|
|
* as in VB.
|
|
*/
|
|
if (pwndLB = (PWND)_GetDlgItem(pwndDlg, idListBox)) {
|
|
WORD fnid = GETFNID(pwndLB);
|
|
|
|
if ((fnid == FNID_LISTBOX && fListBox) ||
|
|
(fnid == FNID_COMBOBOX && !fListBox) ||
|
|
(fnid == FNID_COMBOLISTBOX && fListBox)) {
|
|
if (fListBox) {
|
|
plb = ((PLBWND)pwndLB)->pLBIV;
|
|
} else {
|
|
|
|
pcbox = ((PCOMBOWND)pwndLB)->pcbox;
|
|
plb = ((PLBWND)(pcbox->spwndList))->pLBIV;
|
|
}
|
|
} else {
|
|
RIPERR0(ERROR_LISTBOX_ID_NOT_FOUND, RIP_VERBOSE, "");
|
|
}
|
|
} else if (idListBox != 0) {
|
|
|
|
/*
|
|
* Yell if the app passed an invalid list box id and keep from using a
|
|
* bogus plb. PLB is NULLed above.
|
|
*/
|
|
RIPERR0(ERROR_LISTBOX_ID_NOT_FOUND, RIP_VERBOSE, "");
|
|
}
|
|
|
|
if (idStaticPath < 0 && plb != NULL) {
|
|
|
|
/*
|
|
* Clear idStaticPath because its purpose is over.
|
|
*/
|
|
idStaticPath = 0;
|
|
|
|
}
|
|
|
|
fPostIt = (attrib & DDL_POSTMSGS);
|
|
|
|
if (lpszPathSpec) {
|
|
cch = lstrlenW(lpszPathSpec);
|
|
if (!cch) {
|
|
if (lpszPathSpecClient != (LPBYTE)lpszPathSpec) {
|
|
lpszPathSpecClient = achSlashStar;
|
|
}
|
|
lpszPathSpec = awchSlashStar;
|
|
} else {
|
|
/*
|
|
* Make sure we won't overflow our buffers...
|
|
*/
|
|
if (cch > CCHFILEMAX)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Convert lpszPathSpec into an upper case, OEM string.
|
|
*/
|
|
CharUpper(lpszPathSpec);
|
|
lpchDirectory = lpszPathSpec;
|
|
|
|
lpchFile = szSLASHSTARDOTSTAR + 1;
|
|
|
|
if (*lpchDirectory) {
|
|
|
|
cch = wcslen(lpchDirectory);
|
|
|
|
/*
|
|
* If the directory name has a * or ? in it, don't bother trying
|
|
* the (slow) SetCurrentDirectory.
|
|
*/
|
|
if (((INT)FindCharPosition(lpchDirectory, TEXT('*')) != cch) ||
|
|
((INT)FindCharPosition(lpchDirectory, TEXT('?')) != cch) ||
|
|
!SetCurrentDirectory(lpchDirectory)) {
|
|
|
|
/*
|
|
* Set 'fDir' and 'fRoot' accordingly.
|
|
*/
|
|
lpchFile = lpchDirectory + cch;
|
|
fDir = *(lpchFile - 1) == TEXT('\\');
|
|
fRoot = 0;
|
|
while (cch--) {
|
|
ch = *(lpchFile - 1);
|
|
if (ch == TEXT('*') || ch == TEXT('?'))
|
|
fDir = TRUE;
|
|
|
|
if (ch == TEXT('\\') || ch == TEXT('/') || ch == TEXT(':')) {
|
|
fRoot = (cch == 0 || *(lpchFile - 2) == TEXT(':') ||
|
|
(ch == TEXT(':')));
|
|
break;
|
|
}
|
|
lpchFile--;
|
|
}
|
|
|
|
/*
|
|
* To remove Bug #16, the following error return is to be removed.
|
|
* In order to prevent the existing apps from breaking up, it is
|
|
* decided that the bug will not be fixed and will be mentioned
|
|
* in the documentation.
|
|
* --SANKAR-- Sep 21
|
|
*/
|
|
|
|
/*
|
|
* If no wildcard characters, return error.
|
|
*/
|
|
if (!fDir) {
|
|
RIPERR0(ERROR_NO_WILDCARD_CHARACTERS, RIP_VERBOSE, "");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Special case for lpchDirectory == "\"
|
|
*/
|
|
if (fRoot)
|
|
lpchFile++;
|
|
|
|
/*
|
|
* Do we need to change directories?
|
|
*/
|
|
if (fRoot || cch >= 0) {
|
|
|
|
/*
|
|
* Replace the Filename's first char with a nul.
|
|
*/
|
|
ch = *--lpchFile;
|
|
*lpchFile = TEXT('\0');
|
|
|
|
/*
|
|
* Change the current directory.
|
|
*/
|
|
if (*lpchDirectory) {
|
|
bRet = SetCurrentDirectory(lpchDirectory);
|
|
if (!bRet) {
|
|
|
|
/*
|
|
* Restore the filename before we return...
|
|
*/
|
|
*((LPWSTR)lpchFile)++ = ch;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Restore the filename's first character.
|
|
*/
|
|
*lpchFile++ = ch;
|
|
}
|
|
|
|
/*
|
|
* Undo damage caused by special case above.
|
|
*/
|
|
if (fRoot) {
|
|
lpchFile--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This is copying on top of the data the client passed us! Since
|
|
* the LB_DIR or CB_DIR could be posted, and since we need to
|
|
* pass a client side string pointer when we do that, we need
|
|
* to copy this new data back to the client!
|
|
*/
|
|
if (fPostIt && lpszPathSpecClient != (LPBYTE)lpszPathSpec) {
|
|
WCSToMB(lpchFile, -1, &lpszPathSpecClient, MAXLONG, FALSE);
|
|
}
|
|
wcscpy(lpszPathSpec, lpchFile);
|
|
}
|
|
}
|
|
/*
|
|
* In some cases, the ChopText requires extra space ahead of the path:
|
|
* Give it CCH_CHOPTEXT_EXTRA extra spaces. (See ChopText() above).
|
|
*/
|
|
pszCurrentDir = szStaticPath + CCH_CHOPTEXT_EXTRA;
|
|
GetCurrentDirectory(
|
|
sizeof(szStaticPath)/sizeof(WCHAR) - CCH_CHOPTEXT_EXTRA,
|
|
pszCurrentDir);
|
|
|
|
/*
|
|
* If we have a listbox, lock it down
|
|
*/
|
|
if (pwndLB != NULL) {
|
|
ThreadLockAlways(pwndLB, &tlpwndLB);
|
|
}
|
|
|
|
/*
|
|
* Fill in the static path item.
|
|
*/
|
|
if (idStaticPath) {
|
|
|
|
/*
|
|
* To fix a bug OemToAnsi() call is inserted; SANKAR--Sep 16th
|
|
*/
|
|
// OemToChar(szCurrentDir, szCurrentDir);
|
|
CharLower(pszCurrentDir);
|
|
SetDlgItemText(HWq(pwndDlg), idStaticPath, ChopText(pwndDlg, idStaticPath, szStaticPath));
|
|
}
|
|
|
|
/*
|
|
* Fill in the directory List/ComboBox if it exists.
|
|
*/
|
|
if (idListBox && pwndLB != NULL) {
|
|
|
|
HWND hwndLB = HWq(pwndLB);
|
|
|
|
wDirMsg = (UINT)(fListBox ? LB_RESETCONTENT : CB_RESETCONTENT);
|
|
|
|
if (fPostIt) {
|
|
PostMessage(hwndLB, wDirMsg, 0, 0L);
|
|
} else {
|
|
if (plb != NULL && (fWasVisible = IsLBoxVisible(plb))) {
|
|
SendMessage(hwndLB, WM_SETREDRAW, FALSE, 0L);
|
|
}
|
|
SendMessage(hwndLB, wDirMsg, 0, 0L);
|
|
}
|
|
|
|
wDirMsg = (UINT)(fListBox ? LB_DIR : CB_DIR);
|
|
|
|
if (attrib == DDL_DRIVES)
|
|
attrib |= DDL_EXCLUSIVE;
|
|
|
|
//
|
|
// Hack for DDL_EXCLUSIVE to REALLY work.
|
|
//
|
|
fWin40Compat = TestWF(pwndLB, WFWIN40COMPAT);
|
|
|
|
//
|
|
// BACKWARDS COMPATIBILITY HACK
|
|
//
|
|
// We want DDL_EXCLUSIVE to _really_ work for new apps. I.E., we
|
|
// want apps to be able to specify DDL_DRIVES/DDL_VOLUMES with
|
|
// DDL_EXCLUSIVE and privilege bits -- and have only those items
|
|
// matching show up, w/out files.
|
|
//
|
|
if (attrib & DDL_EXCLUSIVE)
|
|
{
|
|
if (fWin40Compat)
|
|
{
|
|
if (attrib & (DDL_DRIVES | DDL_DIRECTORY))
|
|
attrib |= DDL_NOFILES;
|
|
}
|
|
else
|
|
{
|
|
if (attrib == (DDL_DRIVES | DDL_EXCLUSIVE))
|
|
attrib |= DDL_NOFILES;
|
|
}
|
|
}
|
|
|
|
if (!(attrib & DDL_NOFILES)) {
|
|
|
|
/*
|
|
* Add everything except the subdirectories and disk drives.
|
|
*/
|
|
if (fPostIt) {
|
|
/*
|
|
* Post lpszPathSpecClient, the client side pointer.
|
|
*/
|
|
#ifdef WASWIN31
|
|
PostMessage(hwndLB, wDirMsg, attrib &
|
|
~(DDL_DIRECTORY | DDL_DRIVES | DDL_POSTMSGS),
|
|
(LPARAM)lpszPathSpecClient);
|
|
#else
|
|
/*
|
|
* On NT, keep DDL_POSTMSGS in wParam because we need to know
|
|
* in the wndproc whether the pointer is clientside or server
|
|
* side.
|
|
*/
|
|
PostMessage(hwndLB, wDirMsg,
|
|
attrib & ~(DDL_DIRECTORY | DDL_DRIVES),
|
|
(LPARAM)lpszPathSpecClient);
|
|
#endif
|
|
|
|
} else {
|
|
|
|
/*
|
|
* IanJa: #ifndef WIN16 (32-bit Windows), attrib gets extended
|
|
* to LONG wParam automatically by the compiler
|
|
*/
|
|
SendMessage(hwndLB, wDirMsg,
|
|
attrib & ~(DDL_DIRECTORY | DDL_DRIVES),
|
|
(LPARAM)lpszPathSpec);
|
|
}
|
|
|
|
#ifdef WASWIN31
|
|
/*
|
|
* Strip out just the subdirectory and drive bits.
|
|
*/
|
|
attrib &= (DDL_DIRECTORY | DDL_DRIVES);
|
|
#else
|
|
//
|
|
// B#1433
|
|
// The old code stripped out read-only, hidden, system, and archive
|
|
// information for subdirectories, making it impossible to have
|
|
// a listbox w/ hidden directories!
|
|
//
|
|
|
|
/*
|
|
* Strip out just the subdirectory and drive bits. ON NT, keep
|
|
* the DDL_POSTMSG bit so we know how to thunk this message.
|
|
*/
|
|
if (!fWin40Compat)
|
|
attrib &= DDL_TYPE;
|
|
else
|
|
{
|
|
attrib &= (DDL_TYPE | (attrib & DDL_PRIVILEGES));
|
|
attrib |= DDL_NOFILES;
|
|
}
|
|
// attrib &= (DDL_DIRECTORY | DDL_DRIVES | DDL_POSTMSGS);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Add directories and volumes to the listbox.
|
|
//
|
|
if (attrib & DDL_TYPE) {
|
|
|
|
/*
|
|
* Add the subdirectories and disk drives.
|
|
*/
|
|
lpszPathSpec = szSLASHSTARDOTSTAR + 1;
|
|
|
|
attrib |= DDL_EXCLUSIVE;
|
|
|
|
if (fPostIt) {
|
|
/*
|
|
* Post lpszPathSpecClient, the client side pointer (see text
|
|
* above).
|
|
*/
|
|
PostMessage(hwndLB, wDirMsg, attrib, (LPARAM)lpszPathSpecClient);
|
|
} else {
|
|
SendMessage(hwndLB, wDirMsg, attrib, (LPARAM)lpszPathSpec);
|
|
}
|
|
}
|
|
|
|
if (!fPostIt && fWasVisible) {
|
|
SendMessage(hwndLB, WM_SETREDRAW, TRUE, 0L);
|
|
NtUserInvalidateRect(hwndLB, NULL, TRUE);
|
|
}
|
|
}
|
|
|
|
if (pwndLB != NULL) {
|
|
ThreadUnlock(&tlpwndLB);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxDlgDirList
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DlgDirListA, HWND, hwndDlg, LPSTR, lpszPathSpecClient, int, idListBox, int, idStaticPath, UINT, attrib)
|
|
BOOL DlgDirListA(
|
|
HWND hwndDlg,
|
|
LPSTR lpszPathSpecClient,
|
|
int idListBox,
|
|
int idStaticPath,
|
|
UINT attrib)
|
|
{
|
|
LPWSTR lpszPathSpec;
|
|
PWND pwndDlg;
|
|
TL tlpwndDlg;
|
|
BOOL fRet;
|
|
|
|
pwndDlg = ValidateHwnd(hwndDlg);
|
|
|
|
if (pwndDlg == NULL)
|
|
return FALSE;
|
|
|
|
lpszPathSpec = NULL;
|
|
if (lpszPathSpecClient) {
|
|
if (!MBToWCS(lpszPathSpecClient, -1, &lpszPathSpec, -1, TRUE))
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* The last parameter is TRUE to indicate ListBox (not ComboBox)
|
|
*/
|
|
ThreadLock(pwndDlg, &tlpwndDlg);
|
|
fRet = xxxDlgDirListHelper(pwndDlg, lpszPathSpec, lpszPathSpecClient,
|
|
idListBox, idStaticPath, attrib, TRUE);
|
|
ThreadUnlock(&tlpwndDlg);
|
|
|
|
if (lpszPathSpec) {
|
|
if (fRet) {
|
|
/*
|
|
* Non-zero retval means some text to copy out. Copy out up to
|
|
* the nul terminator (buffer will be big enough).
|
|
*/
|
|
WCSToMB(lpszPathSpec, -1, &lpszPathSpecClient, MAXLONG, FALSE);
|
|
}
|
|
UserLocalFree(lpszPathSpec);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
FUNCLOG5(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DlgDirListW, HWND, hwndDlg, LPWSTR, lpszPathSpecClient, int, idListBox, int, idStaticPath, UINT, attrib)
|
|
BOOL DlgDirListW(
|
|
HWND hwndDlg,
|
|
LPWSTR lpszPathSpecClient,
|
|
int idListBox,
|
|
int idStaticPath,
|
|
UINT attrib)
|
|
{
|
|
LPWSTR lpszPathSpec;
|
|
PWND pwndDlg;
|
|
TL tlpwndDlg;
|
|
BOOL fRet;
|
|
|
|
pwndDlg = ValidateHwnd(hwndDlg);
|
|
|
|
if (pwndDlg == NULL)
|
|
return FALSE;
|
|
|
|
lpszPathSpec = lpszPathSpecClient;
|
|
|
|
/*
|
|
* The last parameter is TRUE to indicate ListBox (not ComboBox)
|
|
*/
|
|
ThreadLock(pwndDlg, &tlpwndDlg);
|
|
fRet = xxxDlgDirListHelper(pwndDlg, lpszPathSpec, (LPBYTE)lpszPathSpecClient,
|
|
idListBox, idStaticPath, attrib, TRUE);
|
|
ThreadUnlock(&tlpwndDlg);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DlgDirSelectHelper
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
BOOL DlgDirSelectHelper(
|
|
LPWSTR lpszPathSpec,
|
|
int chCount,
|
|
HWND hwndListBox)
|
|
{
|
|
INT cch;
|
|
LPWSTR lpchFile;
|
|
BOOL fDir;
|
|
INT sItem;
|
|
LPWSTR lpchT;
|
|
WCHAR rgch[CCHFILEMAX + 2];
|
|
int cchT;
|
|
LARGE_UNICODE_STRING str;
|
|
|
|
/*
|
|
* Callers such as DlgDirSelectEx do not validate the existance
|
|
* of hwndListBox
|
|
*/
|
|
if (hwndListBox == NULL) {
|
|
RIPERR0(ERROR_CONTROL_ID_NOT_FOUND, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
sItem = (INT)SendMessage(hwndListBox, LB_GETCURSEL, 0, 0L);
|
|
if (sItem < 0)
|
|
return FALSE;
|
|
|
|
cchT = (INT)SendMessage(hwndListBox, LB_GETTEXT, sItem, (LPARAM)rgch);
|
|
UserAssert(cchT < (sizeof(rgch)/sizeof(rgch[0])));
|
|
|
|
lpchFile = rgch;
|
|
fDir = (*rgch == TEXT('['));
|
|
|
|
/*
|
|
* Check if all details along with file name are to be returned. Make sure
|
|
* we can find the listbox because with drop down combo boxes, the
|
|
* GetDlgItem will fail.
|
|
*
|
|
* Make sure this window has been using the listbox window proc because
|
|
* we store some data as a window long.
|
|
*/
|
|
|
|
/*
|
|
* Only the file name is to be returned. Find the end of the filename.
|
|
*/
|
|
lpchT = lpchFile;
|
|
while ((*lpchT) && (*lpchT != TABCHAR))
|
|
lpchT++;
|
|
*lpchT = TEXT('\0');
|
|
|
|
cch = wcslen(lpchFile);
|
|
|
|
/*
|
|
* Selection is drive or directory.
|
|
*/
|
|
if (fDir) {
|
|
lpchFile++;
|
|
cch--;
|
|
*(lpchFile + cch - 1) = TEXT('\\');
|
|
|
|
/*
|
|
* Selection is drive
|
|
*/
|
|
if (rgch[1] == TEXT('-')) {
|
|
lpchFile++;
|
|
cch--;
|
|
*(lpchFile + 1) = TEXT(':');
|
|
*(lpchFile + 2) = 0;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Selection is file. If filename has no extension, append '.'
|
|
*/
|
|
lpchT = lpchFile;
|
|
for (; (cch > 0) && (*lpchT != TABCHAR);
|
|
cch--, lpchT++) {
|
|
if (*lpchT == TEXT('.'))
|
|
goto Exit;
|
|
}
|
|
if (*lpchT == TABCHAR) {
|
|
memmove(lpchT + 1, lpchT, CHARSTOBYTES(cch + 1));
|
|
*lpchT = TEXT('.');
|
|
} else {
|
|
*lpchT++ = TEXT('.');
|
|
*lpchT = 0;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
RtlInitLargeUnicodeString(&str, lpchFile, (UINT)-1);
|
|
TextCopy(&str, lpszPathSpec, (UINT)chCount);
|
|
return fDir;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* DlgDirSelectEx
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
|
|
FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DlgDirSelectExA, HWND, hwndDlg, LPSTR, lpszPathSpec, int, chCount, int, idListBox)
|
|
BOOL DlgDirSelectExA(
|
|
HWND hwndDlg,
|
|
LPSTR lpszPathSpec,
|
|
int chCount,
|
|
int idListBox)
|
|
{
|
|
LPWSTR lpwsz;
|
|
BOOL fRet;
|
|
|
|
lpwsz = (LPWSTR)UserLocalAlloc(HEAP_ZERO_MEMORY, chCount * sizeof(WCHAR));
|
|
if (!lpwsz) {
|
|
return FALSE;
|
|
}
|
|
|
|
fRet = DlgDirSelectHelper(lpwsz, chCount, GetDlgItem(hwndDlg, idListBox));
|
|
|
|
WCSToMB(lpwsz, -1, &lpszPathSpec, chCount, FALSE);
|
|
|
|
UserLocalFree(lpwsz);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
FUNCLOG4(LOG_GENERAL, BOOL, DUMMYCALLINGTYPE, DlgDirSelectExW, HWND, hwndDlg, LPWSTR, lpszPathSpec, int, chCount, int, idListBox)
|
|
BOOL DlgDirSelectExW(
|
|
HWND hwndDlg,
|
|
LPWSTR lpszPathSpec,
|
|
int chCount,
|
|
int idListBox)
|
|
{
|
|
return DlgDirSelectHelper(lpszPathSpec, chCount, GetDlgItem(hwndDlg, idListBox));
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* xxxLbDir
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
/*
|
|
* Note that these FILE_ATTRIBUTE_* values map directly with
|
|
* their DDL_* counterparts, with the exception of FILE_ATTRIBUTE_NORMAL.
|
|
*/
|
|
#define FIND_ATTR ( \
|
|
FILE_ATTRIBUTE_NORMAL | \
|
|
FILE_ATTRIBUTE_DIRECTORY | \
|
|
FILE_ATTRIBUTE_HIDDEN | \
|
|
FILE_ATTRIBUTE_SYSTEM | \
|
|
FILE_ATTRIBUTE_ARCHIVE | \
|
|
FILE_ATTRIBUTE_READONLY )
|
|
#define EXCLUDE_ATTR ( \
|
|
FILE_ATTRIBUTE_DIRECTORY | \
|
|
FILE_ATTRIBUTE_HIDDEN | \
|
|
FILE_ATTRIBUTE_SYSTEM )
|
|
|
|
INT xxxLbDir(
|
|
PLBIV plb,
|
|
UINT attrib,
|
|
LPWSTR lhszFileSpec)
|
|
{
|
|
INT result;
|
|
BOOL fWasVisible, bRet;
|
|
WCHAR Buffer[CCHFILEMAX + 1];
|
|
WCHAR Buffer2[CCHFILEMAX + 1];
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA ffd;
|
|
UINT attribFile;
|
|
DWORD mDrives;
|
|
INT cDrive;
|
|
UINT attribInclMask, attribExclMask;
|
|
|
|
CheckLock(plb->spwnd);
|
|
|
|
/*
|
|
* Make sure the buffer is valid and copy it onto the stack. Why? Because
|
|
* there is a chance that lhszFileSpec is pointing to an invalid string
|
|
* because some app posted a CB_DIR or LB_DIR without the DDL_POSTMSGS
|
|
* bit set.
|
|
*/
|
|
try {
|
|
wcscpy(Buffer2, lhszFileSpec);
|
|
lhszFileSpec = Buffer2;
|
|
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
return -1;
|
|
}
|
|
|
|
result = -1;
|
|
|
|
#ifndef UNICODE
|
|
CharToOem(lhszFileSpec, lhszFileSpec);
|
|
#endif
|
|
|
|
if (fWasVisible = IsLBoxVisible(plb)) {
|
|
SendMessage(HWq(plb->spwnd), WM_SETREDRAW, FALSE, 0);
|
|
}
|
|
|
|
/*
|
|
* First we add the files then the directories and drives.
|
|
* If they only wanted drives then skip the file query
|
|
* Also under Windows specifing only 0x8000 (DDL_EXCLUSIVE) adds no files).
|
|
*/
|
|
|
|
|
|
// if ((attrib != (DDL_EXCLUSIVE | DDL_DRIVES)) && (attrib != DDL_EXCLUSIVE) &&
|
|
if (attrib != (DDL_EXCLUSIVE | DDL_DRIVES | DDL_NOFILES)) {
|
|
hFind = FindFirstFile(lhszFileSpec, &ffd);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
|
|
/*
|
|
* If this is not an exclusive search, include normal files.
|
|
*/
|
|
attribInclMask = attrib & FIND_ATTR;
|
|
if (!(attrib & DDL_EXCLUSIVE))
|
|
attribInclMask |= FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY |
|
|
FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
/*
|
|
* Make a mask of the attributes to be excluded from
|
|
* the search.
|
|
*/
|
|
attribExclMask = ~attrib & EXCLUDE_ATTR;
|
|
|
|
// LATER BUG - scottlu
|
|
// Win3 assumes doing a LoadCursor here will return the same wait cursor that
|
|
// has already been created, whereas calling ServerLoadCursor creates a new
|
|
// one every time!
|
|
// hCursorT = NtUserSetCursor(ServerLoadCursor(NULL, IDC_WAIT));
|
|
|
|
|
|
// FindFirst/Next works different in NT then DOS. Under DOS you passed in
|
|
// a set of attributes under NT you get back a set of attributes and have
|
|
// to test for those attributes (Dos input attributes were Hidden, System
|
|
// and Directoy) the dos find first always returned ReadOnly and archive files
|
|
|
|
// we are going to select a file in one of two cases.
|
|
// 1) if any of the attrib bits are set on the file.
|
|
// 2) if we want normal files and the file is a notmal file (the file attrib
|
|
// bits don't contain any NOEXCLBITS
|
|
|
|
do {
|
|
attribFile = (UINT)ffd.dwFileAttributes;
|
|
if (attribFile == FILE_ATTRIBUTE_COMPRESSED) {
|
|
attribFile = FILE_ATTRIBUTE_NORMAL;
|
|
}
|
|
attribFile &= ~FILE_ATTRIBUTE_COMPRESSED;
|
|
|
|
/*
|
|
* Accept those files that have only the
|
|
* attributes that we are looking for.
|
|
*/
|
|
if ((attribFile & attribInclMask) != 0 &&
|
|
(attribFile & attribExclMask) == 0) {
|
|
if (attribFile & DDL_DIRECTORY) {
|
|
|
|
/*
|
|
* Don't include '.' (current directory) in list.
|
|
*/
|
|
if (*((LPDWORD)&ffd.cFileName[0]) == 0x0000002E)
|
|
goto cfnf;
|
|
|
|
/*
|
|
* If we're not looking for dirs, ignore it
|
|
*/
|
|
if (!(attrib & DDL_DIRECTORY))
|
|
goto cfnf;
|
|
|
|
} else if (attrib & DDL_NOFILES) {
|
|
/*
|
|
* Don't include files if DDL_NOFILES is set.
|
|
*/
|
|
goto cfnf;
|
|
}
|
|
|
|
LB_CreateLBLine(&ffd,
|
|
Buffer);
|
|
result = xxxLBInsertItem(plb, Buffer, 0, MSGFLAG_SPECIAL_THUNK | LBI_ADD);
|
|
}
|
|
cfnf:
|
|
bRet = FindNextFile(hFind, &ffd);
|
|
|
|
} while (result >= -1 && bRet);
|
|
FindClose(hFind);
|
|
|
|
// LATER see above comment
|
|
// NtUserSetCursor(hCursorT);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If drive bit set, include drives in the list.
|
|
*/
|
|
if (result != LB_ERRSPACE && (attrib & DDL_DRIVES)) {
|
|
ffd.cFileName[0] = TEXT('[');
|
|
ffd.cFileName[1] = ffd.cFileName[3] = TEXT('-');
|
|
ffd.cFileName[4] = TEXT(']');
|
|
ffd.cFileName[5] = 0;
|
|
mDrives = GetLogicalDrives();
|
|
for (cDrive = 0; mDrives; mDrives >>= 1, cDrive++) {
|
|
if (mDrives & 1) {
|
|
ffd.cFileName[2] = (WCHAR)(TEXT('A') + cDrive);
|
|
|
|
/*
|
|
* We have to set the SPECIAL_THUNK bit because we are
|
|
* adding a server side string to a list box that may not
|
|
* be HASSTRINGS so we have to force the server-client
|
|
* string thunk.
|
|
*/
|
|
if ((result = xxxLBInsertItem(plb, CharLower(ffd.cFileName), -1,
|
|
MSGFLAG_SPECIAL_THUNK)) < 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == LB_ERRSPACE) {
|
|
xxxNotifyOwner(plb, LB_ERRSPACE);
|
|
}
|
|
|
|
if (fWasVisible) {
|
|
SendMessage(HWq(plb->spwnd), WM_SETREDRAW, TRUE, 0);
|
|
}
|
|
|
|
xxxLBShowHideScrollBars(plb);
|
|
|
|
xxxCheckRedraw(plb, FALSE, 0);
|
|
|
|
if (result != LB_ERRSPACE) {
|
|
|
|
/*
|
|
* Return index of last item in the listbox. We can't just return
|
|
* result because that is the index of the last item added which may
|
|
* be in the middle somewhere if the LBS_SORT style is on.
|
|
*/
|
|
return plb->cMac - 1;
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* xxxLbInsertFile
|
|
*
|
|
* Yet another CraigC shell hack... This responds to LB_ADDFILE messages
|
|
* sent to directory windows in the file system as a response to the
|
|
* WM_FILESYSCHANGE message. That way, we don't reread the whole
|
|
* directory when we copy files.
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
INT xxxLbInsertFile(
|
|
PLBIV plb,
|
|
LPWSTR lpFile)
|
|
{
|
|
WCHAR chBuffer[CCHFILEMAX + 1];
|
|
INT result = -1;
|
|
HANDLE hFind;
|
|
WIN32_FIND_DATA ffd;
|
|
|
|
CheckLock(plb->spwnd);
|
|
|
|
hFind = FindFirstFile(lpFile, &ffd);
|
|
if (hFind != INVALID_HANDLE_VALUE) {
|
|
FindClose(hFind);
|
|
LB_CreateLBLine(&ffd, chBuffer);
|
|
result = xxxLBInsertItem(plb, chBuffer, 0, MSGFLAG_SPECIAL_THUNK | LBI_ADD);
|
|
}
|
|
|
|
if (result == LB_ERRSPACE) {
|
|
xxxNotifyOwner(plb, result);
|
|
}
|
|
|
|
xxxCheckRedraw(plb, FALSE, 0);
|
|
return result;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LB_CreateLBLine
|
|
*
|
|
* This creates a character string that contains all the required
|
|
* details of a file;( Name)
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
void LB_CreateLBLine(
|
|
PWIN32_FIND_DATA pffd,
|
|
LPWSTR lpBuffer)
|
|
{
|
|
BYTE bAttribute;
|
|
LPWSTR lpch;
|
|
|
|
lpch = lpBuffer;
|
|
|
|
bAttribute = (BYTE)pffd->dwFileAttributes;
|
|
if (bAttribute & DDL_DIRECTORY) /* Is it a directory */
|
|
*lpch++ = TEXT('[');
|
|
|
|
/*
|
|
* Copy the file name
|
|
*
|
|
* If we are running from wow, check if the shortname exists
|
|
*/
|
|
if (GetClientInfo()->dwTIFlags & TIF_16BIT) {
|
|
UNICODE_STRING Name;
|
|
BOOLEAN fSpace = FALSE;
|
|
|
|
RtlInitUnicodeString(&Name, pffd->cFileName);
|
|
if (RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace) {
|
|
/*
|
|
* Legal 8.3 name and no spaces, so use the principal
|
|
* file name.
|
|
*/
|
|
wcscpy(lpch, pffd->cFileName);
|
|
} else {
|
|
if (pffd->cAlternateFileName[0] == 0)
|
|
wcscpy(lpch, pffd->cFileName);
|
|
else
|
|
/*
|
|
* Use the alternate file name.
|
|
*/
|
|
wcscpy(lpch, pffd->cAlternateFileName);
|
|
}
|
|
/*
|
|
* Make filename lower-case for 16-bit apps. Some Corel apps
|
|
* require this.
|
|
*/
|
|
CharLower(lpch);
|
|
|
|
}
|
|
else
|
|
wcscpy(lpch, pffd->cFileName);
|
|
|
|
lpch = (LPWSTR)(lpch + wcslen(lpch));
|
|
|
|
if (bAttribute & DDL_DIRECTORY) /* Is it a directory */
|
|
*lpch++ = TEXT(']');
|
|
|
|
*lpch = TEXT('\0');
|
|
|
|
#ifndef UNICODE
|
|
OemToChar(lpBuffer, lpBuffer);
|
|
#endif
|
|
|
|
*lpch = TEXT('\0'); /* Null terminate */
|
|
}
|