mirror of
https://github.com/notepad-plus-plus/notepad-plus-plus.git
synced 2025-07-26 07:15:21 +02:00
Fix miss-treating browsing folder as saving file issue in FileDialog
Improve file name parsing. Use the absolute path to check if a file name is a directory. Expand environment variables if they are part of the file name. Avoid unnecessary calls to onPreFileOk(). Close #9467
This commit is contained in:
parent
c677b15d82
commit
5a45674c36
@ -98,6 +98,44 @@ namespace // anonymous
|
|||||||
return pos != s.npos && ((s.length() - pos) == suffix.length());
|
return pos != s.npos && ((s.length() - pos) == suffix.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void expandEnv(generic_string& s)
|
||||||
|
{
|
||||||
|
TCHAR buffer[MAX_PATH] = { 0 };
|
||||||
|
// This returns the resulting string length or 0 in case of error.
|
||||||
|
DWORD ret = ExpandEnvironmentStrings(s.c_str(), buffer, static_cast<DWORD>(std::size(buffer)));
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
if (ret == static_cast<DWORD>(lstrlen(buffer) + 1))
|
||||||
|
{
|
||||||
|
s = buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Buffer was too small, try with a bigger buffer of the required size.
|
||||||
|
std::vector<TCHAR> buffer2(ret, 0);
|
||||||
|
ret = ExpandEnvironmentStrings(s.c_str(), buffer2.data(), static_cast<DWORD>(buffer2.size()));
|
||||||
|
assert(ret == static_cast<DWORD>(lstrlen(buffer2.data()) + 1));
|
||||||
|
s = buffer2.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generic_string getFilename(IShellItem* psi)
|
||||||
|
{
|
||||||
|
generic_string result;
|
||||||
|
if (psi)
|
||||||
|
{
|
||||||
|
PWSTR pszFilePath = nullptr;
|
||||||
|
HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||||
|
if (SUCCEEDED(hr) && pszFilePath)
|
||||||
|
{
|
||||||
|
result = pszFilePath;
|
||||||
|
CoTaskMemFree(pszFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool setDialogFolder(IFileDialog* dialog, const TCHAR* folder)
|
bool setDialogFolder(IFileDialog* dialog, const TCHAR* folder)
|
||||||
{
|
{
|
||||||
IShellItem* psi = nullptr;
|
IShellItem* psi = nullptr;
|
||||||
@ -112,13 +150,26 @@ namespace // anonymous
|
|||||||
|
|
||||||
generic_string getDialogFileName(IFileDialog* dialog)
|
generic_string getDialogFileName(IFileDialog* dialog)
|
||||||
{
|
{
|
||||||
|
generic_string fileName;
|
||||||
PWSTR pszFilePath = nullptr;
|
PWSTR pszFilePath = nullptr;
|
||||||
dialog->GetFileName(&pszFilePath);
|
HRESULT hr = dialog->GetFileName(&pszFilePath);
|
||||||
generic_string fileName = pszFilePath;
|
if (SUCCEEDED(hr) && pszFilePath)
|
||||||
|
{
|
||||||
|
fileName = pszFilePath;
|
||||||
CoTaskMemFree(pszFilePath);
|
CoTaskMemFree(pszFilePath);
|
||||||
|
}
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generic_string getDialogFolder(IFileDialog* dialog)
|
||||||
|
{
|
||||||
|
com_ptr<IShellItem> psi;
|
||||||
|
HRESULT hr = dialog->GetFolder(&psi);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
return getFilename(psi);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Backups the current directory in constructor and restores it in destructor.
|
// Backups the current directory in constructor and restores it in destructor.
|
||||||
// This is needed in case dialog changes the current directory.
|
// This is needed in case dialog changes the current directory.
|
||||||
class CurrentDirBackup
|
class CurrentDirBackup
|
||||||
@ -283,6 +334,19 @@ private:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generic_string getAbsPath(const generic_string& fileName)
|
||||||
|
{
|
||||||
|
if (::PathIsRelative(fileName.c_str()))
|
||||||
|
{
|
||||||
|
TCHAR buffer[MAX_PATH] = { 0 };
|
||||||
|
const generic_string folder = getDialogFolder(_dialog);
|
||||||
|
LPTSTR ret = ::PathCombine(buffer, folder.c_str(), fileName.c_str());
|
||||||
|
if (ret)
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
// Called after the user input but before OnFileOk() and before any name validation.
|
// Called after the user input but before OnFileOk() and before any name validation.
|
||||||
// Prefer SendMessage communication with the edit box here rather than IFileDialog methods.
|
// Prefer SendMessage communication with the edit box here rather than IFileDialog methods.
|
||||||
// The setter methods post the message to the queue, and it may not be processed in time.
|
// The setter methods post the message to the queue, and it may not be processed in time.
|
||||||
@ -292,9 +356,10 @@ private:
|
|||||||
return;
|
return;
|
||||||
// Get the entered name.
|
// Get the entered name.
|
||||||
generic_string fileName = getDialogFileName(_dialog);
|
generic_string fileName = getDialogFileName(_dialog);
|
||||||
|
expandEnv(fileName);
|
||||||
bool nameChanged = transformPath(fileName);
|
bool nameChanged = transformPath(fileName);
|
||||||
// Update the controls.
|
// Update the controls.
|
||||||
if (not ::PathIsDirectory(fileName.c_str()))
|
if (!::PathIsDirectory(getAbsPath(fileName).c_str()))
|
||||||
{
|
{
|
||||||
// Name is a file path.
|
// Name is a file path.
|
||||||
// Add file extension if missing.
|
// Add file extension if missing.
|
||||||
@ -391,7 +456,9 @@ private:
|
|||||||
_staticThis->_monitorKeyboard = false;
|
_staticThis->_monitorKeyboard = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (_staticThis->_monitorKeyboard && !processingReturn)
|
// Avoid unnecessary processing by polling keyboard only on some messages.
|
||||||
|
bool checkMsg = msg > WM_USER;
|
||||||
|
if (_staticThis->_monitorKeyboard && !processingReturn && checkMsg)
|
||||||
{
|
{
|
||||||
SHORT state = GetAsyncKeyState(VK_RETURN);
|
SHORT state = GetAsyncKeyState(VK_RETURN);
|
||||||
if (state & 0x8000)
|
if (state & 0x8000)
|
||||||
@ -575,19 +642,6 @@ public:
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
static generic_string getFilename(IShellItem* psi)
|
|
||||||
{
|
|
||||||
generic_string result;
|
|
||||||
PWSTR pszFilePath = NULL;
|
|
||||||
HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
|
||||||
if (SUCCEEDED(hr) && pszFilePath)
|
|
||||||
{
|
|
||||||
result = pszFilePath;
|
|
||||||
CoTaskMemFree(pszFilePath);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool hasReadonlyAttr(IShellItem* psi)
|
static bool hasReadonlyAttr(IShellItem* psi)
|
||||||
{
|
{
|
||||||
SFGAOF attrs = 0;
|
SFGAOF attrs = 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user