From 4347e811611ab15e544765a3a34761d2f5c68cb3 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 25 Mar 2015 10:45:58 +0100 Subject: Windows: Support virtual folders as initial directory for file dialog. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the scheme "clsid" to be able to pass them as "clsid:" (with '{', '}' stripped). Task-number: QTBUG-33962 Change-Id: Ib045fe81518bca6e91588007ce8a245a48479b1f Reviewed-by: Leena Miettinen Reviewed-by: Patrick Spendrin Reviewed-by: Oliver Wolff Reviewed-by: Björn Breitmeyer Reviewed-by: Joerg Bornemann --- .../platforms/windows/qwindowsdialoghelpers.cpp | 80 +++++++++++++++++----- 1 file changed, 61 insertions(+), 19 deletions(-) (limited to 'src/plugins/platforms/windows/qwindowsdialoghelpers.cpp') diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 3b47829402..3284795fc1 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -58,16 +58,14 @@ #include #include #include +#include +#include #include #include #include "qtwindows_additional.h" -#define STRICT_TYPED_ITEMIDS -#include -#include - // #define USE_NATIVE_COLOR_DIALOG /* Testing purposes only */ #ifdef Q_CC_MINGW /* Add missing declarations for MinGW */ @@ -873,8 +871,8 @@ public: virtual void setWindowTitle(const QString &title); inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::AcceptMode acceptMode, QFileDialogOptions::FileDialogOptions options); - inline void setDirectory(const QString &directory); - inline void updateDirectory() { setDirectory(m_data.directory().toLocalFile()); } + inline void setDirectory(const QUrl &directory); + inline void updateDirectory() { setDirectory(m_data.directory()); } inline QString directory() const; virtual void doExec(HWND owner = 0); virtual void setNameFilters(const QStringList &f); @@ -916,7 +914,7 @@ protected: static QList libraryItemFolders(IShellItem *item); static QString libraryItemDefaultSaveFolder(IShellItem *item); static int itemPaths(IShellItemArray *items, QList *fileResult = 0); - static IShellItem *shellItem(const QString &path); + static IShellItem *shellItem(const QUrl &url); const QWindowsFileDialogSharedData &data() const { return m_data; } QWindowsFileDialogSharedData &data() { return m_data; } @@ -976,25 +974,58 @@ void QWindowsNativeFileDialogBase::setWindowTitle(const QString &title) m_fileDialog->SetTitle(reinterpret_cast(title.utf16())); } -IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path) +IShellItem *QWindowsNativeFileDialogBase::shellItem(const QUrl &url) { #ifndef Q_OS_WINCE - if (QWindowsContext::shell32dll.sHCreateItemFromParsingName) { - IShellItem *result = 0; - const QString native = QDir::toNativeSeparators(path); + if (url.isLocalFile()) { + if (!QWindowsContext::shell32dll.sHCreateItemFromParsingName) + return Q_NULLPTR; + IShellItem *result = Q_NULLPTR; + const QString native = QDir::toNativeSeparators(url.toLocalFile()); const HRESULT hr = - QWindowsContext::shell32dll.sHCreateItemFromParsingName(reinterpret_cast(native.utf16()), - NULL, IID_IShellItem, - reinterpret_cast(&result)); - if (SUCCEEDED(hr)) - return result; + QWindowsContext::shell32dll.sHCreateItemFromParsingName(reinterpret_cast(native.utf16()), + NULL, IID_IShellItem, + reinterpret_cast(&result)); + if (FAILED(hr)) { + qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(url.toString())); + return Q_NULLPTR; + } + return result; + } else if (url.scheme() == QLatin1String("clsid")) { + if (!QWindowsContext::shell32dll.sHGetKnownFolderIDList || !QWindowsContext::shell32dll.sHCreateItemFromIDList) + return Q_NULLPTR; + // Support for virtual folders via GUID + // (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx) + // specified as "clsid:" (without '{', '}'). + IShellItem *result = Q_NULLPTR; + const QUuid uuid(url.path()); + if (uuid.isNull()) { + qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); + return Q_NULLPTR; + } + PIDLIST_ABSOLUTE idList; + HRESULT hr = QWindowsContext::shell32dll.sHGetKnownFolderIDList(uuid, 0, 0, &idList); + if (FAILED(hr)) { + qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); + return Q_NULLPTR; + } + hr = QWindowsContext::shell32dll.sHCreateItemFromIDList(idList, IID_IShellItem, reinterpret_cast(&result)); + CoTaskMemFree(idList); + if (FAILED(hr)) { + qErrnoWarning("%s: SHCreateItemFromIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString())); + return Q_NULLPTR; + } + return result; + } else { + qWarning() << __FUNCTION__ << ": Unhandled scheme: " << url.scheme(); } +#else // !Q_OS_WINCE + Q_UNUSED(url) #endif - qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(path)); return 0; } -void QWindowsNativeFileDialogBase::setDirectory(const QString &directory) +void QWindowsNativeFileDialogBase::setDirectory(const QUrl &directory) { if (!directory.isEmpty()) { if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { @@ -1284,9 +1315,20 @@ void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel } } +static inline bool isClsid(const QString &s) +{ + // detect "374DE290-123F-4565-9164-39C4925E467B". + static const QRegularExpression pattern(QLatin1String("[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{8}")); + Q_ASSERT(pattern.isValid()); + return pattern.match(s).hasMatch(); +} + void QWindowsNativeFileDialogBase::selectFile(const QString &fileName) const { - m_fileDialog->SetFileName((wchar_t*)fileName.utf16()); + // Hack to prevent CLSIDs from being set as file name due to + // QFileDialogPrivate::initialSelection() being QString-based. + if (!isClsid(fileName)) + m_fileDialog->SetFileName((wchar_t*)fileName.utf16()); } // Return the index of the selected filter, accounting for QFileDialog -- cgit v1.2.3