Windows: Support virtual folders as initial directory for file dialog.
Use the scheme "clsid" to be able to pass them as "clsid:<GUID>"
(with '{', '}' stripped).
Task-number: QTBUG-33962
Change-Id: Ib045fe81518bca6e91588007ce8a245a48479b1f
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com>
Reviewed-by: Patrick Spendrin <patrick.spendrin@kdab.com>
Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
Reviewed-by: Björn Breitmeyer <bjoern.breitmeyer@kdab.com>
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
parent
696a18b063
commit
4347e81161
|
|
@ -225,8 +225,10 @@ bool QWindowsUser32DLL::initTouch()
|
|||
|
||||
QWindowsShell32DLL::QWindowsShell32DLL()
|
||||
: sHCreateItemFromParsingName(0)
|
||||
, sHGetKnownFolderIDList(0)
|
||||
, sHGetStockIconInfo(0)
|
||||
, sHGetImageList(0)
|
||||
, sHCreateItemFromIDList(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -234,8 +236,10 @@ void QWindowsShell32DLL::init()
|
|||
{
|
||||
QSystemLibrary library(QStringLiteral("shell32"));
|
||||
sHCreateItemFromParsingName = (SHCreateItemFromParsingName)(library.resolve("SHCreateItemFromParsingName"));
|
||||
sHGetKnownFolderIDList = (SHGetKnownFolderIDList)(library.resolve("SHGetKnownFolderIDList"));
|
||||
sHGetStockIconInfo = (SHGetStockIconInfo)library.resolve("SHGetStockIconInfo");
|
||||
sHGetImageList = (SHGetImageList)library.resolve("SHGetImageList");
|
||||
sHCreateItemFromIDList = (SHCreateItemFromIDList)library.resolve("SHCreateItemFromIDList");
|
||||
}
|
||||
|
||||
QWindowsShcoreDLL::QWindowsShcoreDLL()
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@
|
|||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
struct IBindCtx;
|
||||
struct _SHSTOCKICONINFO;
|
||||
|
||||
|
|
@ -120,12 +124,16 @@ struct QWindowsShell32DLL
|
|||
inline void init();
|
||||
|
||||
typedef HRESULT (WINAPI *SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **);
|
||||
typedef HRESULT (WINAPI *SHGetKnownFolderIDList)(const GUID &, DWORD, HANDLE, PIDLIST_ABSOLUTE *);
|
||||
typedef HRESULT (WINAPI *SHGetStockIconInfo)(int , int , _SHSTOCKICONINFO *);
|
||||
typedef HRESULT (WINAPI *SHGetImageList)(int, REFIID , void **);
|
||||
typedef HRESULT (WINAPI *SHCreateItemFromIDList)(PCIDLIST_ABSOLUTE, REFIID, void **);
|
||||
|
||||
SHCreateItemFromParsingName sHCreateItemFromParsingName;
|
||||
SHGetKnownFolderIDList sHGetKnownFolderIDList;
|
||||
SHGetStockIconInfo sHGetStockIconInfo;
|
||||
SHGetImageList sHGetImageList;
|
||||
SHCreateItemFromIDList sHCreateItemFromIDList;
|
||||
};
|
||||
|
||||
// Shell scaling library (Windows 8.1 onwards)
|
||||
|
|
|
|||
|
|
@ -58,16 +58,14 @@
|
|||
#include <QtCore/QExplicitlySharedDataPointer>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QMutexLocker>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QtCore/QRegularExpression>
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "qtwindows_additional.h"
|
||||
|
||||
#define STRICT_TYPED_ITEMIDS
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
// #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<QUrl> libraryItemFolders(IShellItem *item);
|
||||
static QString libraryItemDefaultSaveFolder(IShellItem *item);
|
||||
static int itemPaths(IShellItemArray *items, QList<QUrl> *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<const wchar_t *>(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<const wchar_t *>(native.utf16()),
|
||||
NULL, IID_IShellItem,
|
||||
reinterpret_cast<void **>(&result));
|
||||
if (SUCCEEDED(hr))
|
||||
return result;
|
||||
QWindowsContext::shell32dll.sHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
|
||||
NULL, IID_IShellItem,
|
||||
reinterpret_cast<void **>(&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:<GUID>" (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<void **>(&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
|
||||
|
|
|
|||
|
|
@ -976,6 +976,16 @@ QDir QFileDialog::directory() const
|
|||
|
||||
\note The non-native QFileDialog supports only local files.
|
||||
|
||||
\note On Windows, it is possible to pass URLs representing
|
||||
one of the \e {virtual folders}, such as "Computer" or "Network".
|
||||
This is done by passing a QUrl using the scheme \c clsid followed
|
||||
by the CLSID value with the curly braces removed. For example the URL
|
||||
\c clsid:374DE290-123F-4565-9164-39C4925E467B denotes the download
|
||||
location. For a complete list of possible values, see the MSDN documentation on
|
||||
\l{https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx}{KNOWNFOLDERID}.
|
||||
This feature was added in Qt 5.5.
|
||||
|
||||
\sa QUuid
|
||||
\since 5.2
|
||||
*/
|
||||
void QFileDialog::setDirectoryUrl(const QUrl &directory)
|
||||
|
|
|
|||
Loading…
Reference in New Issue