#include "ctlspriv.h" #pragma hdrstop #include "usrctl32.h" #include "listbox.h" //---------------------------------------------------------------------------// // // Directory ListBox Routines // //---------------------------------------------------------------------------// // // ListBox_CreateLine // // This creates a character string that contains all the required // details of a file;( Name) // VOID ListBox_CreateLine(PWIN32_FIND_DATA pffd, LPWSTR pszBuffer, DWORD cchBuffer) { BOOL fDirectory = TESTFLAG(pffd->dwFileAttributes, DDL_DIRECTORY); StringCchPrintf(pszBuffer, cchBuffer, TEXT("%s%s%s"), fDirectory ? TEXT("[") : TEXT(""), pffd->cFileName, fDirectory ? TEXT("]") : TEXT("")); } //---------------------------------------------------------------------------// // // ListBox_DirHandler // // 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 ListBox_DirHandler(PLBIV plb, UINT attrib, LPWSTR pszFileSpec) { INT result; BOOL fWasVisible, bRet; WCHAR szBuffer[MAX_PATH + 1]; WCHAR szBuffer2[MAX_PATH + 1]; HANDLE hFind; WIN32_FIND_DATA ffd; UINT attribFile; DWORD mDrives; INT cDrive; UINT attribInclMask, attribExclMask; // // Make sure the buffer is valid and copy it onto the stack. Why? Because // there is a chance that pszFileSpec is pointing to an invalid string // because some app posted a CB_DIR or LB_DIR without the DDL_POSTMSGS // bit set. // try { StringCchCopy(szBuffer2, ARRAYSIZE(szBuffer2), pszFileSpec); pszFileSpec = szBuffer2; } except (UnhandledExceptionFilter( GetExceptionInformation() )) { return -1; } __endexcept result = -1; fWasVisible = IsLBoxVisible(plb); if (fWasVisible) { SendMessage(plb->hwnd, 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(pszFileSpec, &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) { BOOL fCreate = TRUE; if (attribFile & DDL_DIRECTORY) { // // Don't include '.' (current directory) in list. // if (*((LPDWORD)&ffd.cFileName[0]) == 0x0000002E) { fCreate = FALSE; } // // If we're not looking for dirs, ignore it // if (!(attrib & DDL_DIRECTORY)) { fCreate = FALSE; } } else if (attrib & DDL_NOFILES) { // // Don't include files if DDL_NOFILES is set. // fCreate = FALSE; } if (fCreate) { ListBox_CreateLine(&ffd, szBuffer, ARRAYSIZE(szBuffer)); result = ListBox_InsertItem(plb, szBuffer, 0, LBI_ADD); } } 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 = ListBox_InsertItem(plb, CharLower(ffd.cFileName), -1, 0)) < 0) { break; } } } } if (result == LB_ERRSPACE) { ListBox_NotifyOwner(plb, LB_ERRSPACE); } if (fWasVisible) { SendMessage(plb->hwnd, WM_SETREDRAW, TRUE, 0); } ListBox_ShowHideScrollBars(plb); ListBox_CheckRedraw(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; } } //---------------------------------------------------------------------------// // // ListBox_InsertFile // // 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. // INT ListBox_InsertFile(PLBIV plb, LPWSTR lpFile) { WCHAR szBuffer[MAX_PATH + 1]; INT result = -1; HANDLE hFind; WIN32_FIND_DATA ffd; hFind = FindFirstFile(lpFile, &ffd); if (hFind != INVALID_HANDLE_VALUE) { FindClose(hFind); ListBox_CreateLine(&ffd, szBuffer, ARRAYSIZE(szBuffer)); result = ListBox_InsertItem(plb, szBuffer, 0, LBI_ADD); } if (result == LB_ERRSPACE) { ListBox_NotifyOwner(plb, result); } ListBox_CheckRedraw(plb, FALSE, 0); return result; } //---------------------------------------------------------------------------// // // Public ListBox APIs support. // // Uncomment the following to include support for these //#define INCLUDE_LISTBOX_FUNCTIONS #ifdef INCLUDE_LISTBOX_FUNCTIONS //---------------------------------------------------------------------------// // // Defines and common macros // #define TABCHAR TEXT('\t') #define DDL_PRIVILEGES (DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_ARCHIVE) #define DDL_TYPE (DDL_DRIVES | DDL_DIRECTORY | DDL_POSTMSGS) #define CHARSTOBYTES(cch) ((cch) * sizeof(TCHAR)) #define CCH_CHOPTEXT_EXTRA 7 #define AWCHLEN(a) ((sizeof(a)/sizeof(a[0])) - 1) //---------------------------------------------------------------------------// // // Globals // WCHAR awchSlashStar[] = L"\\*"; CHAR achSlashStar[] = "\\*"; WCHAR szSLASHSTARDOTSTAR[] = TEXT("\\*"); /* This is a single "\" */ //---------------------------------------------------------------------------// // // 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. // // 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. // LPWSTR ChopText(HWND hwndDlg, INT idStatic, LPWSTR lpchBuffer) { HWND hwndStatic; LPWSTR lpszRet; lpszRet = NULL; // // Get length of static field. // hwndStatic = GetDlgItem(hwndDlg, idStatic); if (hwndStatic) { // // 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; PSTAT pstat; HDC hdc; HFONT hOldFont; INT cchPath; LPWSTR lpch; LPWSTR lpchPath; TCHAR szClassName[MAX_PATH]; GetClientRect(hwndStatic, &rc); cxField = rc.right - rc.left; // // Set up DC appropriately for the static control. // hdc = GetDC(hwndStatic); // // Only assume this is a static window if this window uses the // static window wndproc. // hOldFont = NULL; if (GetClassName(hwndStatic, szClassName, ARRAYSIZE(szClassName) && lstrcmpi(WC_STATIC, szClassName) == 0)) { pstat = Static_GetPtr(hwndStatic); 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)); } 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)); break; } } } } if (hOldFont) { SelectObject(hdc, hOldFont); } ReleaseDC(hwndStatic, hdc); lpszRet = lpch; } return lpszRet; } //---------------------------------------------------------------------------// // // DlgDirListHelper // // NOTE: If idStaticPath is < 0, then that parameter contains the details // about what should be in each line of the list box // DWORD FindCharPosition(LPWSTR lpString, WCHAR ch) { DWORD dwPos = 0L; while (*lpString && *lpString != ch) { ++lpString; ++dwPos; } return dwPos; } //---------------------------------------------------------------------------// BOOL DlgDirListHelper( HWND hwndDlg, LPWSTR lpszPathSpec, LPBYTE lpszPathSpecClient, INT idListBox, INT idStaticPath, UINT attrib, BOOL fListBox) // Listbox or ComboBox? { HWND hwndLB; BOOL fDir = TRUE; BOOL fRoot, bRet; BOOL fPostIt; INT cch; WCHAR ch; WCHAR szStaticPath[CCH_CHOPTEXT_EXTRA + MAX_PATH]; LPWSTR pszCurrentDir; UINT wDirMsg; LPWSTR lpchFile; LPWSTR lpchDirectory; PLBIV plb; BOOL fWasVisible = FALSE; BOOL fWin40Compat; PCBOX pcbox; BOOL bResult; bResult = FALSE; // // Strip the private bit DDL_NOFILES out - KidPix passes it in my mistake! // if (attrib & ~DDL_VALID) { TraceMsg(TF_STANDARD, "Invalid flags, %x & ~%x != 0", attrib, DDL_VALID); bResult = FALSE; } else { if (attrib & DDL_NOFILES) { TraceMsg(TF_STANDARD, "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 (!hwndDlg && (idStaticPath || idListBox)) { TraceMsg(TF_STANDARD, "Passed NULL hwnd but valide control id"); bResult = FALSE; } else { 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. // hwndLB = GetDlgItem(hwndDlg, idListBox); if (hwndLB) { TCHAR szClassName[MAX_PATH]; szClassName[0] = 0; GetClassName(hwndLB, szClassName, ARRAYSIZE(szClassName)); if (((lstrcmpi(WC_LISTBOX, szClassName) == 0) && fListBox) || ((lstrcmpi(WC_COMBOBOX, szClassName) == 0) && !fListBox)) { if (fListBox) { plb = ListBox_GetPtr(hwndLB); } else { pcbox = ComboBox_GetPtr(hwndLB); plb = ListBox_GetPtr(pcbox->hwndList); } } else { TraceMsg(TF_STANDARD, "Listbox not found in hwnd = %#.4x", hwndDlg); } } 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. // TraceMsg(TF_STANDARD, "Listbox control id = %d not found in hwnd = %#.4x", idListBox, hwndDlg); } 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 > MAX_PATH) { 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) { TraceMsg(TF_ERROR, "No Wildcard characters"); 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); } // REVIEW: API assumes lpszPathSpec is large enough buffer StringCchCopy(lpszPathSpec, lstrlen(lpchFile)+1, 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(ARRAYSIZE(szStaticPath)-CCH_CHOPTEXT_EXTRA, pszCurrentDir); // // 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(hwndDlg, idStaticPath, ChopText(hwndDlg, idStaticPath, szStaticPath)); } // // Fill in the directory List/ComboBox if it exists. // if (idListBox && hwndLB != NULL) { 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(hwndLB, 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); InvalidateRect(hwndLB, NULL, TRUE); } } bResult = TRUE; } } return bResult; } //---------------------------------------------------------------------------// BOOL DlgDirListA( HWND hwndDlg, LPSTR lpszPathSpecClient, INT idListBox, INT idStaticPath, UINT attrib) { LPWSTR lpszPathSpec; BOOL fRet; fRet = FALSE; if (hwndDlg) { lpszPathSpec = NULL; if (lpszPathSpecClient && (!MBToWCS(lpszPathSpecClient, -1, &lpszPathSpec, -1, TRUE)) ) { fRet = FALSE; } else { // // The last parameter is TRUE to indicate ListBox (not ComboBox) // fRet = DlgDirListHelper(hwndDlg, lpszPathSpec, lpszPathSpecClient, idListBox, idStaticPath, attrib, TRUE); 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; } //---------------------------------------------------------------------------// BOOL DlgDirListW( HWND hwndDlg, LPWSTR lpszPathSpecClient, INT idListBox, INT idStaticPath, UINT attrib) { LPWSTR lpszPathSpec; BOOL fRet; fRet = FALSE; if (hwndDlg) { lpszPathSpec = lpszPathSpecClient; // // The last parameter is TRUE to indicate ListBox (not ComboBox) // fRet = DlgDirListHelper(hwndDlg, lpszPathSpec, (LPBYTE)lpszPathSpecClient, idListBox, idStaticPath, attrib, TRUE); } return fRet; } //---------------------------------------------------------------------------// BOOL DlgDirSelectHelper( LPWSTR lpszPathSpec, INT chCount, HWND hwndListBox) { INT cch; LPWSTR lpchFile; BOOL fDir; INT sItem; LPWSTR lpchT; WCHAR rgch[MAX_PATH + 2]; INT cchT; LARGE_UNICODE_STRING str; BOOL bRet; bRet = FALSE; // // Callers such as DlgDirSelectEx do not validate the existance // of hwndListBox // if (hwndListBox == NULL) { TraceMsg(TF_STANDARD, "Controls Id not found"); bRet = FALSE; } else { sItem = (INT)SendMessage(hwndListBox, LB_GETCURSEL, 0, 0L); if (sItem < 0) { bRet = FALSE; } else { 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('.')) { break; } } if (*lpchT == TABCHAR) { _memmove(lpchT + 1, lpchT, CHARSTOBYTES(cch + 1)); *lpchT = TEXT('.'); } else if (cch <= 0) { *lpchT++ = TEXT('.'); *lpchT = 0; } } bRet = fDir; } } RtlInitLargeUnicodeString(&str, lpchFile, (UINT)-1); TextCopy(&str, lpszPathSpec, (UINT)chCount); return bRet; } //---------------------------------------------------------------------------// 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) { fRet = FALSE; } else { fRet = DlgDirSelectHelper(lpwsz, chCount, GetDlgItem(hwndDlg, idListBox)); WCSToMB(lpwsz, -1, &lpszPathSpec, chCount, FALSE); UserLocalFree(lpwsz); } return fRet; } //---------------------------------------------------------------------------// BOOL DlgDirSelectExW( HWND hwndDlg, LPWSTR lpszPathSpec, INT chCount, INT idListBox) { return DlgDirSelectHelper(lpszPathSpec, chCount, GetDlgItem(hwndDlg, idListBox)); } #endif // INCLUDE_LISTBOX_FUNCTIONS