diff options
Diffstat (limited to 'src/plugins/platforms/windows')
-rw-r--r-- | src/plugins/platforms/windows/accessible/iaccessible2.cpp | 27 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdialoghelpers.cpp | 504 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdrag.cpp | 72 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdrag.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsintegration.cpp | 8 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsintegration.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowstheme.cpp | 2 | ||||
-rw-r--r-- | src/plugins/platforms/windows/windows.pro | 6 |
8 files changed, 553 insertions, 76 deletions
diff --git a/src/plugins/platforms/windows/accessible/iaccessible2.cpp b/src/plugins/platforms/windows/accessible/iaccessible2.cpp index 6acfd6e602..03bb94db8f 100644 --- a/src/plugins/platforms/windows/accessible/iaccessible2.cpp +++ b/src/plugins/platforms/windows/accessible/iaccessible2.cpp @@ -1493,13 +1493,26 @@ HRESULT STDMETHODCALLTYPE QWindowsIA2Accessible::QueryService(REFGUID guidServic *iface = 0; accessibleDebug("QWindowsIA2Accessible::QS(): %s", IIDToString(riid).constData()); - if (guidService == IID_IAccessible && riid == IID_IAccessible2) { - // The conditions for entering here should be ok (from _dicoveringInterfaces in IAccessible2.idl) - *iface = static_cast<IAccessible2*>(this); - } else if (guidService == IID_IAccessible && (riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch)) { - // The above conditions works with AccProbe and NVDA. - *iface = static_cast<IAccessible*>(this); - } else if (riid == IID_IAccessibleApplication) { + + if (guidService == IID_IAccessible) { + if (riid == IID_IServiceProvider) { + // do not end up calling QueryInterface for IID_IServiceProvider + *iface = 0; + } else if (riid == IID_IAccessible || riid == IID_IUnknown || riid == IID_IDispatch) { + // The above conditions works with AccProbe and NVDA. + *iface = static_cast<IAccessible*>(this); + } else { + // According to _dicoveringInterfaces Discovery of Interfaces, we should really only + // enter here if riid == IID_IAccessible2, but some screen readers does not like that, + // and other servers seems to have realized that. (Chrome and Mozilla for instance, + // calls QueryInterface more or less in the same way) + + // For instance, accProbe discovers IID_IAccessibleTable2 by a QueryService only. + return QueryInterface(riid, iface); + } + } + + if (riid == IID_IAccessibleApplication) { *iface = new AccessibleApplication; return S_OK; } diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 87cb224d49..9e5578d35d 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -43,12 +43,9 @@ #include "qwindowscontext.h" #include "qwindowswindow.h" +#include "qwindowsintegration.h" #include "qwindowstheme.h" // Color conversion helpers -#include <QtWidgets/QColorDialog> -#include <QtWidgets/QFontDialog> -#include <QtWidgets/QFileDialog> - #include <QtGui/QGuiApplication> #include <QtGui/QColor> @@ -60,6 +57,7 @@ #include <QtCore/QSharedPointer> #include <QtCore/QObject> #include <QtCore/QThread> +#include <QtCore/QSysInfo> #include <QtCore/private/qsystemlibrary_p.h> #include "qtwindows_additional.h" @@ -384,9 +382,10 @@ void eatMouseMove() \class QWindowsNativeDialogBase \brief Base class for Windows native dialogs. - Base clases for native dialogs that mimick the - behaviour of their QDialog counterparts as close as - possible. + Base classes for native dialogs (using the CLSID-based + dialog interfaces "IFileDialog", etc. available from Windows + Vista on) that mimick the behaviour of their QDialog + counterparts as close as possible. A major difference is that there is only an exec(), which is a modal, blocking call; there is no non-blocking show(). @@ -862,12 +861,45 @@ int QWindowsNativeFileDialogBase::itemPaths(IShellItemArray *items, return itemCount; } -// Copy a string to an Utf16 buffer. -static inline void toBuffer(const QString &what, WCHAR **ptr) +// Split a list of name filters into description and actual filters +struct FilterSpec +{ + QString description; + QString filter; +}; + +static QList<FilterSpec> filterSpecs(const QStringList &filters, + bool hideFilterDetails, + int *totalStringLength) { - const int length = 1 + what.size(); - memcpy(*ptr, what.utf16(), length * sizeof(WCHAR)); - *ptr += length; + QList<FilterSpec> result; + result.reserve(filters.size()); + *totalStringLength = 0; + + const QRegExp filterSeparatorRE(QStringLiteral("[;\\s]+")); + const QString separator = QStringLiteral(";"); + Q_ASSERT(filterSeparatorRE.isValid()); + // Split filter specification as 'Texts (*.txt[;] *.doc)' + // into description and filters specification as '*.txt;*.doc' + foreach (const QString &filterString, filters) { + const int openingParenPos = filterString.lastIndexOf(QLatin1Char('(')); + const int closingParenPos = openingParenPos != -1 ? + filterString.indexOf(QLatin1Char(')'), openingParenPos + 1) : -1; + FilterSpec filterSpec; + filterSpec.filter = closingParenPos == -1 ? + QString(QLatin1Char('*')) : + filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed(); + filterSpec.filter.replace(filterSeparatorRE, separator); + filterSpec.description = filterString; + if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description + filterSpec.description.truncate(openingParenPos); + while (filterSpec.description.endsWith(QLatin1Char(' '))) + filterSpec.description.truncate(filterSpec.description.size() - 1); + } + *totalStringLength += filterSpec.filter.size() + filterSpec.description.size(); + result.push_back(filterSpec); + } + return result; } void QWindowsNativeFileDialogBase::setNameFilters(const QStringList &filters) @@ -875,48 +907,30 @@ void QWindowsNativeFileDialogBase::setNameFilters(const QStringList &filters) /* Populates an array of COMDLG_FILTERSPEC from list of filters, * store the strings in a flat, contiguous buffer. */ m_nameFilters = filters; - const int size = filters.size(); int totalStringLength = 0; - for (int i = 0; i < size; ++i) - totalStringLength += filters.at(i).size(); + const QList<FilterSpec> specs = filterSpecs(filters, m_hideFiltersDetails, &totalStringLength); + const int size = specs.size(); - QScopedArrayPointer<WCHAR> buffer(new WCHAR[totalStringLength * 2 + 2 * size]); + QScopedArrayPointer<WCHAR> buffer(new WCHAR[totalStringLength + 2 * size]); QScopedArrayPointer<COMDLG_FILTERSPEC> comFilterSpec(new COMDLG_FILTERSPEC[size]); const QString matchesAll = QStringLiteral(" (*)"); - const QRegExp filterSeparatorRE(QStringLiteral("[;\\s]+")); - const QString separator = QStringLiteral(";"); - Q_ASSERT(filterSeparatorRE.isValid()); - WCHAR *ptr = buffer.data(); // Split filter specification as 'Texts (*.txt[;] *.doc)' // into description and filters specification as '*.txt;*.doc' + for (int i = 0; i < size; ++i) { - QString filterString = filters.at(i); - const int openingParenPos = filterString.lastIndexOf(QLatin1Char('(')); - const int closingParenPos = openingParenPos != -1 ? - filterString.indexOf(QLatin1Char(')'), openingParenPos + 1) : -1; - QString filterSpec = closingParenPos == -1 ? - QString(QLatin1Char('*')) : - filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed(); - filterSpec.replace(filterSeparatorRE, separator); - if (m_hideFiltersDetails) { - // Do not show pattern in description - if (openingParenPos != -1) { - filterString.truncate(openingParenPos); - while (filterString.endsWith(QLatin1Char(' '))) - filterString.truncate(filterString.size() - 1); - } - } else { - // Display glitch: 'All files (*)' shows up as 'All files (*) (*)' - if (filterString.endsWith(matchesAll)) - filterString.truncate(filterString.size() - matchesAll.size()); - } + // Display glitch (CLSID only): 'All files (*)' shows up as 'All files (*) (*)' + QString description = specs[i].description; + if (!m_hideFiltersDetails && description.endsWith(matchesAll)) + description.truncate(description.size() - matchesAll.size()); // Add to buffer. comFilterSpec[i].pszName = ptr; - toBuffer(filterString, &ptr); + ptr += description.toWCharArray(ptr); + *ptr++ = 0; comFilterSpec[i].pszSpec = ptr; - toBuffer(filterSpec, &ptr); + ptr += specs[i].filter.toWCharArray(ptr); + *ptr++ = 0; } m_fileDialog->SetFileTypes(size, comFilterSpec.data()); @@ -947,9 +961,23 @@ void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel } } +// Return the index of the selected filter, accounting for QFileDialog +// sometimes stripping the filter specification depending on the +// hideFilterDetails setting. +static int indexOfNameFilter(const QStringList &filters, const QString &needle) +{ + const int index = filters.indexOf(needle); + if (index >= 0) + return index; + for (int i = 0; i < filters.size(); ++i) + if (filters.at(i).startsWith(needle)) + return i; + return -1; +} + void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter) { - const int index = m_nameFilters.indexOf(filter); + const int index = indexOfNameFilter(m_nameFilters, filter); if (index >= 0) { m_fileDialog->SetFileTypeIndex(index + 1); // one-based. } else { @@ -1270,6 +1298,380 @@ QString QWindowsFileDialogHelper::selectedNameFilter() const return QString(); } +#ifndef Q_OS_WINCE + +/*! + \class QWindowsXpNativeFileDialog + \brief Native Windows directory dialog for Windows XP using SHlib-functions. + + Uses the synchronous GetOpenFileNameW(), GetSaveFileNameW() from ComDlg32 + or SHBrowseForFolder() for directories. + + \internal + \sa QWindowsXpFileDialogHelper + + \ingroup qt-lighthouse-win +*/ + +class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase +{ + Q_OBJECT +public: + typedef QSharedPointer<QFileDialogOptions> OptionsPtr; + + static QWindowsXpNativeFileDialog *create(const OptionsPtr &options); + + virtual void setWindowTitle(const QString &t) { m_title = t; } + virtual void exec(HWND owner = 0); + virtual QPlatformDialogHelper::DialogCode result() const { return m_result; } + + void setDirectory(const QString &d) { m_directory = d; } + QString directory() const { return m_directory; } + void selectFile(const QString &f) { m_initialFile = f; } + QStringList selectedFiles() const { return m_selectedFiles; } + void setNameFilters(const QStringList &n) { m_nameFilters = n; } + void selectNameFilter(const QString &f); + QString selectedNameFilter() const { return m_selectedNameFilter; } + + int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam); + +public slots: + virtual void close() {} + +private: + typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW); + typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW); + + explicit QWindowsXpNativeFileDialog(const OptionsPtr &options); + void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const; + QStringList execExistingDir(HWND owner); + QStringList execFileNames(HWND owner, int *selectedFilterIndex) const; + + const OptionsPtr m_options; + QString m_title; + QString m_directory; + QString m_initialFile; + QStringList m_selectedFiles; + QString m_selectedNameFilter; + QStringList m_nameFilters; + QPlatformDialogHelper::DialogCode m_result; + + static PtrGetOpenFileNameW m_getOpenFileNameW; + static PtrGetSaveFileNameW m_getSaveFileNameW; +}; + +QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0; +QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0; + +QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options) +{ + // GetOpenFileNameW() GetSaveFileName() are resolved + // dynamically as not to create a dependency on Comdlg32, which + // is used on XP only. + if (!m_getOpenFileNameW) { + QSystemLibrary library(QStringLiteral("Comdlg32")); + m_getOpenFileNameW = (PtrGetOpenFileNameW)(library.resolve("GetOpenFileNameW")); + m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW")); + } + if (m_getOpenFileNameW && m_getSaveFileNameW) + return new QWindowsXpNativeFileDialog(options); + return 0; +} + +QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) : + m_options(options), m_result(QPlatformDialogHelper::Rejected) +{ + const QStringList nameFilters = m_options->nameFilters(); + if (!nameFilters.isEmpty()) + setNameFilters(nameFilters); + const QString initialDirectory = m_options->initialDirectory(); + if (!initialDirectory.isEmpty()) + setDirectory(initialDirectory); + const QString initialNameFilter = m_options->initiallySelectedNameFilter(); + if (!initialNameFilter.isEmpty()) + selectNameFilter(initialNameFilter); + const QStringList selectedFiles = m_options->initiallySelectedFiles(); + if (!selectedFiles.isEmpty()) + selectFile(selectedFiles.front()); + setWindowTitle(m_options->windowTitle()); +} + +void QWindowsXpNativeFileDialog::selectNameFilter(const QString &f) +{ + const int index = indexOfNameFilter(m_nameFilters, f); + if (index >= 0) + m_selectedNameFilter = m_nameFilters.at(index); +} + +void QWindowsXpNativeFileDialog::exec(HWND owner) +{ + int selectedFilterIndex = -1; + m_selectedFiles = m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? + execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex); + QWindowsDialogs::eatMouseMove(); + if (m_selectedFiles.isEmpty()) { + m_result = QPlatformDialogHelper::Rejected; + emit rejected(); + } else { + if (selectedFilterIndex >= 0 && selectedFilterIndex < m_nameFilters.size()) { + m_selectedNameFilter = m_nameFilters.at(selectedFilterIndex); + } else { + m_selectedNameFilter.clear(); + } + m_directory = QFileInfo(m_selectedFiles.front()).absolutePath(); + m_result = QPlatformDialogHelper::Accepted; + emit accepted(); + } +} + +// Callback for QWindowsNativeXpFileDialog directory dialog. +// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers) + +static int CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + QWindowsXpNativeFileDialog *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData); + return dialog->existingDirCallback(hwnd, uMsg, lParam); +} + +#ifdef Q_CC_MINGW +typedef ITEMIDLIST *qt_LpItemIdList; +#else +typedef PIDLIST_ABSOLUTE qt_LpItemIdList; +#endif + +int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam) +{ + switch (uMsg) { + case BFFM_INITIALIZED: + if (!m_initialFile.isEmpty()) + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(m_initialFile.utf16())); + break; + case BFFM_SELCHANGED: { + wchar_t path[MAX_PATH]; + const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path) + && path[0]; + SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1); + } + break; + } + return 0; +} + +QStringList QWindowsXpNativeFileDialog::execExistingDir(HWND owner) +{ + BROWSEINFO bi; + wchar_t initPath[MAX_PATH]; + initPath[0] = 0; + bi.hwndOwner = owner; + bi.pidlRoot = NULL; + //### This does not seem to be respected? - the dialog always displays "Browse for folder" + bi.lpszTitle = (wchar_t*)m_title.utf16(); + bi.pszDisplayName = initPath; + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; + bi.lpfn = xpFileDialogGetExistingDirCallbackProc; + bi.lParam = LPARAM(this); + QStringList selectedFiles; + if (qt_LpItemIdList pItemIDList = SHBrowseForFolder(&bi)) { + wchar_t path[MAX_PATH]; + path[0] = 0; + if (SHGetPathFromIDList(pItemIDList, path) && path[0]) + selectedFiles.push_back(QDir::cleanPath(QString::fromWCharArray(path))); + IMalloc *pMalloc; + if (SHGetMalloc(&pMalloc) == NOERROR) { + pMalloc->Free(pItemIDList); + pMalloc->Release(); + } + } + return selectedFiles; +} + +// Return an allocated wchar_t array from a QString, reserve more memory if desired. +static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0) +{ + const size_t stringSize = s.size(); + wchar_t *result = new wchar_t[qMax(stringSize + 1, reserveSize)]; + s.toWCharArray(result); + result[stringSize] = 0; + return result; +} + +// Open/Save files +void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND owner) const +{ + ZeroMemory(ofn, sizeof(OPENFILENAME)); + ofn->lStructSize = sizeof(OPENFILENAME); + ofn->hwndOwner = owner; + + // Create a buffer with the filter strings. + int totalStringLength = 0; + QList<FilterSpec> specs = + filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength); + const int size = specs.size(); + wchar_t *ptr = new wchar_t[totalStringLength + 2 * size + 1]; + ofn->lpstrFilter = ptr; + foreach (const FilterSpec &spec, specs) { + ptr += spec.description.toWCharArray(ptr); + *ptr++ = 0; + ptr += spec.filter.toWCharArray(ptr); + *ptr++ = 0; + } + *ptr = 0; + const int nameFilterIndex = indexOfNameFilter(m_nameFilters, m_selectedNameFilter); + if (nameFilterIndex >= 0) + ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based. + // lpstrFile receives the initial selection and is the buffer + // for the target. If it contains any invalid character, the dialog + // will not show. + ofn->nMaxFile = 65535; + const QString initiallySelectedFile = + QDir::toNativeSeparators(m_initialFile).remove(QLatin1Char('<')). + remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|')); + ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile); + ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_directory)); + ofn->lpstrTitle = (wchar_t*)m_title.utf16(); + // Determine lpstrDefExt. Note that the current MSDN docs document this + // member wrong. It should rather be documented as "the default extension + // if no extension was given and if the current filter does not have an + // extension (e.g (*)). If the current filter has an extension, use + // the extension of the current filter". + if (m_options->acceptMode() == QFileDialogOptions::AcceptSave) { + QString defaultSuffix = m_options->defaultSuffix(); + if (defaultSuffix.startsWith(QLatin1Char('.'))) + defaultSuffix.remove(0, 1); + if (!defaultSuffix.isEmpty()) + ofn->lpstrDefExt = qStringToWCharArray(defaultSuffix); + } + // Flags. + ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST); + if (m_options->fileMode() == QFileDialogOptions::ExistingFile + || m_options->fileMode() == QFileDialogOptions::ExistingFiles) + ofn->Flags |= (OFN_FILEMUSTEXIST); + if (m_options->fileMode() == QFileDialogOptions::ExistingFiles) + ofn->Flags |= (OFN_ALLOWMULTISELECT); + if (!(m_options->options() & QFileDialogOptions::DontConfirmOverwrite)) + ofn->Flags |= OFN_OVERWRITEPROMPT; +} + +QStringList QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedFilterIndex) const +{ + *selectedFilterIndex = -1; + OPENFILENAME ofn; + populateOpenFileName(&ofn, owner); + QStringList result; + const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave; + if (isSave ? m_getSaveFileNameW(&ofn) : m_getOpenFileNameW(&ofn)) { + *selectedFilterIndex = ofn.nFilterIndex - 1; + result.push_back(QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile))); + // For multiselection, the first item is the path followed + // by "\0<file1>\0<file2>\0\0". + if (ofn.Flags & (OFN_ALLOWMULTISELECT)) { + wchar_t *ptr = ofn.lpstrFile + result.front().size() + 1; + if (*ptr) { + const QString path = result.takeAt(0) + QLatin1Char('/'); + while (*ptr) { + const QString fileName = QString::fromWCharArray(ptr); + result.push_back(path + fileName); + ptr += fileName.size() + 1; + } // extract multiple files + } // has multiple files + } // multiple flag set + } + delete [] ofn.lpstrFile; + delete [] ofn.lpstrInitialDir; + delete [] ofn.lpstrFilter; + delete [] ofn.lpstrDefExt; + return result; +} + +/*! + \class QWindowsXpFileDialogHelper + \brief Dialog helper using QWindowsXpNativeFileDialog + + \sa QWindowsXpNativeFileDialog + \internal + \ingroup qt-lighthouse-win +*/ + +class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper> +{ +public: + QWindowsXpFileDialogHelper() {} + virtual bool supportsNonModalDialog() const { return false; } + + virtual bool defaultNameFilterDisables() const + { return true; } + virtual void setDirectory(const QString &directory); + virtual QString directory() const; + virtual void selectFile(const QString &filename); + virtual QStringList selectedFiles() const; + virtual void setFilter() {} + virtual void setNameFilters(const QStringList &); + virtual void selectNameFilter(const QString &); + virtual QString selectedNameFilter() const; + +private: + virtual QWindowsNativeDialogBase *createNativeDialog(); + inline QWindowsXpNativeFileDialog *nativeFileDialog() const + { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); } +}; + +QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() +{ + if (QWindowsNativeDialogBase *result = QWindowsXpNativeFileDialog::create(options())) { + QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); + QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject())); + return result; + } + return 0; +} + +void QWindowsXpFileDialogHelper::setDirectory(const QString &directory) +{ + if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + nfd->setDirectory(directory); +} + +QString QWindowsXpFileDialogHelper::directory() const +{ + if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + return nfd->directory(); + return QString(); +} + +void QWindowsXpFileDialogHelper::selectFile(const QString &filename) +{ + if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + nfd->selectFile(filename); +} + +QStringList QWindowsXpFileDialogHelper::selectedFiles() const +{ + if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + return nfd->selectedFiles(); + return QStringList(); +} + +void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n) +{ + if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + nfd->setNameFilters(n); +} + +void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f) +{ + if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + nfd->selectNameFilter(f); +} + +QString QWindowsXpFileDialogHelper::selectedNameFilter() const +{ + if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) + return nfd->selectedNameFilter(); + return QString(); +} + +#endif // Q_OS_WINCE + /*! \class QWindowsNativeColorDialog \brief Native Windows color dialog. @@ -1399,10 +1801,11 @@ namespace QWindowsDialogs { // QWindowsDialogHelperBase creation functions bool useHelper(QPlatformTheme::DialogType type) { + if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs) + return false; switch (type) { case QPlatformTheme::FileDialog: - return true; - break; + return QSysInfo::windowsVersion() >= QSysInfo::WV_XP; case QPlatformTheme::ColorDialog: #ifdef USE_NATIVE_COLOR_DIALOG return true; @@ -1417,9 +1820,20 @@ bool useHelper(QPlatformTheme::DialogType type) QPlatformDialogHelper *createHelper(QPlatformTheme::DialogType type) { + if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs) + return 0; switch (type) { case QPlatformTheme::FileDialog: +#ifndef Q_OS_WINCE + if (QWindowsIntegration::instance()->options() & QWindowsIntegration::XpNativeDialogs + || QSysInfo::windowsVersion() == QSysInfo::WV_XP) { + return new QWindowsXpFileDialogHelper(); + } + if (QSysInfo::windowsVersion() > QSysInfo::WV_XP) + return new QWindowsFileDialogHelper(); +#else return new QWindowsFileDialogHelper(); +#endif // Q_OS_WINCE case QPlatformTheme::ColorDialog: #ifdef USE_NATIVE_COLOR_DIALOG return new QWindowsColorDialogHelper(); diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index f74b2140f6..805046c715 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -301,9 +301,15 @@ public: STDMETHOD(GiveFeedback)(DWORD dwEffect); private: - typedef QMap <Qt::DropAction, HCURSOR> ActionCursorMap; - - inline void clearCursors(); + class DragCursorHandle { + Q_DISABLE_COPY(DragCursorHandle) + public: + DragCursorHandle(HCURSOR c, quint64 k) : cursor(c), cacheKey(k) {} + ~DragCursorHandle() { DestroyCursor(cursor); } + HCURSOR cursor; + quint64 cacheKey; + }; + typedef QMap <Qt::DropAction, QSharedPointer<DragCursorHandle> > ActionCursorMap; QWindowsDrag *m_drag; Qt::MouseButtons m_currentButtons; @@ -322,7 +328,7 @@ QWindowsOleDropSource::QWindowsOleDropSource(QWindowsDrag *drag) : QWindowsOleDropSource::~QWindowsOleDropSource() { - clearCursors(); + m_cursors.clear(); if (QWindowsContext::verboseOLE) qDebug("%s", __FUNCTION__); } @@ -347,10 +353,14 @@ void QWindowsOleDropSource::createCursors() QPixmap cpm = drag->dragCursor(action); if (cpm.isNull()) cpm = m_drag->defaultCursor(action); + QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); + if (!cursorHandler.isNull() && cpm.cacheKey() == cursorHandler->cacheKey) + continue; if (cpm.isNull()) { qWarning("%s: Unable to obtain drag cursor for %d.", __FUNCTION__, action); continue; } + int w = cpm.width(); int h = cpm.height(); @@ -380,23 +390,14 @@ void QWindowsOleDropSource::createCursors() const int hotX = hasPixmap ? qMax(0,newHotSpot.x()) : 0; const int hotY = hasPixmap ? qMax(0,newHotSpot.y()) : 0; - if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY)) - m_cursors.insert(actions.at(cnum), sysCursor); + if (const HCURSOR sysCursor = QWindowsCursor::createPixmapCursor(newCursor, hotX, hotY)) { + m_cursors.insert(action, QSharedPointer<DragCursorHandle>(new DragCursorHandle(sysCursor, cpm.cacheKey()))); + } } if (QWindowsContext::verboseOLE) qDebug("%s %d cursors", __FUNCTION__, m_cursors.size()); } -void QWindowsOleDropSource::clearCursors() -{ - if (!m_cursors.isEmpty()) { - const ActionCursorMap::const_iterator cend = m_cursors.constEnd(); - for (ActionCursorMap::const_iterator it = m_cursors.constBegin(); it != cend; ++it) - DestroyCursor(it.value()); - m_cursors.clear(); - } -} - //--------------------------------------------------------------------- // IUnknown Methods //--------------------------------------------------------------------- @@ -488,9 +489,14 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) if (QWindowsContext::verboseOLE > 2) qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action); + QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); + quint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); + if (cursorHandler.isNull() || currentCacheKey != cursorHandler->cacheKey) + createCursors(); + const ActionCursorMap::const_iterator it = m_cursors.constFind(action); if (it != m_cursors.constEnd()) { - SetCursor(it.value()); + SetCursor(it.value()->cursor); return ResultFromScode(S_OK); } @@ -595,6 +601,9 @@ QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { + if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) + dh->DragEnter(reinterpret_cast<HWND>(m_window->winId()), pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect); + if (QWindowsContext::verboseOLE) qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, m_window, grfKeyState, pt.x, pt.y); @@ -608,6 +617,9 @@ QWindowsOleDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { + if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) + dh->DragOver(reinterpret_cast<POINT*>(&pt), *pdwEffect); + QWindow *dragOverWindow = findDragOverWindow(pt); if (QWindowsContext::verboseOLE) qDebug("%s widget=%p key=%lu, pt=%ld,%ld", __FUNCTION__, dragOverWindow, grfKeyState, pt.x, pt.y); @@ -628,6 +640,9 @@ QWindowsOleDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP QWindowsOleDropTarget::DragLeave() { + if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) + dh->DragLeave(); + if (QWindowsContext::verboseOLE) qDebug().nospace() <<__FUNCTION__ << ' ' << m_window; @@ -640,9 +655,12 @@ QWindowsOleDropTarget::DragLeave() #define KEY_STATE_BUTTON_MASK (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) QT_ENSURE_STACK_ALIGNED_FOR_SSE STDMETHODIMP -QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, +QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { + if (IDropTargetHelper* dh = QWindowsDrag::instance()->dropHelper()) + dh->Drop(pDataObj, reinterpret_cast<POINT*>(&pt), *pdwEffect); + QWindow *dropWindow = findDragOverWindow(pt); if (QWindowsContext::verboseOLE) @@ -700,6 +718,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, return NOERROR; } + /*! \class QWindowsDrag \brief Windows drag implementation. @@ -707,12 +726,15 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT /*pDataObj*/, DWORD grfKeyState, \ingroup qt-lighthouse-win */ -QWindowsDrag::QWindowsDrag() : m_dropDataObject(0) +QWindowsDrag::QWindowsDrag() : + m_dropDataObject(0), m_cachedDropTargetHelper(0) { } QWindowsDrag::~QWindowsDrag() { + if (m_cachedDropTargetHelper) + m_cachedDropTargetHelper->Release(); } /*! @@ -726,6 +748,18 @@ QMimeData *QWindowsDrag::dropData() return &m_dropData; } +/*! + \brief May be used to handle extended cursors functionality for drags from outside the app. +*/ +IDropTargetHelper* QWindowsDrag::dropHelper() { + if (!m_cachedDropTargetHelper) { + CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, + IID_IDropTargetHelper, + reinterpret_cast<void**>(&m_cachedDropTargetHelper)); + } + return m_cachedDropTargetHelper; +} + QPixmap QWindowsDrag::defaultCursor(Qt::DropAction action) const { switch (action) { diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h index ab06545884..d3bfd36955 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.h +++ b/src/plugins/platforms/windows/qwindowsdrag.h @@ -47,6 +47,8 @@ #include <qpa/qplatformdrag.h> #include <QtGui/QPixmap> +struct IDropTargetHelper; + QT_BEGIN_NAMESPACE class QWindowsDropMimeData : public QWindowsInternalMimeData { public: @@ -100,12 +102,16 @@ public: void releaseDropDataObject(); QMimeData *dropData(); + IDropTargetHelper* dropHelper(); + QPixmap defaultCursor(Qt::DropAction action) const; private: QWindowsDropMimeData m_dropData; IDataObject *m_dropDataObject; + IDropTargetHelper* m_cachedDropTargetHelper; + mutable QPixmap m_copyDragCursor; mutable QPixmap m_moveDragCursor; mutable QPixmap m_linkDragCursor; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index b7309c3f7c..a02f0cd494 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -279,6 +279,12 @@ static inline unsigned parseOptions(const QStringList ¶mList) } else if (param.endsWith(QLatin1String("native"))) { options |= QWindowsIntegration::FontDatabaseNative; } + } else if (param.startsWith(QLatin1String("dialogs="))) { + if (param.endsWith(QLatin1String("xp"))) { + options |= QWindowsIntegration::XpNativeDialogs; + } else if (param.endsWith(QLatin1String("none"))) { + options |= QWindowsIntegration::NoNativeDialogs; + } } else if (param == QLatin1String("gl=gdi")) { options |= QWindowsIntegration::DisableArb; } @@ -332,6 +338,8 @@ bool QWindowsIntegration::hasCapability(QPlatformIntegration::Capability cap) co #endif // !QT_NO_OPENGL case WindowMasks: return true; + case MultipleWindows: + return true; default: return QPlatformIntegration::hasCapability(cap); } diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index ca47dabb4b..abf663c052 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -56,7 +56,9 @@ public: enum Options { // Options to be passed on command line. FontDatabaseFreeType = 0x1, FontDatabaseNative = 0x2, - DisableArb = 0x4 + DisableArb = 0x4, + NoNativeDialogs = 0x8, + XpNativeDialogs = 0x10 }; explicit QWindowsIntegration(const QStringList ¶mList); diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index d9de911914..2fb905d23b 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -509,6 +509,7 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) con case TrashIcon: resourceId = 191; break; +#ifndef Q_OS_WINCE case MessageBoxInformation: iconName = IDI_INFORMATION; break; @@ -538,6 +539,7 @@ QPixmap QWindowsTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) con } } break; +#endif default: break; } diff --git a/src/plugins/platforms/windows/windows.pro b/src/plugins/platforms/windows/windows.pro index 1527f0e496..ca356e1276 100644 --- a/src/plugins/platforms/windows/windows.pro +++ b/src/plugins/platforms/windows/windows.pro @@ -1,4 +1,6 @@ TARGET = windows + +PLUGIN_TYPE = platforms load(qt_plugin) QT *= core-private @@ -6,7 +8,6 @@ QT *= gui-private QT *= platformsupport-private INCLUDEPATH += ../../../3rdparty/harfbuzz/src -QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/platforms # Note: OpenGL32 must precede Gdi32 as it overwrites some functions. LIBS *= -lole32 @@ -172,6 +173,3 @@ contains(QT_CONFIG, freetype) { OTHER_FILES += windows.json contains(QT_CONFIG, accessibility):include(accessible/accessible.pri) - -target.path += $$[QT_INSTALL_PLUGINS]/platforms -INSTALLS += target |