From cdba0ba961fbac9dede03f44bc9185e5a9caf75d Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 16 May 2022 01:30:04 +0100 Subject: [PATCH] Add IFileDialog-based wxFileDialog implementation If possible, i.e. if none of the features not supported by IFileDialog are needed, prefer to use IFileDialog for wxFileDialog rather than the old common dialog functions. There are no real differences in appearance because the old functions actually already forward to the new IFileDialog-based implementation internally anyhow, if possible, but this provides us with more flexibility and some things that were ignored by the common dialog functions now work, such as setting the initial dialog directory. --- include/wx/msw/filedlg.h | 4 ++ src/msw/filedlg.cpp | 142 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/include/wx/msw/filedlg.h b/include/wx/msw/filedlg.h index 398641025d..51ac463231 100644 --- a/include/wx/msw/filedlg.h +++ b/include/wx/msw/filedlg.h @@ -59,6 +59,10 @@ private: // functions. int ShowCommFileDialog(WXHWND owner); + // And another one using IFileDialog. + int ShowIFileDialog(WXHWND owner); + + wxArrayString m_fileNames; // remember if our SetPosition() or Centre() (which requires special diff --git a/src/msw/filedlg.cpp b/src/msw/filedlg.cpp index 60636c83c3..55fd4d2074 100644 --- a/src/msw/filedlg.cpp +++ b/src/msw/filedlg.cpp @@ -45,7 +45,9 @@ #include "wx/scopeguard.h" #include "wx/tokenzr.h" #include "wx/modalhook.h" + #include "wx/msw/private/dpiaware.h" +#include "wx/msw/private/filedialog.h" // ---------------------------------------------------------------------------- // constants @@ -405,6 +407,21 @@ int wxFileDialog::ShowModal() wxWindowDisabler disableOthers(this, parent); + /* + We need to use the old style dialog in order to use a hook function + which allows us to position it or use custom controls in it but, if + possible, we prefer to use the new style one instead. + */ +#if wxUSE_IFILEOPENDIALOG + if ( !m_bMovedWindow && !HasExtraControlCreator() ) + { + const int rc = ShowIFileDialog(hWndParent); + if ( rc != wxID_NONE ) + return rc; + //else: Failed to use IFileDialog, fall back to the traditional one. + } +#endif // wxUSE_IFILEOPENDIALOG + return ShowCommFileDialog(hWndParent); } @@ -699,4 +716,129 @@ int wxFileDialog::ShowCommFileDialog(WXHWND hWndParent) } +#if wxUSE_IFILEOPENDIALOG + +int wxFileDialog::ShowIFileDialog(WXHWND hWndParent) +{ + // Create the dialog. + wxMSWImpl::wxIFileDialog + fileDialog(HasFlag(wxFD_SAVE) ? CLSID_FileSaveDialog + : CLSID_FileOpenDialog); + + if ( !fileDialog.IsOk() ) + return wxID_NONE; + + // Configure the dialog before showing it. + fileDialog.SetTitle(m_message); + + HRESULT hr; + + wxArrayString wildDescriptions, wildFilters; + const UINT nWildcards = wxParseCommonDialogsFilter(m_wildCard, + wildDescriptions, + wildFilters); + if ( nWildcards ) + { + wxVector filterSpecs(nWildcards); + for ( UINT n = 0; n < nWildcards; ++n ) + { + filterSpecs[n].pszName = wildDescriptions[n].wc_str(); + filterSpecs[n].pszSpec = wildFilters[n].wc_str(); + } + + hr = fileDialog->SetFileTypes(nWildcards, &filterSpecs[0]); + if ( FAILED(hr) ) + wxLogApiError(wxS("IFileDialog::SetFileTypes"), hr); + + hr = fileDialog->SetFileTypeIndex(m_filterIndex + 1); + if ( FAILED(hr) ) + wxLogApiError(wxS("IFileDialog::SetFileTypeIndex"), hr); + } + + if ( !m_dir.empty() ) + { + fileDialog.SetInitialPath(m_dir); + } + + if ( !m_fileName.empty() ) + { + hr = fileDialog->SetFileName(m_fileName.wc_str()); + if ( FAILED(hr) ) + wxLogApiError(wxS("IFileDialog::SetDefaultExtension"), hr); + } + + + // We never set the following flags currently: + // + // - FOS_STRICTFILETYPES + // - FOS_NOVALIDATE + // - FOS_CREATEPROMPT + // - FOS_SHAREAWARE + // - FOS_NOREADONLYRETURN + // - FOS_NOTESTFILECREATE + // - FOS_OKBUTTONNEEDSINTERACTION + // - FOS_DONTADDTORECENT + // - FOS_DEFAULTNOMINIMODE + // - FOS_FORCEPREVIEWPANEON + // + // We might want to add wxFD_XXX equivalents for some of them in the future. + int options = 0; + if ( HasFlag(wxFD_OVERWRITE_PROMPT) ) + options |= FOS_OVERWRITEPROMPT; + if ( !HasFlag(wxFD_CHANGE_DIR) ) + options |= FOS_NOCHANGEDIR; + if ( HasFlag(wxFD_FILE_MUST_EXIST) ) + options |= FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST; + if ( HasFlag(wxFD_MULTIPLE) ) + options |= FOS_ALLOWMULTISELECT; + if ( HasFlag(wxFD_SHOW_HIDDEN) ) + options |= FOS_FORCESHOWHIDDEN; + if ( HasFlag(wxFD_NO_FOLLOW) ) + options |= FOS_NODEREFERENCELINKS; + + // Finally do show the dialog. + const int rc = fileDialog.Show(hWndParent, options, &m_fileNames, &m_path); + if ( rc == wxID_OK ) + { + UINT nFilterIndex; + hr = fileDialog->GetFileTypeIndex(&nFilterIndex); + if ( SUCCEEDED(hr) ) + { + // As with the common dialog, the index is 1-based here. + m_filterIndex = nFilterIndex - 1; + } + else + { + wxLogApiError(wxS("IFileDialog::GetFileTypeIndex"), hr); + + m_filterIndex = 0; + } + + if ( HasFlag(wxFD_MULTIPLE) ) + { + // This shouldn't the case, but check to be absolutely sure. + if ( !m_fileNames.empty() ) + m_dir = wxFileName(m_fileNames[0]).GetPath(); + } + else // Single selected file is in m_path. + { + // Append the extension if necessary. + m_path = AppendExtension(m_path, wildFilters[m_filterIndex]); + + const wxFileName fn(m_path); + m_dir = fn.GetPath(); + m_fileName = fn.GetFullName(); + + // For compatibility, our GetFilenames() must also return the same + // file, so put it into the array too. + m_fileNames.Clear(); + m_fileNames.Add(m_fileName); + } + } + + return rc; +} + +#endif // wxUSE_IFILEOPENDIALOG + #endif // wxUSE_FILEDLG