windows-nt/Source/XPSP1/NT/inetsrv/iis/admin/common2/filechooser.cpp
2020-09-26 16:20:57 +08:00

769 lines
17 KiB
C++

//
// FileChooser.cpp
//
#include "stdafx.h"
#include "common.h"
#include "FileChooser.h"
#include <Shlwapi.h>
#include <shellapi.h>
#include <ShlObj.h>
#include <CommDlg.h>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
const TCHAR QuotMark = _T('\"');
const TCHAR AllExt[] = _T(".*");
//---------------------
BOOL
CFileChooser::Init(CWindow * pParent, DWORD dwStyle, UINT idEdit, UINT idButton)
{
ASSERT(NULL != pParent);
ASSERT(NULL != pParent->GetDlgItem(idEdit));
ASSERT(NULL != pParent->GetDlgItem(idButton));
m_pParent = pParent;
SetStyle(dwStyle);
VERIFY(m_edit.SubclassWindow(pParent->GetDlgItem(idEdit)));
if (StyleBitSet(FC_AUTOCOMPLETION))
{
SHAutoComplete(pParent->GetDlgItem(idEdit), SHACF_FILESYSTEM);
}
VERIFY(m_button.SubclassWindow(pParent->GetDlgItem(idButton)));
return FALSE;
}
// External SetPath
void
CFileChooser::SetPath(const CString& path)
{
m_strPath = path;
if (OpenForRead() && StyleBitSet(FC_PREPARE_DEFAULT))
CreateDefaultPathForRead();
SetPathToEdit(m_strPath);
m_bEditDirty = FALSE;
}
BOOL
CFileChooser::HasEditFocus()
{
return GetFocus() == m_edit.m_hWnd;
}
void
CFileChooser::SetPathToEdit(LPCTSTR path)
{
if (HasEditFocus())
{
m_edit.SetWindowText(path);
}
else
{
SetCompactedPath(path);
}
}
void
CFileChooser::CreateDefaultPathForRead()
{
if (!PathFileExists(m_strPath))
{
// try to find first file with the first extension
// from the extensions list
BOOL bDefaultSet = FALSE;
BOOL bPathEmpty = m_strPath.IsEmpty();
TCHAR find_str[MAX_PATH];
WIN32_FIND_DATA find_data;
if (bPathEmpty)
{
GetCurrentDirectory(MAX_PATH, find_str);
m_strPath = find_str;
}
else
{
StrCpy(find_str, m_strPath);
if (!PathIsDirectory(find_str))
{
PathRemoveFileSpec(find_str);
}
}
PathAppend(find_str, _T("*"));
std::list<CFilterEntry>::iterator it;
for (it = m_ext.begin(); it != m_ext.end(); it++)
{
CString ext = (*it).m_ext;
PathAddExtension(find_str, ext);
HANDLE hFind = FindFirstFile(find_str, &find_data);
if ( hFind != INVALID_HANDLE_VALUE
&& (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0
)
{
if (!bPathEmpty)
{
TCHAR buf[MAX_PATH];
StrCpy(buf, m_strPath);
PathRemoveFileSpec(buf);
m_strPath = buf;
}
m_strPath += find_data.cFileName;
FindClose(hFind);
bDefaultSet = TRUE;
break;
}
}
if (!bDefaultSet && StyleBitSet(FC_WILDCARD_DEFAULT))
{
// if nothing found, just attach *.ext to the path
// find_str was prepared before as xxx\*.
m_strPath = find_str;
if (!m_ext.empty())
{
m_strPath += m_ext.front().m_ext;
}
else
{
m_strPath += _T("*");
}
}
}
}
BOOL
CFileChooser::IsValidChar(UINT nChar, BOOL bExcludeWildcards)
{
switch (PathGetCharType((TCHAR)nChar))
{
case GCT_INVALID:
return FALSE;
case GCT_WILD:
return !bExcludeWildcards;
case GCT_LFNCHAR:
case GCT_SEPARATOR:
case GCT_SHORTCHAR:
break;
}
return TRUE;
}
BOOL
CFileChooser::IsValidPath(LPCTSTR path)
{
UINT len = lstrlen(path);
BOOL bRes = TRUE;
for (UINT i = 0; i < len; i++)
{
TCHAR c = path[i];
if (!IsValidChar(c))
{
bRes = FALSE;
break;
}
}
return bRes;
}
// Character filtering routine for the edit control.
// Returns TRUE if character should be passed to the CEdit
//
LRESULT
CFileChooser::OnEditChar(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
UINT nChar = wParam;
UINT nRepCount = (UINT)lParam & 0xFFFF;
UINT nFlags = (UINT)((lParam & 0xFFFF0000) >> 16);
bHandled = TRUE;
if (IsValidChar(nChar))
{
bHandled = FALSE;
}
else
{
switch (nChar)
{
case VK_DELETE:
case VK_BACK:
case _T('/'):
bHandled = FALSE;
break;
case _T('"'):
bHandled = !StyleBitSet(FC_COMMANDLINE);
break;
}
}
m_bEditDirty = !bHandled;
return 0;
}
// Text was pasted to edit control
void
CFileChooser::OnPaste()
{
TCHAR buf[MAX_PATH];
int len = m_edit.GetWindowText(buf, MAX_PATH);
for (int i = 0; i < len || IsValidChar(buf[i]); i++)
;
if (i < len)
{
m_edit.SendMessage(EM_SETSEL, i, len - 1);
m_bTextValid = FALSE;
m_bEditDirty = FALSE;
}
else
{
m_strPath = buf;
SetPathToEdit(buf);
m_bEditDirty = TRUE;
}
}
void
CFileChooser::OnEditChange()
{
if (!m_bInternalChange)
{
m_bEditDirty = TRUE;
}
}
LRESULT
CFileChooser::OnEditSetFocus(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_bInternalChange = TRUE;
m_edit.SetWindowText(m_strPath);
m_bInternalChange = FALSE;
bHandled = FALSE;
return 0;
}
LRESULT
CFileChooser::OnEditKillFocus(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// update internal string buffer with path
TCHAR buf[MAX_PATH];
ZeroMemory(buf, MAX_PATH);
if (m_bEditDirty)
{
m_edit.GetWindowText(buf, MAX_PATH);
m_strPath = buf;
}
m_bInternalChange = TRUE;
SetCompactedPath(m_strPath);
m_bInternalChange = FALSE;
m_bEditDirty = FALSE;
bHandled = FALSE;
return 0;
}
LRESULT
CFileChooser::OnSetBrowseState(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// We are producing dialog on the way back
if (!wParam)
{
OnBrowseBtn();
}
bHandled = FALSE;
return 0;
}
void
CFileChooser::SetCompactedPath(LPCTSTR path)
{
// compact path before writing to edit
CRect rc;
m_edit.GetClientRect(&rc);
HDC dc = m_edit.GetDC();
TCHAR buf[MAX_PATH] = {0};
StrCpy(buf, path);
PathMakePretty(buf);
PathCompactPath(dc, buf, rc.Width());
m_edit.ReleaseDC(dc);
m_edit.SetWindowText(buf);
}
DWORD
CFileChooser::GetFileName(CString& strFile)
{
DWORD dwRes = FC_SUCCESS;
TCHAR str[MAX_PATH];
BOOL expanded = FALSE;
if ( !m_bTextValid
|| FC_SUCCESS != ExtractPath(str)
|| !IsValidPath(str)
)
return FC_TEXT_IS_INVALID;
if (StyleBitSet(FC_PATH_CHECK))
{
if (OpenForRead())
{
TCHAR str_exp[MAX_PATH];
lstrcpy(str_exp, str);
DoEnvironmentSubst(str_exp, MAX_PATH);
expanded = lstrcmpi(str, str_exp) != 0;
if (!PathFileExists(str_exp) && !PathIsDirectory(str_exp))
{
BOOL bFound = FALSE;
if (StyleBitSet(FC_CHECK_FILENAME_ONLY))
{
// try with default extension(s) if it is just filename
// without any extensions
LPTSTR p = PathFindExtension(str_exp);
if (p != NULL && *p == 0)
{
CString strExt, strTest = str_exp;
std::list<CFilterEntry>::iterator it;
for (it = m_ext.begin(); it != m_ext.end(); it++)
{
strExt = (*it).m_ext;
if (PathFileExists(strTest + strExt))
{
StrCat(str, strExt);
bFound = TRUE;
break;
}
}
}
}
if (!bFound)
dwRes = FC_FILE_DOES_NOT_EXIST;
}
else if (PathIsDirectory(str_exp))
{
if (!StyleBitSet(FC_DIRECTORY_ONLY))
{
PathAddBackslash(str);
dwRes = FC_FILENAME_IS_DIRECTORY;
}
}
else if (StyleBitSet(FC_DIRECTORY_ONLY))
{
if (PathFileExists(str_exp))
dwRes = FC_FILENAME_IS_FILE;
}
}
else if (StyleBitSet(FC_FORWRITE))
{
// TODO: make sure we have write access to this path
}
}
if (dwRes == FC_SUCCESS)
{
if (StyleBitSet(FC_COMMANDLINE) || expanded)
{
// We are returning whole command line, get it again
GetText(str);
}
strFile = str;
}
return dwRes;
}
BOOL
CFileChooser::BrowseForFile(CString& strPath, CString& strFile)
{
BOOL bRes = FALSE;
OPENFILENAME ofn;
TCHAR buf[MAX_PATH];
ZeroMemory(&ofn, sizeof(OPENFILENAME));
StrCpy(buf, strFile);
ofn.lStructSize = sizeof(OPENFILENAME);
// We are not using template
ofn.hInstance = NULL;
ofn.Flags |= m_ofn_Flags;
ofn.Flags |= OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
if (OpenForRead())
ofn.Flags |= OFN_FILEMUSTEXIST;
else
ofn.Flags |= (OFN_NOREADONLYRETURN | OFN_NOTESTFILECREATE | OFN_HIDEREADONLY);
#if (_WIN32_WINNT >= 0x0500)
ofn.FlagsEx &= ~(OFN_EX_NOPLACESBAR);
#endif
// Create filter using our extensions list
CString strFilter, strDefExt;
CreateFilter(strFilter, strDefExt);
ofn.lpstrDefExt = strDefExt;
ofn.lpstrFile = buf;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrInitialDir = strPath.IsEmpty() ? NULL : (LPCTSTR)strPath;
ofn.lpstrFilter = strFilter;
ofn.nFilterIndex = GetFilterIndex(strFile);
// We better set the owner, or this dialog will be visible on task bar
ofn.hwndOwner = m_pParent->m_hWnd;
ofn.lpstrTitle = m_strTitle;
if (StyleBitSet(FC_HIDEREADONLY))
ofn.Flags |= OFN_HIDEREADONLY;
if (!StyleBitSet(FC_FORWRITE))
bRes = GetOpenFileName(&ofn);
else
bRes = GetSaveFileName(&ofn);
if (bRes)
{
m_bDoReplaceFile = TRUE;
}
else
{
#ifdef _DEBUG
DWORD dwError;
ASSERT(0 == (dwError = CommDlgExtendedError()));
#endif
}
strFile = buf;
return bRes;
}
static int CALLBACK
FileChooserCallback(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
CFileChooser * pThis = (CFileChooser *)lpData;
ASSERT(pThis != NULL);
return pThis->BrowseForFolderCallback(hwnd, uMsg, lParam);
}
int CFileChooser::BrowseForFolderCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
{
switch (uMsg)
{
case BFFM_INITIALIZED:
ASSERT(m_pPathTemp != NULL);
if (::PathIsNetworkPath(m_pPathTemp))
return 0;
while (!::PathIsDirectory(m_pPathTemp))
{
if (0 == ::PathRemoveFileSpec(m_pPathTemp) && !::PathIsRoot(m_pPathTemp))
{
return 0;
}
DWORD attr = GetFileAttributes(m_pPathTemp);
if (StyleBitSet(FC_FORWRITE) && (attr & FILE_ATTRIBUTE_READONLY) == 0)
break;
}
::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)m_pPathTemp);
break;
case BFFM_SELCHANGED:
{
LPITEMIDLIST pidl = (LPITEMIDLIST)lParam;
TCHAR path[MAX_PATH];
if (SHGetPathFromIDList(pidl, path))
{
::SendMessage(hwnd, BFFM_ENABLEOK, 0, !PathIsNetworkPath(path));
}
}
break;
case BFFM_VALIDATEFAILED:
break;
}
return 0;
}
BOOL
CFileChooser::BrowseForFolder(CString& strPath)
{
LPITEMIDLIST pidl = NULL;
HRESULT hr;
BOOL bRes = FALSE;
if (SUCCEEDED(hr = CoInitialize(NULL)))
{
if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_DRIVES, NULL, 0, &pidl)))
{
LPITEMIDLIST pidList = NULL;
BROWSEINFO bi;
TCHAR buf[MAX_PATH];
ZeroMemory(&bi, sizeof(bi));
int drive = PathGetDriveNumber(strPath);
if (GetDriveType(PathBuildRoot(buf, drive)) == DRIVE_FIXED)
{
StrCpy(buf, strPath);
}
else
{
buf[0] = 0;
}
bi.hwndOwner = m_pParent->m_hWnd;
bi.pidlRoot = pidl;
bi.pszDisplayName = m_pPathTemp = buf;
bi.lpszTitle = m_strTitle;
bi.ulFlags |= BIF_NEWDIALOGSTYLE | BIF_RETURNONLYFSDIRS/* | BIF_EDITBOX*/;
bi.lpfn = FileChooserCallback;
bi.lParam = (LPARAM)this;
pidList = SHBrowseForFolder(&bi);
if ( pidList != NULL
&& SHGetPathFromIDList(pidList, buf)
)
{
strPath = buf;
bRes = TRUE;
}
IMalloc * pMalloc;
VERIFY(SUCCEEDED(SHGetMalloc(&pMalloc)));
if (pidl != NULL)
pMalloc->Free(pidl);
pMalloc->Release();
}
CoUninitialize();
}
return bRes;
}
void
CFileChooser::AddExtension(HINSTANCE hInst, UINT idText, UINT idExt)
{
CString text, ext;
if (text.LoadString(hInst, idText) && ext.LoadString(hInst, idExt))
{
AddExtension(text, ext);
}
}
void
CFileChooser::AddExtension(LPCTSTR text, LPCTSTR ext)
{
ASSERT(ext != NULL && *ext == _T('.'));
CFilterEntry entry(text, ext);
m_ext.push_back(entry);
}
const TCHAR cDelimiter = _T('\n');
void
CFileChooser::CreateFilter(CString& strFilter, CString& strDefExt)
{
strFilter.Empty();
strDefExt.Empty();
BOOL bExtDone = FALSE;
std::list<CFilterEntry>::iterator it;
for (it = m_ext.begin(); it != m_ext.end(); it++)
{
CFilterEntry entry = (*it);
strFilter += entry.m_text;
if (m_dwStyle & FC_WILDCARD_DESC)
{
strFilter += _T(" (*");
strFilter += entry.m_ext;
strFilter += _T(")");
}
strFilter += cDelimiter;
strFilter += _T('*');
strFilter += entry.m_ext;
strFilter += cDelimiter;
if (!bExtDone)
{
LPCTSTR pExt = entry.m_ext;
strDefExt =
*pExt == _T('.') ? pExt + 1 : pExt;
bExtDone = TRUE;
}
}
if (!strFilter.IsEmpty())
{
strFilter += cDelimiter;
for (int i = 0; i < strFilter.GetLength(); i++)
{
if (strFilter[i] == cDelimiter)
strFilter.SetAt(i, 0);
}
}
}
int
CFileChooser::GetFilterIndex(const CString& fileName)
{
LPTSTR p = PathFindExtension(fileName);
if (p == NULL)
p = (LPTSTR)AllExt;
std::list<CFilterEntry>::iterator it;
int idx = 1;
for (it = m_ext.begin(); it != m_ext.end(); it++, idx++)
{
if (StrCmpI((*it).m_ext, p) == 0)
return idx;
}
return 0;
}
void
CFileChooser::GetText(LPTSTR buf)
{
ASSERT(buf != NULL);
if (m_bEditDirty)
{
m_edit.GetWindowText(buf, MAX_PATH);
}
else
{
StrCpy(buf, m_strPath);
}
}
int
CFileChooser::ExtractPath(LPTSTR path)
{
ASSERT(path != NULL);
int rc = FC_SUCCESS;
TCHAR buf[MAX_PATH] = {0};
LPTSTR start = buf;
GetText(buf);
if (StyleBitSet(FC_COMMANDLINE))
{
if (*buf == QuotMark)
{
LPTSTR end = StrChr(++start, QuotMark);
if (end == NULL)
{
// Wrong format, closing quotation mark is not set
rc = FC_NO_CLOSING_QUOTE;
// Return part of the path up to first space
PathRemoveArgs(buf);
}
else
{
++end;
*end = 0;
PathUnquoteSpaces(buf);
start = buf;
}
}
else
{
PathRemoveArgs(buf);
}
}
StrCpy(path, start);
return rc;
}
int
CFileChooser::ExtractArgs(LPTSTR buf)
{
ASSERT(buf != NULL);
int rc = FC_SUCCESS;
GetText(buf);
LPTSTR p = PathGetArgs(buf);
if (p != NULL)
{
StrCpy(buf, p);
}
else
{
*buf = 0;
}
return rc;
}
void
CFileChooser::OnBrowseBtn()
{
BOOL bRes = FALSE;
if (m_bDialogActive)
return;
m_bDialogActive = TRUE;
TCHAR path[MAX_PATH] = {0};
TCHAR args[MAX_PATH] = {0};
int rc = ExtractPath(path);
if (StyleBitSet(FC_COMMANDLINE))
{
ExtractArgs(args);
}
CString strFile, strBuffer;
// m_strPath = path;
strBuffer = path;
if (StyleBitSet(FC_FORWRITE))
{
if (!PathIsDirectory(path))
{
if (PathRemoveFileSpec(path))
{
// check if path part of filename exists
if (PathIsDirectory(path))
{
// we will use non-path part of spec as a filename
strFile = PathFindFileName(strBuffer);
}
else
{
// it is wrong path, use default one
// TODO: actually I need to take from filespec all existent
// chunks of path and filename, for example c:\aa\bb\cc\dd.txt,
// if c:\aa\bb exists, then strPath should be set to c:\aa\bb,
// and strFile to dd.txt
path[0] = 0;
}
}
else
{
// it is filename only
strFile = strBuffer;
path[0] = 0;
}
}
}
else
{
if (!PathIsDirectory(path))
{
strFile = PathFindFileName(path);
PathRemoveFileSpec(path);
}
}
CString strPath(path);
if (StyleBitSet(FC_DIRECTORY_ONLY))
{
bRes = BrowseForFolder(strPath);
if (bRes)
{
StrCpy(path, strPath);
}
}
else
{
bRes = BrowseForFile(strPath, strFile);
if (bRes)
{
StrCpy(path, strFile);
}
}
if (bRes)
{
if (StyleBitSet(FC_COMMANDLINE))
{
PathQuoteSpaces(path);
m_strPath = path;
if (*args != 0)
{
m_strPath += _T(' ');
m_strPath += args;
}
}
else
m_strPath = path;
SetPathToEdit(m_strPath);
m_bEditDirty = FALSE;
}
m_bDialogActive = FALSE;
}