Fix append extension feature not working in save dialog
In some cases, checkbox was found instead of OK button and window procedure was overriden for it. Now, the OK button is identified using style and label checks. Also, in some cases IFileDialog::GetFileTypeIndex() returns the old value. To fix that, always remember the file type selected. Fix #9939, close #9945
This commit is contained in:
parent
f9d6fb9e31
commit
0e1a4663d9
|
@ -22,7 +22,6 @@
|
||||||
#endif
|
#endif
|
||||||
#include <comdef.h> // _com_error
|
#include <comdef.h> // _com_error
|
||||||
#include <comip.h> // _com_ptr_t
|
#include <comip.h> // _com_ptr_t
|
||||||
|
|
||||||
#include "CustomFileDialog.h"
|
#include "CustomFileDialog.h"
|
||||||
#include "Parameters.h"
|
#include "Parameters.h"
|
||||||
|
|
||||||
|
@ -51,6 +50,7 @@ namespace // anonymous
|
||||||
|
|
||||||
static const int IDC_FILE_CUSTOM_CHECKBOX = 4;
|
static const int IDC_FILE_CUSTOM_CHECKBOX = 4;
|
||||||
static const int IDC_FILE_TYPE_CHECKBOX = IDC_FILE_CUSTOM_CHECKBOX + 1;
|
static const int IDC_FILE_TYPE_CHECKBOX = IDC_FILE_CUSTOM_CHECKBOX + 1;
|
||||||
|
static const TCHAR TEMP_OK_BUTTON_LABEL[] = _T("Save");
|
||||||
|
|
||||||
// Returns a first extension from the extension specification string.
|
// Returns a first extension from the extension specification string.
|
||||||
// Multiple extensions are separated with ';'.
|
// Multiple extensions are separated with ';'.
|
||||||
|
@ -284,6 +284,10 @@ public:
|
||||||
{
|
{
|
||||||
if (dialogIndex == 0)
|
if (dialogIndex == 0)
|
||||||
return false;
|
return false;
|
||||||
|
// Remember the current file type index.
|
||||||
|
// Since GetFileTypeIndex() might return the old value in some cases.
|
||||||
|
// Specifically, when called after SetFileTypeIndex().
|
||||||
|
_currentType = dialogIndex;
|
||||||
generic_string name = getDialogFileName(_dialog);
|
generic_string name = getDialogFileName(_dialog);
|
||||||
if (changeExt(name, dialogIndex - 1))
|
if (changeExt(name, dialogIndex - 1))
|
||||||
return SUCCEEDED(_dialog->SetFileName(name.c_str()));
|
return SUCCEEDED(_dialog->SetFileName(name.c_str()));
|
||||||
|
@ -311,20 +315,19 @@ public:
|
||||||
{
|
{
|
||||||
if (id == IDC_FILE_TYPE_CHECKBOX)
|
if (id == IDC_FILE_TYPE_CHECKBOX)
|
||||||
{
|
{
|
||||||
bool ok = false;
|
UINT newFileType = 0;
|
||||||
if (bChecked)
|
if (bChecked)
|
||||||
{
|
{
|
||||||
ok = SUCCEEDED(_dialog->SetFileTypeIndex(_lastSelectedType));
|
newFileType = _lastSelectedType;
|
||||||
ok &= OnTypeChange(_lastSelectedType);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UINT currentIndex = 0;
|
if (_currentType != 0 && _currentType != _wildcardType)
|
||||||
ok = SUCCEEDED(_dialog->GetFileTypeIndex(¤tIndex));
|
_lastSelectedType = _currentType;
|
||||||
if (ok && currentIndex != 0 && currentIndex != _wildcardType)
|
newFileType = _wildcardType;
|
||||||
_lastSelectedType = currentIndex;
|
|
||||||
ok &= SUCCEEDED(_dialog->SetFileTypeIndex(_wildcardType));
|
|
||||||
}
|
}
|
||||||
|
_dialog->SetFileTypeIndex(newFileType);
|
||||||
|
OnTypeChange(newFileType);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
return E_NOTIMPL;
|
return E_NOTIMPL;
|
||||||
|
@ -337,8 +340,8 @@ public:
|
||||||
|
|
||||||
|
|
||||||
FileDialogEventHandler(IFileDialog* dlg, const std::vector<Filter>& filterSpec, int fileIndex, int wildcardIndex)
|
FileDialogEventHandler(IFileDialog* dlg, const std::vector<Filter>& filterSpec, int fileIndex, int wildcardIndex)
|
||||||
: _cRef(1), _dialog(dlg), _customize(dlg), _filterSpec(filterSpec), _lastSelectedType(fileIndex + 1),
|
: _cRef(1), _dialog(dlg), _customize(dlg), _filterSpec(filterSpec), _currentType(fileIndex + 1),
|
||||||
_wildcardType(wildcardIndex >= 0 ? wildcardIndex + 1 : 0)
|
_lastSelectedType(fileIndex + 1), _wildcardType(wildcardIndex >= 0 ? wildcardIndex + 1 : 0)
|
||||||
{
|
{
|
||||||
_staticThis = this;
|
_staticThis = this;
|
||||||
}
|
}
|
||||||
|
@ -360,8 +363,6 @@ private:
|
||||||
// Call this as late as possible to ensure all the controls of the dialog are created.
|
// Call this as late as possible to ensure all the controls of the dialog are created.
|
||||||
void initControls()
|
void initControls()
|
||||||
{
|
{
|
||||||
_okButtonProc = nullptr;
|
|
||||||
_fileNameProc = nullptr;
|
|
||||||
assert(_dialog);
|
assert(_dialog);
|
||||||
com_ptr<IOleWindow> pOleWnd = _dialog;
|
com_ptr<IOleWindow> pOleWnd = _dialog;
|
||||||
if (pOleWnd)
|
if (pOleWnd)
|
||||||
|
@ -373,6 +374,7 @@ private:
|
||||||
EnumChildWindows(hwndDlg, &EnumChildProc, 0);
|
EnumChildWindows(hwndDlg, &EnumChildProc, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_dialog->SetOkButtonLabel(nullptr); // Reset label to default.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldInitControls() const
|
bool shouldInitControls() const
|
||||||
|
@ -380,20 +382,9 @@ private:
|
||||||
return !_okButtonProc && !_fileNameProc;
|
return !_okButtonProc && !_fileNameProc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes the name extension according to currently selected file type index.
|
bool changeExt(generic_string& name, int extIndex)
|
||||||
bool changeExt(generic_string& name)
|
|
||||||
{
|
{
|
||||||
if (!_dialog)
|
if (extIndex >= 0 && extIndex < static_cast<int>(_filterSpec.size()))
|
||||||
return false;
|
|
||||||
UINT dialogIndex = 0;
|
|
||||||
if (FAILED(_dialog->GetFileTypeIndex(&dialogIndex)) || dialogIndex == 0)
|
|
||||||
return false;
|
|
||||||
return changeExt(name, dialogIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool changeExt(generic_string& name, size_t extIndex)
|
|
||||||
{
|
|
||||||
if (extIndex >= 0 && extIndex < _filterSpec.size())
|
|
||||||
{
|
{
|
||||||
const generic_string ext = get1stExt(_filterSpec[extIndex].ext);
|
const generic_string ext = get1stExt(_filterSpec[extIndex].ext);
|
||||||
if (!endsWith(ext, _T(".*")))
|
if (!endsWith(ext, _T(".*")))
|
||||||
|
@ -431,7 +422,7 @@ private:
|
||||||
// Name is a file path.
|
// Name is a file path.
|
||||||
// Add file extension if missing.
|
// Add file extension if missing.
|
||||||
if (!hasExt(fileName))
|
if (!hasExt(fileName))
|
||||||
nameChanged |= changeExt(fileName);
|
nameChanged |= changeExt(fileName, _currentType - 1);
|
||||||
}
|
}
|
||||||
// Update the edit box text.
|
// Update the edit box text.
|
||||||
// It will update the address if the path is a directory.
|
// It will update the address if the path is a directory.
|
||||||
|
@ -476,19 +467,29 @@ private:
|
||||||
HWND hwndChild = FindWindowEx(hwnd, nullptr, _T("Edit"), _T(""));
|
HWND hwndChild = FindWindowEx(hwnd, nullptr, _T("Edit"), _T(""));
|
||||||
if (hwndChild)
|
if (hwndChild)
|
||||||
{
|
{
|
||||||
_fileNameProc = (WNDPROC)SetWindowLongPtr(hwndChild, GWLP_WNDPROC, (LPARAM)&FileNameWndProc);
|
_staticThis->_fileNameProc = (WNDPROC)SetWindowLongPtr(hwndChild, GWLP_WNDPROC, (LPARAM)&FileNameWndProc);
|
||||||
_staticThis->_hwndNameEdit = hwndChild;
|
_staticThis->_hwndNameEdit = hwndChild;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lstrcmpi(buffer, _T("Button")) == 0)
|
else if (lstrcmpi(buffer, _T("Button")) == 0)
|
||||||
{
|
{
|
||||||
// The button of interest has a focus by default.
|
|
||||||
LONG style = GetWindowLong(hwnd, GWL_STYLE);
|
LONG style = GetWindowLong(hwnd, GWL_STYLE);
|
||||||
if (style & BS_DEFPUSHBUTTON)
|
if (style & (WS_CHILDWINDOW | WS_VISIBLE | WS_GROUP))
|
||||||
_okButtonProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LPARAM)&OkButtonWndProc);
|
{
|
||||||
|
DWORD type = style & 0xF;
|
||||||
|
DWORD appearance = style & 0xF0;
|
||||||
|
if ((type == BS_PUSHBUTTON || type == BS_DEFPUSHBUTTON) && (appearance == BS_TEXT))
|
||||||
|
{
|
||||||
|
if (GetWindowText(hwnd, buffer, bufferLen) > 0 &&
|
||||||
|
wcsstr(buffer, TEMP_OK_BUTTON_LABEL) != nullptr)
|
||||||
|
{
|
||||||
|
_staticThis->_okButtonProc = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LPARAM)&OkButtonWndProc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_okButtonProc && _fileNameProc)
|
if (_staticThis->_okButtonProc && _staticThis->_fileNameProc)
|
||||||
return FALSE; // Found all children, stop enumeration.
|
return FALSE; // Found all children, stop enumeration.
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -513,7 +514,7 @@ private:
|
||||||
}
|
}
|
||||||
if (pressed)
|
if (pressed)
|
||||||
_staticThis->onPreFileOk();
|
_staticThis->onPreFileOk();
|
||||||
return CallWindowProc(_okButtonProc, hwnd, msg, wparam, lparam);
|
return CallWindowProc(_staticThis->_okButtonProc, hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LRESULT CALLBACK FileNameWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
static LRESULT CALLBACK FileNameWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
|
||||||
|
@ -545,11 +546,9 @@ private:
|
||||||
processingReturn = false;
|
processingReturn = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CallWindowProc(_fileNameProc, hwnd, msg, wparam, lparam);
|
return CallWindowProc(_staticThis->_fileNameProc, hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
static WNDPROC _okButtonProc;
|
|
||||||
static WNDPROC _fileNameProc;
|
|
||||||
static FileDialogEventHandler* _staticThis;
|
static FileDialogEventHandler* _staticThis;
|
||||||
|
|
||||||
long _cRef;
|
long _cRef;
|
||||||
|
@ -558,13 +557,14 @@ private:
|
||||||
const std::vector<Filter> _filterSpec;
|
const std::vector<Filter> _filterSpec;
|
||||||
generic_string _lastUsedFolder;
|
generic_string _lastUsedFolder;
|
||||||
HWND _hwndNameEdit = nullptr;
|
HWND _hwndNameEdit = nullptr;
|
||||||
|
WNDPROC _okButtonProc = nullptr;
|
||||||
|
WNDPROC _fileNameProc = nullptr;
|
||||||
|
UINT _currentType = 0; // File type currenly selected in dialog.
|
||||||
|
UINT _lastSelectedType = 0; // Last selected non-wildcard file type.
|
||||||
|
UINT _wildcardType = 0; // Wildcard *.* file type index (usually 1).
|
||||||
bool _monitorKeyboard = true;
|
bool _monitorKeyboard = true;
|
||||||
UINT _lastSelectedType = 0;
|
|
||||||
UINT _wildcardType = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WNDPROC FileDialogEventHandler::_okButtonProc;
|
|
||||||
WNDPROC FileDialogEventHandler::_fileNameProc;
|
|
||||||
FileDialogEventHandler* FileDialogEventHandler::_staticThis;
|
FileDialogEventHandler* FileDialogEventHandler::_staticThis;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -592,6 +592,10 @@ public:
|
||||||
IID_PPV_ARGS(&_dialog));
|
IID_PPV_ARGS(&_dialog));
|
||||||
_customize = _dialog;
|
_customize = _dialog;
|
||||||
|
|
||||||
|
// Set label so that OK button can be differentiated during initialization.
|
||||||
|
// It will be reset back to default later on.
|
||||||
|
_dialog->SetOkButtonLabel(TEMP_OK_BUTTON_LABEL);
|
||||||
|
|
||||||
// Init the event handler.
|
// Init the event handler.
|
||||||
// Pass the initially selected file type.
|
// Pass the initially selected file type.
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
|
@ -826,7 +830,7 @@ private:
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
CustomFileDialog::CustomFileDialog(HWND hwnd) : _impl{std::make_unique<Impl>()}
|
CustomFileDialog::CustomFileDialog(HWND hwnd) : _impl{ std::make_unique<Impl>() }
|
||||||
{
|
{
|
||||||
_impl->_hwndOwner = hwnd;
|
_impl->_hwndOwner = hwnd;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue