diff options
Diffstat (limited to 'src/plugins/platforms/windows')
24 files changed, 697 insertions, 284 deletions
diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp index 3ccc6212a9..8830d3023a 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -162,21 +162,10 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) } } - typedef void (WINAPI *PtrNotifyWinEvent)(DWORD, HWND, LONG, LONG); - #if defined(Q_OS_WINCE) // ### TODO: check for NotifyWinEvent in CE 6.0 // There is no user32.lib nor NotifyWinEvent for CE return; #else - static PtrNotifyWinEvent ptrNotifyWinEvent = 0; - static bool resolvedNWE = false; - if (!resolvedNWE) { - resolvedNWE = true; - ptrNotifyWinEvent = (PtrNotifyWinEvent)QSystemLibrary::resolve(QLatin1String("user32"), "NotifyWinEvent"); - } - if (!ptrNotifyWinEvent) - return; - // An event has to be associated with a window, // so find the first parent that is a widget and that has a WId QAccessibleInterface *iface = event->accessibleInterface(); @@ -213,7 +202,7 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) eventNum %= 50; //[0..49] int eventId = - (eventNum - 1); qAccessibleRecentSentEvents()->insert(eventId, qMakePair(QPointer<QObject>(event->object()), event->child())); - ptrNotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); + ::NotifyWinEvent(event->type(), hWnd, OBJID_CLIENT, eventId); ++eventNum; } #endif // Q_OS_WINCE @@ -283,7 +272,7 @@ bool QWindowsAccessibility::handleAccessibleObjectFromWindowRequest(HWND hwnd, W { if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId)) { /* For UI Automation */ - } else if ((DWORD)lParam == OBJID_CLIENT) { + } else if ((DWORD)lParam == DWORD(OBJID_CLIENT)) { #if 1 // Ignoring all requests while starting up // ### Maybe QPA takes care of this??? diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h index af0bd65c80..5a6dc0c2e5 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.h @@ -48,7 +48,6 @@ #include <oleacc.h> -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QWindowsAccessibility : public QPlatformAccessibility @@ -69,6 +68,5 @@ public: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QWINDOWSACCESSIBILITY_H diff --git a/src/plugins/platforms/windows/qtwindows_additional.h b/src/plugins/platforms/windows/qtwindows_additional.h index 8d59fbd7c6..3b2e9787a2 100644 --- a/src/plugins/platforms/windows/qtwindows_additional.h +++ b/src/plugins/platforms/windows/qtwindows_additional.h @@ -126,6 +126,10 @@ typedef struct tagUPDATELAYEREDWINDOWINFO { // IME. #define IMR_CONFIRMRECONVERTSTRING 0x0005 +#ifndef MAPVK_VK_TO_CHAR +# define MAPVK_VK_TO_CHAR 2 +#endif + #endif // if defined(Q_CC_MINGW) /* Touch is supported from Windows 7 onwards and data structures diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index 1ea660d944..f824666a54 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -783,7 +783,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, d->m_creationContext->obtainedGeometry.moveTo(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return true; case QtWindows::CalculateSize: - return false; + return QWindowsGeometryHint::handleCalculateSize(d->m_creationContext->customMargins, msg, result); default: break; } @@ -818,12 +818,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, platformWindow->getSizeHints(reinterpret_cast<MINMAXINFO *>(lParam)); return true;// maybe available on some SDKs revisit WM_NCCALCSIZE case QtWindows::CalculateSize: - // NCCALCSIZE_PARAMS structure if wParam==TRUE - if (wParam && QWindowsContext::verboseWindows) { - const NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(lParam); - qDebug() << platformWindow->window() << *ncp; - } - break; + return QWindowsGeometryHint::handleCalculateSize(platformWindow->customMargins(), msg, result); #endif case QtWindows::ExposeEvent: return platformWindow->handleWmPaint(hwnd, message, wParam, lParam); diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 4dc9af61d8..5b2a3acbae 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -395,6 +395,19 @@ QWindowsWindowCursor QWindowsCursor::standardWindowCursor(Qt::CursorShape shape) } /*! + \brief Return cached pixmap cursor or create new one. +*/ + +QWindowsWindowCursor QWindowsCursor::pixmapWindowCursor(const QCursor &c) +{ + const qint64 cacheKey = c.pixmap().cacheKey(); + PixmapCursorCache::iterator it = m_pixmapCursorCache.find(cacheKey); + if (it == m_pixmapCursorCache.end()) + it = m_pixmapCursorCache.insert(cacheKey, QWindowsWindowCursor(c)); + return it.value(); +} + +/*! \brief Set a cursor on a window. This is called frequently as the mouse moves over widgets in the window @@ -406,11 +419,15 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window) if (QWindowsContext::verboseWindows > 1) qDebug() << __FUNCTION__ << cursorIn << window; - if (!cursorIn || !window) + if (!window) + return; + if (!cursorIn) { + QWindowsWindow::baseWindowOf(window)->setCursor(QWindowsWindowCursor()); return; + } const QWindowsWindowCursor wcursor = cursorIn->shape() == Qt::BitmapCursor ? - QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); + pixmapWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); if (wcursor.handle()) { QWindowsWindow::baseWindowOf(window)->setCursor(wcursor); } else { @@ -449,6 +466,7 @@ void QWindowsCursor::setPos(const QPoint &pos) class QWindowsWindowCursorData : public QSharedData { public: + QWindowsWindowCursorData() : m_cursor(Qt::ArrowCursor), m_handle(0) {} explicit QWindowsWindowCursorData(const QCursor &c); ~QWindowsWindowCursorData(); @@ -464,7 +482,13 @@ QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) : QWindowsWindowCursorData::~QWindowsWindowCursorData() { - DestroyCursor(m_handle); + if (m_handle) + DestroyCursor(m_handle); +} + +QWindowsWindowCursor::QWindowsWindowCursor() : + m_data(new QWindowsWindowCursorData) +{ } QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : @@ -488,6 +512,11 @@ QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCurs return *this; } +bool QWindowsWindowCursor::isNull() const +{ + return m_data->m_handle == 0; +} + QCursor QWindowsWindowCursor::cursor() const { return m_data->m_cursor; diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index b0256d4203..1e818bc9b8 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -55,11 +55,13 @@ class QWindowsWindowCursorData; class QWindowsWindowCursor { public: + QWindowsWindowCursor(); explicit QWindowsWindowCursor(const QCursor &c); ~QWindowsWindowCursor(); QWindowsWindowCursor(const QWindowsWindowCursor &c); QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); + bool isNull() const; QCursor cursor() const; HCURSOR handle() const; @@ -81,11 +83,14 @@ public: static QPoint mousePosition(); QWindowsWindowCursor standardWindowCursor(Qt::CursorShape s = Qt::ArrowCursor); + QWindowsWindowCursor pixmapWindowCursor(const QCursor &c); private: typedef QHash<Qt::CursorShape, QWindowsWindowCursor> StandardCursorCache; + typedef QHash<qint64, QWindowsWindowCursor> PixmapCursorCache; StandardCursorCache m_standardCursorCache; + PixmapCursorCache m_pixmapCursorCache; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 74193c47a3..869e94b566 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -58,6 +58,10 @@ #include <QtCore/QObject> #include <QtCore/QThread> #include <QtCore/QSysInfo> +#include <QtCore/QSharedData> +#include <QtCore/QExplicitlySharedDataPointer> +#include <QtCore/QMutex> +#include <QtCore/QMutexLocker> #include <QtCore/private/qsystemlibrary_p.h> #include "qtwindows_additional.h" @@ -82,6 +86,8 @@ static const IID q_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0 #define IID_IShellItem q_IID_IShellItem #else static const IID IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe}}; +static const IID IID_IShellItemArray = {0xb63ea76d, 0x1f85, 0x456f, {0xa1, 0x9c, 0x48, 0x15, 0x9e, 0xfa, 0x85, 0x8b}}; +# define LFF_FORCEFILESYSTEM 1 #endif static const IID IID_IFileDialogEvents = {0x973510db, 0x7d7f, 0x452b,{0x89, 0x75, 0x74, 0xa8, 0x58, 0x28, 0xd3, 0x54}}; static const CLSID CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7}}; @@ -249,6 +255,34 @@ DECLARE_INTERFACE_(IShellItemArray, IUnknown) }; #endif +#ifndef __IShellLibrary_INTERFACE_DEFINED__ + +enum LIBRARYOPTIONFLAGS {}; +enum DEFAULTSAVEFOLDERTYPE { DSFT_DETECT = 1 }; +enum LIBRARYSAVEFLAGS {}; + +DECLARE_INTERFACE_(IShellLibrary, IUnknown) +{ + STDMETHOD(LoadLibraryFromItem)(THIS_ IShellItem *psiLibrary, DWORD grfMode) PURE; + STDMETHOD(LoadLibraryFromKnownFolder)(THIS_ const GUID &kfidLibrary, DWORD grfMode) PURE; + STDMETHOD(AddFolder)(THIS_ IShellItem *psiLocation) PURE; + STDMETHOD(RemoveFolder)(THIS_ IShellItem *psiLocation) PURE; + STDMETHOD(GetFolders)(THIS_ int lff, REFIID riid, void **ppv) PURE; + STDMETHOD(ResolveFolder)(THIS_ IShellItem *psiFolderToResolve, DWORD dwTimeout, REFIID riid, void **ppv) PURE; + STDMETHOD(GetDefaultSaveFolder)(THIS_ DEFAULTSAVEFOLDERTYPE dsft, REFIID riid, void **ppv) PURE; + STDMETHOD(SetDefaultSaveFolder)(THIS_ DEFAULTSAVEFOLDERTYPE dsft, IShellItem *psi) PURE; + STDMETHOD(GetOptions)(THIS_ LIBRARYOPTIONFLAGS *plofOptions) PURE; + STDMETHOD(SetOptions)(THIS_ LIBRARYOPTIONFLAGS lofMask, LIBRARYOPTIONFLAGS lofOptions) PURE; + STDMETHOD(GetFolderType)(THIS_ GUID *pftid) PURE; + STDMETHOD(SetFolderType)(THIS_ const GUID &ftid) PURE; + STDMETHOD(GetIcon)(THIS_ LPWSTR *ppszIcon) PURE; + STDMETHOD(SetIcon)(THIS_ LPCWSTR pszIcon) PURE; + STDMETHOD(Commit)(THIS_) PURE; + STDMETHOD(Save)(THIS_ IShellItem *psiFolderToSaveIn, LPCWSTR pszLibraryName, LIBRARYSAVEFLAGS lsf, IShellItem **ppsiSavedTo) PURE; + STDMETHOD(SaveInKnownFolder)(THIS_ const GUID &kfidToSaveIn, LPCWSTR pszLibraryName, LIBRARYSAVEFLAGS lsf,IShellItem **ppsiSavedTo) PURE; +}; +#endif + #ifndef __IModalWindow_INTERFACE_DEFINED__ DECLARE_INTERFACE_(IModalWindow, IUnknown) { @@ -387,6 +421,9 @@ void eatMouseMove() Vista on) that mimick the behaviour of their QDialog counterparts as close as possible. + Instances of derived classes are controlled by + QWindowsDialogHelperBase-derived classes. + A major difference is that there is only an exec(), which is a modal, blocking call; there is no non-blocking show(). There 2 types of native dialogs: @@ -401,6 +438,7 @@ void eatMouseMove() like close() can be called on them from event handlers. \endlist + \sa QWindowsDialogHelperBase \internal \ingroup qt-lighthouse-win */ @@ -411,7 +449,6 @@ class QWindowsNativeDialogBase : public QObject public: virtual void setWindowTitle(const QString &title) = 0; virtual void exec(HWND owner = 0) = 0; - virtual QPlatformDialogHelper::DialogCode result() const = 0; signals: void accepted(); @@ -432,12 +469,10 @@ protected: The native dialog is created in setVisible_sys() since then modality and the state of DontUseNativeDialog is known. - Modal dialogs are then started via the platformNativeDialogModalHelp(), - platformNativeDialogModalHelp() slots. - Non-modal dialogs are shown using a separate thread should - they support it. + Modal dialogs are then run by exec(). Non-modal dialogs are shown using a + separate thread started in show() should they support it. - \sa QWindowsDialogThread + \sa QWindowsDialogThread, QWindowsNativeDialogBase \internal \ingroup qt-lighthouse-win */ @@ -445,17 +480,12 @@ protected: template <class BaseClass> QWindowsDialogHelperBase<BaseClass>::QWindowsDialogHelperBase() : m_nativeDialog(0), - m_ownerWindow(0) + m_ownerWindow(0), + m_timerId(0) { } template <class BaseClass> -QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase() -{ - delete m_nativeDialog; -} - -template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() const { if (!m_nativeDialog) { @@ -466,6 +496,19 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() co } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::deleteNativeDialog() +{ + delete m_nativeDialog; + m_nativeDialog = 0; +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::timerEvent(QTimerEvent *) +{ + startDialogThread(); +} + +template <class BaseClass> QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialog() { // Create dialog and apply common settings. @@ -486,22 +529,18 @@ QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialo class QWindowsDialogThread : public QThread { public: - QWindowsDialogThread(QWindowsNativeDialogBase *dialog, - HWND owner = 0) : - m_dialog(dialog), m_owner(owner) {} - + QWindowsDialogThread(QPlatformDialogHelper *h) : m_helper(h) {} void run(); private: - QWindowsNativeDialogBase *m_dialog; - const HWND m_owner; + QPlatformDialogHelper *m_helper; }; void QWindowsDialogThread::run() { if (QWindowsContext::verboseDialogs) qDebug(">%s" , __FUNCTION__); - m_dialog->exec(m_owner); + m_helper->exec(); deleteLater(); if (QWindowsContext::verboseDialogs) qDebug("<%s" , __FUNCTION__); @@ -512,7 +551,7 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, Qt::WindowModality windowModality, QWindow *parent) { - const bool modal = (windowModality == Qt::ApplicationModal); + const bool modal = (windowModality != Qt::NonModal); if (parent) { m_ownerWindow = QWindowsWindow::handleOf(parent); } else { @@ -521,18 +560,40 @@ bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags, if (QWindowsContext::verboseDialogs) qDebug("%s modal=%d native=%p parent=%p" , __FUNCTION__, modal, m_nativeDialog, m_ownerWindow); - if (!modal && !supportsNonModalDialog()) + if (!modal && !supportsNonModalDialog(parent)) return false; // Was it changed in-between? if (!ensureNativeDialog()) return false; - if (!modal) { // Modal dialogs are shown in separate slot. - QWindowsDialogThread *thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow); - thread->start(); + // Start a background thread to show the dialog. For modal dialogs, + // a subsequent call to exec() may follow. So, start an idle timer + // which will start the dialog thread. If exec() is then called, the + // timer is stopped and dialog->exec() is called directly. + if (modal) { + m_timerId = this->startTimer(0); + } else { + startDialogThread(); } return true; } template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::startDialogThread() +{ + QWindowsDialogThread *thread = new QWindowsDialogThread(this); + thread->start(); + stopTimer(); +} + +template <class BaseClass> +void QWindowsDialogHelperBase<BaseClass>::stopTimer() +{ + if (m_timerId) { + this->killTimer(m_timerId); + m_timerId = 0; + } +} + +template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() { if (m_nativeDialog) @@ -545,8 +606,11 @@ void QWindowsDialogHelperBase<BaseClass>::exec() { if (QWindowsContext::verboseDialogs) qDebug("%s" , __FUNCTION__); - if (QWindowsNativeDialogBase *nd = nativeDialog()) + stopTimer(); + if (QWindowsNativeDialogBase *nd = nativeDialog()) { nd->exec(m_ownerWindow); + deleteNativeDialog(); + } } static inline bool snapToDefaultButtonHint() @@ -568,6 +632,101 @@ QVariant QWindowsDialogHelperBase<BaseClass>::styleHint(QPlatformDialogHelper::S } /*! + \class QWindowsFileDialogSharedData + \brief Explicitly shared file dialog parameters that are not in QFileDialogOptions. + + Contain Parameters that need to be cached while the native dialog does not + exist yet. In addition, the data are updated by the change notifications of the + IFileDialogEvent, as querying them after the dialog has closed + does not reliably work. Provides thread-safe setters (for the non-modal case). + + \internal + \ingroup qt-lighthouse-win + \sa QFileDialogOptions +*/ + +class QWindowsFileDialogSharedData +{ +public: + QWindowsFileDialogSharedData() : m_data(new Data) {} + void fromOptions(const QSharedPointer<QFileDialogOptions> &o); + + QString directory() const; + void setDirectory(const QString &); + QString selectedNameFilter() const; + void setSelectedNameFilter(const QString &); + QStringList selectedFiles() const; + void setSelectedFiles(const QStringList &); + QString selectedFile() const; + +private: + class Data : public QSharedData { + public: + QString directory; + QString selectedNameFilter; + QStringList selectedFiles; + QMutex mutex; + }; + QExplicitlySharedDataPointer<Data> m_data; +}; + +inline QString QWindowsFileDialogSharedData::directory() const +{ + m_data->mutex.lock(); + const QString result = m_data->directory; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setDirectory(const QString &d) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = d; +} + +inline QString QWindowsFileDialogSharedData::selectedNameFilter() const +{ + m_data->mutex.lock(); + const QString result = m_data->selectedNameFilter; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setSelectedNameFilter(const QString &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedNameFilter = f; +} + +inline QStringList QWindowsFileDialogSharedData::selectedFiles() const +{ + m_data->mutex.lock(); + const QStringList result = m_data->selectedFiles; + m_data->mutex.unlock(); + return result; +} + +inline QString QWindowsFileDialogSharedData::selectedFile() const +{ + const QStringList files = selectedFiles(); + return files.isEmpty() ? QString() : files.front(); +} + +inline void QWindowsFileDialogSharedData::setSelectedFiles(const QStringList &f) +{ + QMutexLocker (&m_data->mutex); + m_data->selectedFiles = f; +} + +inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFileDialogOptions> &o) +{ + QMutexLocker (&m_data->mutex); + m_data->directory = o->initialDirectory(); + m_data->selectedFiles = o->initiallySelectedFiles(); + m_data->selectedNameFilter = o->initiallySelectedNameFilter(); +} + +/*! \class QWindowsNativeFileDialogEventHandler \brief Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase @@ -609,7 +768,7 @@ public: } // IFileDialogEvents methods - IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; } + IFACEMETHODIMP OnFileOk(IFileDialog *); IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; } IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *); IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; } @@ -658,15 +817,17 @@ class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase public: ~QWindowsNativeFileDialogBase(); - inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am); + inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am, const QWindowsFileDialogSharedData &data); virtual void setWindowTitle(const QString &title); inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options); inline void setDirectory(const QString &directory); + inline void updateDirectory() { setDirectory(m_data.directory()); } inline QString directory() const; virtual void exec(HWND owner = 0); inline void setNameFilters(const QStringList &f); inline void selectNameFilter(const QString &filter); + inline void updateSelectedNameFilter() { selectNameFilter(m_data.selectedNameFilter()); } inline QString selectedNameFilter() const; void selectFile(const QString &fileName) const; bool hideFiltersDetails() const { return m_hideFiltersDetails; } @@ -674,14 +835,16 @@ public: void setDefaultSuffix(const QString &s); inline void setLabelText(QFileDialogOptions::DialogLabel l, const QString &text); - virtual QPlatformDialogHelper::DialogCode result() const - { return fileResult(); } - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const = 0; + // Return the selected files for tracking in OnSelectionChanged(). virtual QStringList selectedFiles() const = 0; + // Return the result for tracking in OnFileOk(). Differs from selection for + // example by appended default suffixes, etc. + virtual QStringList dialogResult() const = 0; inline void onFolderChange(IShellItem *); inline void onSelectionChange(); inline void onTypeChange(); + inline bool onFileOk(); signals: void directoryEntered(const QString& directory); @@ -692,23 +855,30 @@ public slots: virtual void close() { m_fileDialog->Close(S_OK); } protected: - QWindowsNativeFileDialogBase(); + explicit QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data); bool init(const CLSID &clsId, const IID &iid); inline IFileDialog * fileDialog() const { return m_fileDialog; } static QString itemPath(IShellItem *item); + static QStringList libraryItemFolders(IShellItem *item); + static QString libraryItemDefaultSaveFolder(IShellItem *item); static int itemPaths(IShellItemArray *items, QStringList *fileResult = 0); static IShellItem *shellItem(const QString &path); + const QWindowsFileDialogSharedData &data() const { return m_data; } + QWindowsFileDialogSharedData &data() { return m_data; } + private: IFileDialog *m_fileDialog; IFileDialogEvents *m_dialogEvents; DWORD m_cookie; QStringList m_nameFilters; bool m_hideFiltersDetails; + QWindowsFileDialogSharedData m_data; }; -QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase() : - m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false) +QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data) : + m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false), + m_data(data) { } @@ -764,15 +934,17 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path) return result; } #endif - qErrnoWarning("%s: SHCreateItemFromParsingName()) failed", __FUNCTION__); + qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(path)); return 0; } void QWindowsNativeFileDialogBase::setDirectory(const QString &directory) { - if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { - m_fileDialog->SetFolder(psi); - psi->Release(); + if (!directory.isEmpty()) { + if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) { + m_fileDialog->SetFolder(psi); + psi->Release(); + } } } @@ -832,17 +1004,94 @@ void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode, QF qErrnoWarning("%s: SetOptions() failed", __FUNCTION__); } -QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item) +#ifndef Q_OS_WINCE + +// Helper for "Libraries": collections of folders appearing from Windows 7 +// on, visible in the file dialogs. + +// Load a library from a IShellItem (sanitized copy of the inline function +// SHLoadLibraryFromItem from ShObjIdl.h, which does not exist for MinGW). +static IShellLibrary *sHLoadLibraryFromItem(IShellItem *libraryItem, DWORD mode) +{ + // ID symbols present from Windows 7 on: + static const CLSID classId_ShellLibrary = {0xd9b3211d, 0xe57f, 0x4426, {0xaa, 0xef, 0x30, 0xa8, 0x6, 0xad, 0xd3, 0x97}}; + static const IID iId_IShellLibrary = {0x11a66efa, 0x382e, 0x451a, {0x92, 0x34, 0x1e, 0xe, 0x12, 0xef, 0x30, 0x85}}; + + IShellLibrary *helper = 0; + IShellLibrary *result = 0; + if (SUCCEEDED(CoCreateInstance(classId_ShellLibrary, NULL, CLSCTX_INPROC_SERVER, iId_IShellLibrary, reinterpret_cast<void **>(&helper)))) + if (SUCCEEDED(helper->LoadLibraryFromItem(libraryItem, mode))) + helper->QueryInterface(iId_IShellLibrary, reinterpret_cast<void **>(&result)); + if (helper) + helper->Release(); + return result; +} + +// Return all folders of a library-type item. +QStringList QWindowsNativeFileDialogBase::libraryItemFolders(IShellItem *item) +{ + QStringList result; + if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) { + IShellItemArray *itemArray = 0; + if (SUCCEEDED(library->GetFolders(LFF_FORCEFILESYSTEM, IID_IShellItemArray, reinterpret_cast<void **>(&itemArray)))) { + QWindowsNativeFileDialogBase::itemPaths(itemArray, &result); + itemArray->Release(); + } + library->Release(); + } + return result; +} + +// Return default save folders of a library-type item. +QString QWindowsNativeFileDialogBase::libraryItemDefaultSaveFolder(IShellItem *item) { QString result; - LPWSTR name = 0; - if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) { - result = QDir::cleanPath(QString::fromWCharArray(name)); - CoTaskMemFree(name); + if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) { + IShellItem *item = 0; + if (SUCCEEDED(library->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, reinterpret_cast<void **>(&item)))) { + result = QWindowsNativeFileDialogBase::itemPath(item); + item->Release(); + } + library->Release(); } return result; } +#else // !Q_OS_WINCE + +QStringList QWindowsNativeFileDialogBase::libraryItemPaths(IShellItem *) +{ + return QStringList(); +} + +QString QWindowsNativeFileDialogBase::libraryDefaultSaveFolder(IShellItem *) +{ + return QString(); +} + +#endif // Q_OS_WINCE + +QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item) +{ + SFGAOF attributes = 0; + // Check whether it has a file system representation? + if (FAILED(item->GetAttributes(SFGAO_FILESYSTEM, &attributes))) + return QString(); + if (attributes & SFGAO_FILESYSTEM) { + LPWSTR name = 0; + QString result; + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) { + result = QDir::cleanPath(QString::fromWCharArray(name)); + CoTaskMemFree(name); + } + return result; + } + // Check for a "Library" item + if ((QSysInfo::windowsVersion() & QSysInfo::WV_NT_based) && QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS7) + return QWindowsNativeFileDialogBase::libraryItemDefaultSaveFolder(item); + return QString(); +} + int QWindowsNativeFileDialogBase::itemPaths(IShellItemArray *items, QStringList *result /* = 0 */) { @@ -983,14 +1232,16 @@ static int indexOfNameFilter(const QStringList &filters, const QString &needle) void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter) { + if (filter.isEmpty()) + return; const int index = indexOfNameFilter(m_nameFilters, filter); - if (index >= 0) { - m_fileDialog->SetFileTypeIndex(index + 1); // one-based. - } else { + if (index < 0) { qWarning("%s: Invalid parameter '%s' not found in '%s'.", __FUNCTION__, qPrintable(filter), qPrintable(m_nameFilters.join(QStringLiteral(", ")))); + return; } + m_fileDialog->SetFileTypeIndex(index + 1); // one-based. } QString QWindowsNativeFileDialogBase::selectedNameFilter() const @@ -1008,6 +1259,7 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) { if (item) { const QString directory = QWindowsNativeFileDialogBase::itemPath(item); + m_data.setDirectory(directory); emit directoryEntered(directory); } } @@ -1015,13 +1267,23 @@ void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item) void QWindowsNativeFileDialogBase::onSelectionChange() { const QStringList current = selectedFiles(); + m_data.setSelectedFiles(current); if (current.size() == 1) emit currentChanged(current.front()); } void QWindowsNativeFileDialogBase::onTypeChange() { - emit filterSelected(selectedNameFilter()); + const QString filter = selectedNameFilter(); + m_data.setSelectedNameFilter(filter); + emit filterSelected(filter); +} + +bool QWindowsNativeFileDialogBase::onFileOk() +{ + // Store selected files as GetResults() returns invalid data after the dialog closes. + m_data.setSelectedFiles(dialogResult()); + return true; } HRESULT QWindowsNativeFileDialogEventHandler::OnFolderChanging(IFileDialog *, IShellItem *item) @@ -1042,6 +1304,11 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) return S_OK; } +HRESULT QWindowsNativeFileDialogEventHandler::OnFileOk(IFileDialog *) +{ + return m_nativeFileDialog->onFileOk() ? S_OK : S_FALSE; +} + /*! \class QWindowsNativeSaveFileDialog \brief Windows native file save dialog wrapper around IFileSaveDialog. @@ -1055,8 +1322,10 @@ HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *) class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeSaveFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; }; // Append a suffix from the name filter "Foo files (*.foo;*.bar)" @@ -1079,17 +1348,13 @@ static inline QString appendSuffix(const QString &fileName, const QString &filte return fileName + QLatin1Char('.') + filter.mid(suffixPos, endPos - suffixPos); } -QPlatformDialogHelper::DialogCode QWindowsNativeSaveFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeSaveFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItem *item = 0; - const HRESULT hr = fileDialog()->GetResult(&item); - if (FAILED(hr) || !item) - return QPlatformDialogHelper::Rejected; - if (result) - result->push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); - return QPlatformDialogHelper::Accepted; + if (SUCCEEDED(fileDialog()->GetResult(&item)) && item) + result.push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter())); + return result; } QStringList QWindowsNativeSaveFileDialog::selectedFiles() const @@ -1115,23 +1380,23 @@ QStringList QWindowsNativeSaveFileDialog::selectedFiles() const class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase { public: - virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const; + explicit QWindowsNativeOpenFileDialog(const QWindowsFileDialogSharedData &data) : + QWindowsNativeFileDialogBase(data) {} virtual QStringList selectedFiles() const; + virtual QStringList dialogResult() const; private: inline IFileOpenDialog *openFileDialog() const { return static_cast<IFileOpenDialog *>(fileDialog()); } }; -QPlatformDialogHelper::DialogCode QWindowsNativeOpenFileDialog::fileResult(QStringList *result /* = 0 */) const +QStringList QWindowsNativeOpenFileDialog::dialogResult() const { - if (result) - result->clear(); + QStringList result; IShellItemArray *items = 0; - const HRESULT hr = openFileDialog()->GetResults(&items); - if (SUCCEEDED(hr) && items && QWindowsNativeFileDialogBase::itemPaths(items, result) > 0) - return QPlatformDialogHelper::Accepted; - return QPlatformDialogHelper::Rejected; + if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) + QWindowsNativeFileDialogBase::itemPaths(items, &result); + return result; } QStringList QWindowsNativeOpenFileDialog::selectedFiles() const @@ -1150,17 +1415,18 @@ QStringList QWindowsNativeOpenFileDialog::selectedFiles() const QFileDialog::AcceptMode. */ -QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am) +QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am, + const QWindowsFileDialogSharedData &data) { QWindowsNativeFileDialogBase *result = 0; if (am == QFileDialogOptions::AcceptOpen) { - result = new QWindowsNativeOpenFileDialog; + result = new QWindowsNativeOpenFileDialog(data); if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) { delete result; return 0; } } else { - result = new QWindowsNativeSaveFileDialog; + result = new QWindowsNativeSaveFileDialog(data); if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) { delete result; return 0; @@ -1169,16 +1435,17 @@ QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOp return result; } +static inline bool isQQuickWindow(const QWindow *w = 0) +{ + return w && w->inherits("QQuickWindow"); +} + /*! \class QWindowsFileDialogHelper \brief Helper for native Windows file dialogs - Non-modal dialogs are disabled for now. The functionality is - implemented in principle, however there are failures - when querying the results from a dialog run in another thread. - This could probably be fixed be calling CoInitializeEx() with - the right parameters from each thread. The problem is though - that calls to CoInitialize() occur in several places in Qt. + For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + but only on QQuickWindows, which do not have a fallback. \internal \ingroup qt-lighthouse-win @@ -1188,8 +1455,9 @@ class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDi { public: QWindowsFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + // For Qt 4 compatibility, do not create native non-modal dialogs on widgets, + // but only on QQuickWindows, which do not have a fallback. + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1205,11 +1473,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsNativeFileDialogBase *nativeFileDialog() const { return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); } + + // Cache for the case no native dialog is created. + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() { - QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode()); + QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode(), m_data); if (!result) return 0; QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); @@ -1223,6 +1494,7 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() // Apply settings. const QSharedPointer<QFileDialogOptions> &opts = options(); + m_data.fromOptions(opts); result->setWindowTitle(opts->windowTitle()); result->setMode(opts->fileMode(), opts->options()); result->setHideFiltersDetails(opts->testOption(QFileDialogOptions::HideNameFilterDetails)); @@ -1233,18 +1505,14 @@ QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog() result->setLabelText(QFileDialogOptions::FileName, opts->labelText(QFileDialogOptions::FileName)); if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) result->setLabelText(QFileDialogOptions::Accept, opts->labelText(QFileDialogOptions::Accept)); - const QString initialDirectory = opts->initialDirectory(); - if (!initialDirectory.isEmpty()) - result->setDirectory(initialDirectory); + result->updateDirectory(); + result->updateSelectedNameFilter(); const QStringList initialSelection = opts->initiallySelectedFiles(); if (initialSelection.size() > 0) { QFileInfo info(initialSelection.front()); if (!info.isDir()) result->selectFile(info.fileName()); } - const QString initialNameFilter = opts->initiallySelectedNameFilter(); - if (!initialNameFilter.isEmpty()) - result->selectNameFilter(initialNameFilter); const QString defaultSuffix = opts->defaultSuffix(); if (!defaultSuffix.isEmpty()) result->setDefaultSuffix(defaultSuffix); @@ -1256,15 +1524,14 @@ void QWindowsFileDialogHelper::setDirectory(const QString &directory) if (QWindowsContext::verboseDialogs) qDebug("%s %s" , __FUNCTION__, qPrintable(directory)); - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); + if (hasNativeDialog()) + nativeFileDialog()->updateDirectory(); } QString QWindowsFileDialogHelper::directory() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsFileDialogHelper::selectFile(const QString &fileName) @@ -1278,13 +1545,7 @@ void QWindowsFileDialogHelper::selectFile(const QString &fileName) QStringList QWindowsFileDialogHelper::selectedFiles() const { - QStringList files; - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->fileResult(&files); - if (QWindowsContext::verboseDialogs) - qDebug("%s files='%s'" , __FUNCTION__, - qPrintable(files.join(QStringLiteral(", ")))); - return files; + return m_data.selectedFiles(); } void QWindowsFileDialogHelper::setFilter() @@ -1303,15 +1564,14 @@ void QWindowsFileDialogHelper::setNameFilters(const QStringList &filters) void QWindowsFileDialogHelper::selectNameFilter(const QString &filter) { - if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - nfd->selectNameFilter(filter); + m_data.setSelectedNameFilter(filter); + if (hasNativeDialog()) + nativeFileDialog()->updateSelectedNameFilter(); } QString QWindowsFileDialogHelper::selectedNameFilter() const { - if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #ifndef Q_OS_WINCE @@ -1335,20 +1595,12 @@ class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase public: typedef QSharedPointer<QFileDialogOptions> OptionsPtr; - static QWindowsXpNativeFileDialog *create(const OptionsPtr &options); + static QWindowsXpNativeFileDialog *create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); 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: @@ -1358,19 +1610,15 @@ private: typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW); typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW); - explicit QWindowsXpNativeFileDialog(const OptionsPtr &options); + explicit QWindowsXpNativeFileDialog(const OptionsPtr &options, const QWindowsFileDialogSharedData &data); 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; + QWindowsFileDialogSharedData m_data; static PtrGetOpenFileNameW m_getOpenFileNameW; static PtrGetSaveFileNameW m_getSaveFileNameW; @@ -1379,7 +1627,7 @@ private: QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0; QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0; -QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options) +QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data) { // GetOpenFileNameW() GetSaveFileName() are resolved // dynamically as not to create a dependency on Comdlg32, which @@ -1390,51 +1638,33 @@ QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW")); } if (m_getOpenFileNameW && m_getSaveFileNameW) - return new QWindowsXpNativeFileDialog(options); + return new QWindowsXpNativeFileDialog(options, data); return 0; } -QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) : - m_options(options), m_result(QPlatformDialogHelper::Rejected) +QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options, + const QWindowsFileDialogSharedData &data) : + m_options(options), m_result(QPlatformDialogHelper::Rejected), m_data(data) { - 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 ? + const QStringList selectedFiles = + m_options->fileMode() == QFileDialogOptions::DirectoryOnly ? execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex); + m_data.setSelectedFiles(selectedFiles); QWindowsDialogs::eatMouseMove(); - if (m_selectedFiles.isEmpty()) { + if (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(); + const QStringList nameFilters = m_options->nameFilters(); + if (selectedFilterIndex >= 0 && selectedFilterIndex < nameFilters.size()) + m_data.setSelectedNameFilter(nameFilters.at(selectedFilterIndex)); + m_data.setDirectory(QFileInfo(selectedFiles.front()).absolutePath()); m_result = QPlatformDialogHelper::Accepted; emit accepted(); } @@ -1458,9 +1688,11 @@ typedef PIDLIST_ABSOLUTE qt_LpItemIdList; 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())); + case BFFM_INITIALIZED: { + const QString initialFile = m_data.selectedFile(); + if (!initialFile.isEmpty()) + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initialFile.utf16())); + } break; case BFFM_SELCHANGED: { wchar_t path[MAX_PATH]; @@ -1532,7 +1764,7 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow *ptr++ = 0; } *ptr = 0; - const int nameFilterIndex = indexOfNameFilter(m_nameFilters, m_selectedNameFilter); + const int nameFilterIndex = indexOfNameFilter(m_options->nameFilters(), m_data.selectedNameFilter()); if (nameFilterIndex >= 0) ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based. // lpstrFile receives the initial selection and is the buffer @@ -1540,10 +1772,10 @@ void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND ow // will not show. ofn->nMaxFile = 65535; const QString initiallySelectedFile = - QDir::toNativeSeparators(m_initialFile).remove(QLatin1Char('<')). + QDir::toNativeSeparators(m_data.selectedFile()).remove(QLatin1Char('<')). remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|')); ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile); - ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_directory)); + ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.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 @@ -1612,8 +1844,7 @@ class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFile { public: QWindowsXpFileDialogHelper() {} - virtual bool supportsNonModalDialog() const { return false; } - + virtual bool supportsNonModalDialog(const QWindow *parent = 0) const { return isQQuickWindow(parent); } virtual bool defaultNameFilterDisables() const { return true; } virtual void setDirectory(const QString &directory); @@ -1629,11 +1860,14 @@ private: virtual QWindowsNativeDialogBase *createNativeDialog(); inline QWindowsXpNativeFileDialog *nativeFileDialog() const { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); } + + QWindowsFileDialogSharedData m_data; }; QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() { - if (QWindowsNativeDialogBase *result = QWindowsXpNativeFileDialog::create(options())) { + m_data.fromOptions(options()); + if (QWindowsXpNativeFileDialog *result = QWindowsXpNativeFileDialog::create(options(), m_data)) { QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept())); QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject())); return result; @@ -1643,47 +1877,37 @@ QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog() void QWindowsXpFileDialogHelper::setDirectory(const QString &directory) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setDirectory(directory); + m_data.setDirectory(directory); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::directory() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->directory(); - return QString(); + return m_data.directory(); } void QWindowsXpFileDialogHelper::selectFile(const QString &filename) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectFile(filename); + m_data.setSelectedFiles(QStringList(filename)); // Dialog cannot be updated at run-time. } QStringList QWindowsXpFileDialogHelper::selectedFiles() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedFiles(); - return QStringList(); + return m_data.selectedFiles(); } -void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n) +void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->setNameFilters(n); + // Dialog cannot be updated at run-time. } void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f) { - if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - nfd->selectNameFilter(f); + m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time. } QString QWindowsXpFileDialogHelper::selectedNameFilter() const { - if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog()) - return nfd->selectedNameFilter(); - return QString(); + return m_data.selectedNameFilter(); } #endif // Q_OS_WINCE diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index 07f105cb35..7884f398f3 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.h +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.h @@ -65,6 +65,7 @@ template <class BaseClass> class QWindowsDialogHelperBase : public BaseClass { public: + ~QWindowsDialogHelperBase() { deleteNativeDialog(); } virtual void exec(); virtual bool show(Qt::WindowFlags windowFlags, @@ -73,19 +74,24 @@ public: virtual void hide(); virtual QVariant styleHint(QPlatformDialogHelper::StyleHint) const; - virtual bool supportsNonModalDialog() const { return true; } + virtual bool supportsNonModalDialog(const QWindow * /* parent */ = 0) const { return true; } protected: QWindowsDialogHelperBase(); - ~QWindowsDialogHelperBase(); QWindowsNativeDialogBase *nativeDialog() const; + inline bool hasNativeDialog() const { return m_nativeDialog; } + void deleteNativeDialog(); + void timerEvent(QTimerEvent *); private: virtual QWindowsNativeDialogBase *createNativeDialog() = 0; inline QWindowsNativeDialogBase *ensureNativeDialog(); + inline void startDialogThread(); + inline void stopTimer(); QWindowsNativeDialogBase *m_nativeDialog; HWND m_ownerWindow; + int m_timerId; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 1286f5c106..fedda750d9 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -304,10 +304,10 @@ private: class DragCursorHandle { Q_DISABLE_COPY(DragCursorHandle) public: - DragCursorHandle(HCURSOR c, quint64 k) : cursor(c), cacheKey(k) {} + DragCursorHandle(HCURSOR c, qint64 k) : cursor(c), cacheKey(k) {} ~DragCursorHandle() { DestroyCursor(cursor); } HCURSOR cursor; - quint64 cacheKey; + qint64 cacheKey; }; typedef QMap <Qt::DropAction, QSharedPointer<DragCursorHandle> > ActionCursorMap; @@ -490,7 +490,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) qDebug("%s dwEffect=%lu, action=%d", __FUNCTION__, dwEffect, action); QSharedPointer<DragCursorHandle> cursorHandler = m_cursors.value(action); - quint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); + qint64 currentCacheKey = m_drag->currentDrag()->dragCursor(action).cacheKey(); if (cursorHandler.isNull() || currentCacheKey != cursorHandler->cacheKey) createCursors(); diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp index b4fb19e8e8..6fdb10288a 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.cpp @@ -1160,9 +1160,7 @@ QWindowsFontDatabase::~QWindowsFontDatabase() removeApplicationFonts(); } -QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, - QUnicodeTables::Script script, - void *handle) +QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QWindowsFontDatabase::createEngine(script, fontDef, 0, QWindowsContext::instance()->defaultDPI(), false, @@ -1215,7 +1213,7 @@ QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal request.styleStrategy = QFont::NoFontMerging | QFont::PreferMatch; request.hintingPreference = hintingPreference; - fontEngine = QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + fontEngine = QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), sharedFontData()); @@ -1529,8 +1527,8 @@ HFONT QWindowsFontDatabase::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static const char *other_tryFonts[] = { @@ -1715,7 +1713,7 @@ static QStringList extraTryFontsForFamily(const QString& family) return result; } -QStringList QWindowsFontDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { QStringList result = QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script); if (!result.isEmpty()) @@ -1885,8 +1883,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &requ // for scripts that do not require OpenType we should just look at the list of // supported writing systems in the font's OS/2 table. if (scriptRequiresOpenType(script)) { - HB_Face hbFace = few->harfbuzzFace(); - if (!hbFace || !hbFace->supported_scripts[script]) { + if (!few->supportsScript(QChar::Script(script))) { qWarning(" OpenType support missing for script\n"); delete few; return 0; @@ -1917,7 +1914,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(int script, const QFontDef &requ directWriteFont->Release(); #endif - if (script == QUnicodeTables::Common + if (script == QChar::Script_Common && !(request.styleStrategy & QFont::NoFontMerging)) { QStringList extraFonts = extraTryFontsForFamily(request.family); if (extraFonts.size()) { diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase.h b/src/plugins/platforms/windows/qwindowsfontdatabase.h index c14d6027c2..b9e6c38eaa 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase.h @@ -78,9 +78,9 @@ public: ~QWindowsFontDatabase(); virtual void populateFontDatabase(); - virtual QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + virtual QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); virtual QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - virtual QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); virtual void releaseHandle(void *handle); virtual QString fontDir() const; diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp index 406dc636b8..c2ddb912f1 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.cpp @@ -362,7 +362,7 @@ void QWindowsFontDatabaseFT::populate(const QString &family) ReleaseDC(0, dummy); } -QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle) +QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle) { QFontEngine *fe = QBasicFontDatabase::fontEngine(fontDef, script, handle); if (QWindowsContext::verboseFonts) @@ -430,9 +430,9 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; -QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const +QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { - if(script == QUnicodeTables::Common) { + if (script == QChar::Script_Common) { // && !(request.styleStrategy & QFont::NoFontMerging)) { QFontDatabase db; if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) { @@ -518,8 +518,8 @@ HFONT QWindowsFontDatabaseFT::systemFont() static inline bool scriptRequiresOpenType(int script) { - return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala) - || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko); + return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala) + || script == QChar::Script_Khmer || script == QChar::Script_Nko); } static inline int verticalDPI() diff --git a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h index 2ce429d012..d3cbc0210a 100644 --- a/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h +++ b/src/plugins/platforms/windows/qwindowsfontdatabase_ft.h @@ -52,10 +52,10 @@ class QWindowsFontDatabaseFT : public QBasicFontDatabase { public: void populateFontDatabase(); - QFontEngine *fontEngine(const QFontDef &fontDef, QUnicodeTables::Script script, void *handle); + QFontEngine *fontEngine(const QFontDef &fontDef, QChar::Script script, void *handle); QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); - QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; + QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; virtual QString fontDir() const; virtual QFont defaultFont() const; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 7fcd9814bd..c402f00453 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -66,7 +66,6 @@ #include <QtCore/QThreadStorage> #include <QtCore/private/qsystemlibrary_p.h> -#include <QtCore/private/qunicodetables_p.h> #include <QtCore/QDebug> #include <limits.h> @@ -155,9 +154,20 @@ static OUTLINETEXTMETRIC *getOutlineTextMetric(HDC hdc) return otm; } +bool QWindowsFontEngine::hasCFFTable() const +{ + HDC hdc = m_fontEngineData->hdc; + SelectObject(hdc, hfont); + return GetFontData(hdc, MAKE_TAG('C', 'F', 'F', ' '), 0, 0, 0) != GDI_ERROR; +} + void QWindowsFontEngine::getCMap() { ttf = (bool)(tm.tmPitchAndFamily & TMPF_TRUETYPE); + + // TMPF_TRUETYPE is not set for fonts with CFF tables + cffTable = !ttf && hasCFFTable(); + HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); bool symb = false; @@ -1041,7 +1051,7 @@ void QWindowsFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gly bool QWindowsFontEngine::getSfntTableData(uint tag, uchar *buffer, uint *length) const { - if (!ttf) + if (!ttf && !cffTable) return false; HDC hdc = m_fontEngineData->hdc; SelectObject(hdc, hfont); @@ -1216,13 +1226,13 @@ QImage QWindowsFontEngine::alphaRGBMapForGlyph(glyph_t glyph, QFixed, const QTra { HFONT font = hfont; - int contrast; + UINT contrast; SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); int margin = glyphMargin(QFontEngineGlyphCache::Raster_RGBMask); QWindowsNativeImage *mask = drawGDIGlyph(font, glyph, margin, t, QImage::Format_RGB32); - SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) quintptr(contrast), 0); if (mask == 0) return QImage(); @@ -1258,7 +1268,7 @@ QFontEngine *QWindowsFontEngine::cloneWithSize(qreal pixelSize) const request.styleStrategy |= QFont::NoFontMerging; QFontEngine *fontEngine = - QWindowsFontDatabase::createEngine(QUnicodeTables::Common, request, 0, + QWindowsFontDatabase::createEngine(QChar::Script_Common, request, 0, QWindowsContext::instance()->defaultDPI(), false, QStringList(), m_fontEngineData); diff --git a/src/plugins/platforms/windows/qwindowsfontengine.h b/src/plugins/platforms/windows/qwindowsfontengine.h index a23db2f235..2bf6ead503 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.h +++ b/src/plugins/platforms/windows/qwindowsfontengine.h @@ -145,6 +145,7 @@ public: private: QWindowsNativeImage *drawGDIGlyph(HFONT font, glyph_t, int margin, const QTransform &xform, QImage::Format mask_format); + bool hasCFFTable() const; const QSharedPointer<QWindowsFontEngineData> m_fontEngineData; @@ -155,6 +156,7 @@ private: uint stockFont : 1; uint ttf : 1; uint hasOutline : 1; + uint cffTable : 1; TEXTMETRIC tm; int lw; const unsigned char *cmap; diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 183acedfc8..5b6ce695d8 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -376,10 +376,12 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn if (SUCCEEDED(hr)) { for (int i=0; i<glyphs->numGlyphs; ++i) { glyphs->advances_x[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth); - if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) - glyphs->advances_x[i] = glyphs->advances_x[i].round(); glyphs->advances_y[i] = 0; } + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + for (int i = 0; i < glyphs->numGlyphs; ++i) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + } } else { qErrnoWarning("%s: GetDesignGlyphMetrics failed", __FUNCTION__); } diff --git a/src/plugins/platforms/windows/qwindowsglcontext.cpp b/src/plugins/platforms/windows/qwindowsglcontext.cpp index 081b42ce65..6f59b33e62 100644 --- a/src/plugins/platforms/windows/qwindowsglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsglcontext.cpp @@ -232,6 +232,7 @@ static QSurfaceFormat QWindowsOpenGLAdditionalFormat *additionalIn = 0) { QSurfaceFormat format; + format.setRenderableType(QSurfaceFormat::OpenGL); if (pfd.dwFlags & PFD_DOUBLEBUFFER) format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); format.setDepthBufferSize(pfd.cDepthBits); @@ -500,6 +501,7 @@ static QSurfaceFormat enum { attribSize =40 }; QSurfaceFormat result; + result.setRenderableType(QSurfaceFormat::OpenGL); if (!staticContext.hasExtensions()) return result; int iAttributes[attribSize]; @@ -875,6 +877,12 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex m_renderingContext(0), m_pixelFormat(0), m_extensionsUsed(false) { + QSurfaceFormat format = context->format(); + if (format.renderableType() == QSurfaceFormat::DefaultRenderableType) + format.setRenderableType(QSurfaceFormat::OpenGL); + if (format.renderableType() != QSurfaceFormat::OpenGL) + return; + // workaround for matrox driver: // make a cheap call to opengl to force loading of DLL static bool opengl32dll = false; @@ -912,7 +920,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex QWindowsOpenGLAdditionalFormat obtainedAdditional; if (tryExtensions) { m_pixelFormat = - ARB::choosePixelFormat(hdc, *m_staticContext, context->format(), + ARB::choosePixelFormat(hdc, *m_staticContext, format, requestedAdditional, &m_obtainedPixelFormatDescriptor); if (m_pixelFormat > 0) { m_obtainedFormat = @@ -922,7 +930,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex } } // tryExtensions if (!m_pixelFormat) { // Failed, try GDI - m_pixelFormat = GDI::choosePixelFormat(hdc, context->format(), requestedAdditional, + m_pixelFormat = GDI::choosePixelFormat(hdc, format, requestedAdditional, &m_obtainedPixelFormatDescriptor); if (m_pixelFormat) m_obtainedFormat = @@ -945,7 +953,7 @@ QWindowsGLContext::QWindowsGLContext(const QOpenGLStaticContextPtr &staticContex if (m_extensionsUsed) m_renderingContext = ARB::createContext(*m_staticContext, hdc, - context->format(), + format, requestedAdditional, sharingRenderingContext); if (!m_renderingContext) diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index da4519199f..30e0478e64 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -77,6 +77,7 @@ #include <QtCore/private/qeventdispatcher_win_p.h> #include <QtCore/QDebug> +#include <QtCore/QVariant> QT_BEGIN_NAMESPACE @@ -114,6 +115,11 @@ public: bool asyncExpose() const; void setAsyncExpose(bool value); + + QVariantMap windowProperties(QPlatformWindow *window) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name) const; + QVariant windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const; + void setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value); }; void *QWindowsNativeInterface::nativeResourceForWindow(const QByteArray &resource, QWindow *window) @@ -150,6 +156,37 @@ void *QWindowsNativeInterface::nativeResourceForBackingStore(const QByteArray &r return 0; } +static const char customMarginPropertyC[] = "WindowsCustomMargins"; + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name) const +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + return qVariantFromValue(platformWindow->customMargins()); + return QVariant(); +} + +QVariant QWindowsNativeInterface::windowProperty(QPlatformWindow *window, const QString &name, const QVariant &defaultValue) const +{ + const QVariant result = windowProperty(window, name); + return result.isValid() ? result : defaultValue; +} + +void QWindowsNativeInterface::setWindowProperty(QPlatformWindow *window, const QString &name, const QVariant &value) +{ + QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window); + if (name == QLatin1String(customMarginPropertyC)) + platformWindow->setCustomMargins(qvariant_cast<QMargins>(value)); +} + +QVariantMap QWindowsNativeInterface::windowProperties(QPlatformWindow *window) const +{ + QVariantMap result; + const QString customMarginProperty = QLatin1String(customMarginPropertyC); + result.insert(customMarginProperty, windowProperty(window, customMarginProperty)); + return result; +} + #ifndef QT_NO_OPENGL void *QWindowsNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) { @@ -372,6 +409,11 @@ QPlatformWindow *QWindowsIntegration::createPlatformWindow(QWindow *window) cons QWindowsWindow::WindowData requested; requested.flags = window->flags(); requested.geometry = window->geometry(); + // Apply custom margins (see QWindowsWindow::setCustomMargins())). + const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); + if (customMarginsV.isValid()) + requested.customMargins = qvariant_cast<QMargins>(customMarginsV); + const QWindowsWindow::WindowData obtained = QWindowsWindow::WindowData::create(window, requested, window->title()); if (QWindowsContext::verboseIntegration || QWindowsContext::verboseWindows) diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 56deb27442..924d604641 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -856,9 +856,15 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms if (isNumpad && (nModifiers & AltAny)) { code = winceKeyBend(msg.wParam); } else if (!isDeadKey) { - unsigned char kbdBuffer[256]; // Will hold the complete keyboard state - GetKeyboardState(kbdBuffer); - code = toKeyOrUnicode(msg.wParam, scancode, kbdBuffer); + // QTBUG-8764, QTBUG-10032 + // Can't call toKeyOrUnicode because that would call ToUnicode, and, if a dead key + // is pressed at the moment, Windows would NOT use it to compose a character for the next + // WM_CHAR event. + + // Instead, use MapVirtualKey, which will provide adequate values. + code = MapVirtualKey(msg.wParam, MAPVK_VK_TO_CHAR); + if (code < 0x20 || code == 0x7f) // The same logic as in toKeyOrUnicode() + code = winceKeyBend(msg.wParam); } // Invert state logic: @@ -1120,7 +1126,7 @@ QList<int> QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const } result << int(baseKey + keyMods); // The base key is _always_ valid, of course - for (int i = 1; i < NumMods; ++i) { + for (size_t i = 1; i < NumMods; ++i) { Qt::KeyboardModifiers neededMods = ModsTbl[i]; quint32 key = kbItem.qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) diff --git a/src/plugins/platforms/windows/qwindowsmime.cpp b/src/plugins/platforms/windows/qwindowsmime.cpp index 7fb32d3513..a8bacd631d 100644 --- a/src/plugins/platforms/windows/qwindowsmime.cpp +++ b/src/plugins/platforms/windows/qwindowsmime.cpp @@ -703,14 +703,14 @@ QWindowsMimeURI::QWindowsMimeURI() bool QWindowsMimeURI::canConvertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData) const { - if (getCf(formatetc) == CF_HDROP) { + if (mimeData->hasUrls() && getCf(formatetc) == CF_HDROP) { QList<QUrl> urls = mimeData->urls(); for (int i=0; i<urls.size(); i++) { if (!urls.at(i).toLocalFile().isEmpty()) return true; } } - return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasFormat(QStringLiteral("text/uri-list")); + return (getCf(formatetc) == CF_INETURL_W || getCf(formatetc) == CF_INETURL) && mimeData->hasUrls(); } bool QWindowsMimeURI::convertFromMime(const FORMATETC &formatetc, const QMimeData *mimeData, STGMEDIUM *pmedium) const diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index e1f4f4b143..dd16ea1c6f 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -49,6 +49,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QWindow> #include <QtCore/QDebug> #include <QtCore/QScopedArrayPointer> @@ -236,6 +237,9 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, platformWindow->setFlag(QWindowsWindow::AutoMouseCapture); if (QWindowsContext::verboseEvents) qDebug() << "Automatic mouse capture " << window; + // Implement "Click to focus" for native child windows. + if (!window->isTopLevel() && QGuiApplication::focusWindow() != window) + window->requestActivate(); } else if (platformWindow->hasMouseCapture() && platformWindow->testFlag(QWindowsWindow::AutoMouseCapture) && (msg.message == WM_LBUTTONUP || msg.message == WM_MBUTTONUP diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index bcae2e0816..250fea56b1 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -44,6 +44,7 @@ #include <QtCore/QUrl> #include <QtCore/QDebug> +#include <QtCore/QDir> #include <shlobj.h> #ifndef Q_OS_WINCE @@ -57,7 +58,8 @@ enum { debug = 0 }; static inline bool shellExecute(const QString &file) { #ifndef Q_OS_WINCE - const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)file.utf16(), 0, 0, SW_SHOWNORMAL); + const QString nativeFilePath = QDir::toNativeSeparators(file); + const quintptr result = (quintptr)ShellExecute(0, 0, (wchar_t*)nativeFilePath.utf16(), 0, 0, SW_SHOWNORMAL); // ShellExecute returns a value greater than 32 if successful if (result <= 32) { qWarning("ShellExecute '%s' failed (error %s).", qPrintable(file), qPrintable(QString::number(result))); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 7a68cdeb47..bb0092cd54 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -300,7 +300,7 @@ struct WindowCreationData tool(false), embedded(false), hasAlpha(false) {} void fromWindow(const QWindow *w, const Qt::WindowFlags flags, unsigned creationFlags = 0); - inline WindowData create(const QWindow *w, const QRect &geometry, QString title) const; + inline WindowData create(const QWindow *w, const WindowData &data, QString title) const; inline void applyWindowFlags(HWND hwnd) const; void initialize(HWND h, bool frameChange, qreal opacityLevel) const; @@ -452,7 +452,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag } QWindowsWindow::WindowData - WindowCreationData::create(const QWindow *w, const QRect &geometry, QString title) const + WindowCreationData::create(const QWindow *w, const WindowData &data, QString title) const { typedef QSharedPointer<QWindowCreationContext> QWindowCreationContextPtr; @@ -472,7 +472,7 @@ QWindowsWindow::WindowData const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w, isGL); - QRect rect = QPlatformWindow::initialGeometry(w, geometry, defaultWindowWidth, defaultWindowHeight); + QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight); if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); @@ -480,8 +480,9 @@ QWindowsWindow::WindowData const wchar_t *titleUtf16 = reinterpret_cast<const wchar_t *>(title.utf16()); const wchar_t *classNameUtf16 = reinterpret_cast<const wchar_t *>(windowClassName.utf16()); - // Capture events before CreateWindowEx() returns. - const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, style, exStyle)); + // Capture events before CreateWindowEx() returns. The context is cleared in + // the QWindowsWindow constructor. + const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle)); QWindowsContext::instance()->setWindowCreationContext(context); if (QWindowsContext::verboseWindows) @@ -490,14 +491,14 @@ QWindowsWindow::WindowData << " class=" <<windowClassName << " title=" << title << "\nrequested: " << rect << ": " << context->frameWidth << 'x' << context->frameHeight - << '+' << context->frameX << '+' << context->frameY; + << '+' << context->frameX << '+' << context->frameY + << " custom margins: " << context->customMargins; result.hwnd = CreateWindowEx(exStyle, classNameUtf16, titleUtf16, style, context->frameX, context->frameY, context->frameWidth, context->frameHeight, parentHandle, NULL, appinst, NULL); - QWindowsContext::instance()->setWindowCreationContext(QWindowCreationContextPtr()); if (QWindowsContext::verboseWindows) qDebug().nospace() << "CreateWindowEx: returns " << w << ' ' << result.hwnd << " obtained geometry: " @@ -511,6 +512,7 @@ QWindowsWindow::WindowData result.geometry = context->obtainedGeometry; result.frame = context->margins; result.embedded = embedded; + result.customMargins = context->customMargins; return result; } @@ -549,6 +551,8 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe qWarning() << "QWidget: Incompatible window flags: the window can't be on top and on bottom at the same time"; } else if (flags & Qt::WindowStaysOnBottomHint) { SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, swpFlags); + } else if (frameChange) { // Force WM_NCCALCSIZE with wParam=1 in case of custom margins. + SetWindowPos(hwnd, 0, 0, 0, 0, 0, swpFlags); } if (flags & (Qt::CustomizeWindowHint|Qt::WindowTitleHint)) { HMENU systemMenu = GetSystemMenu(hwnd, FALSE); @@ -577,9 +581,10 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe #define QWINDOWSIZE_MAX ((1<<24)-1) -QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w) : +QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : minimumSize(w->minimumSize()), - maximumSize(w->maximumSize()) + maximumSize(w->maximumSize()), + customMargins(cm) { } @@ -609,6 +614,33 @@ QMargins QWindowsGeometryHint::frame(DWORD style, DWORD exStyle) return result; } +bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result) +{ +#ifndef Q_OS_WINCE + // NCCALCSIZE_PARAMS structure if wParam==TRUE + if (!msg.wParam || customMargins.isNull()) + return false; + *result = DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + NCCALCSIZE_PARAMS *ncp = reinterpret_cast<NCCALCSIZE_PARAMS *>(msg.lParam); + const RECT oldClientArea = ncp->rgrc[0]; + ncp->rgrc[0].left += customMargins.left(); + ncp->rgrc[0].top += customMargins.top(); + ncp->rgrc[0].right -= customMargins.right(); + ncp->rgrc[0].bottom -= customMargins.bottom(); + result = 0; + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldClientArea << '+' << customMargins << "-->" + << ncp->rgrc[0] << ' ' << ncp->rgrc[1] << ' ' << ncp->rgrc[2] + << ' ' << ncp->lppos->cx << ',' << ncp->lppos->cy; + return true; +#else + Q_UNUSED(customMargins) + Q_UNUSED(msg) + Q_UNUSED(result) + return false; +#endif +} + #ifndef Q_OS_WINCE void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { @@ -625,8 +657,8 @@ void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXI << " in " << *mmi; const QMargins margins = QWindowsGeometryHint::frame(style, exStyle); - const int frameWidth = margins.left() + margins.right(); - const int frameHeight = margins.top() + margins.bottom(); + const int frameWidth = margins.left() + margins.right() + customMargins.left() + customMargins.right(); + const int frameHeight = margins.top() + margins.bottom() + customMargins.top() + customMargins.bottom(); if (minimumSize.width() > 0) mmi->ptMinTrackSize.x = minimumSize.width() + frameWidth; if (minimumSize.height() > 0) @@ -675,10 +707,11 @@ bool QWindowsGeometryHint::positionIncludesFrame(const QWindow *w) QWindowCreationContext::QWindowCreationContext(const QWindow *w, const QRect &geometry, + const QMargins &cm, DWORD style_, DWORD exStyle_) : - geometryHint(w), style(style_), exStyle(exStyle_), + geometryHint(w, cm), style(style_), exStyle(exStyle_), requestedGeometry(geometry), obtainedGeometry(geometry), - margins(QWindowsGeometryHint::frame(style, exStyle)), + margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm), frameX(CW_USEDEFAULT), frameY(CW_USEDEFAULT), frameWidth(CW_USEDEFAULT), frameHeight(CW_USEDEFAULT) { @@ -689,14 +722,16 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, if (geometry.isValid()) { frameX = geometry.x(); frameY = geometry.y(); - frameWidth = margins.left() + geometry.width() + margins.right(); - frameHeight = margins.top() + geometry.height() + margins.bottom(); + const QMargins effectiveMargins = margins + customMargins; + frameWidth = effectiveMargins.left() + geometry.width() + effectiveMargins.right(); + frameHeight = effectiveMargins.top() + geometry.height() + effectiveMargins.bottom(); const bool isDefaultPosition = !frameX && !frameY && w->isTopLevel(); if (!QWindowsGeometryHint::positionIncludesFrame(w) && !isDefaultPosition) { - frameX -= margins.left(); - frameY -= margins.top(); + frameX -= effectiveMargins.left(); + frameY -= effectiveMargins.top(); } } + if (QWindowsContext::verboseWindows) qDebug().nospace() << __FUNCTION__ << ' ' << w << geometry @@ -704,7 +739,8 @@ QWindowCreationContext::QWindowCreationContext(const QWindow *w, << " frame: " << frameWidth << 'x' << frameHeight << '+' << frameX << '+' << frameY << " min" << geometryHint.minimumSize - << " max" << geometryHint.maximumSize; + << " max" << geometryHint.maximumSize + << " custom margins " << customMargins; } /*! @@ -736,9 +772,6 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : m_hdc(0), m_windowState(Qt::WindowNoState), m_opacity(1.0), -#ifndef QT_NO_CURSOR - m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()), -#endif m_dropTarget(0), m_savedStyle(0), m_format(aWindow->format()), @@ -753,6 +786,8 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) : { if (aWindow->surfaceType() == QWindow::OpenGLSurface) setFlag(OpenGLSurface); + // Clear the creation context as the window can be found in QWindowsContext's map. + QWindowsContext::instance()->setWindowCreationContext(QSharedPointer<QWindowCreationContext>()); QWindowsContext::instance()->addWindow(m_data.hwnd, this); if (aWindow->isTopLevel()) { switch (aWindow->type()) { @@ -879,8 +914,9 @@ QWindowsWindow::WindowData { WindowCreationData creationData; creationData.fromWindow(w, parameters.flags); - WindowData result = creationData.create(w, parameters.geometry, title); - creationData.initialize(result.hwnd, false, 1); + WindowData result = creationData.create(w, parameters, title); + // Force WM_NCCALCSIZE (with wParam=1) via SWP_FRAMECHANGED for custom margin. + creationData.initialize(result.hwnd, !parameters.customMargins.isNull(), 1); return result; } @@ -1067,7 +1103,7 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) const QSize newSize = rect.size(); // Check on hint. if (newSize != oldSize) { - const QWindowsGeometryHint hint(window()); + const QWindowsGeometryHint hint(window(), m_data.customMargins); if (!hint.validSize(newSize)) { qWarning("%s: Attempt to set a size (%dx%d) violating the constraints" "(%dx%d - %dx%d) on window '%s'.", __FUNCTION__, @@ -1085,14 +1121,19 @@ void QWindowsWindow::setGeometry(const QRect &rectIn) if (m_data.geometry != rect) { qWarning("%s: Unable to set geometry %dx%d+%d+%d on '%s'." " Resulting geometry: %dx%d+%d+%d " - "(frame: %d, %d, %d, %d).", + "(frame: %d, %d, %d, %d, custom margin: %d, %d, %d, %d" + ", minimum size: %dx%d, maximum size: %dx%d).", __FUNCTION__, rect.width(), rect.height(), rect.x(), rect.y(), qPrintable(window()->objectName()), m_data.geometry.width(), m_data.geometry.height(), m_data.geometry.x(), m_data.geometry.y(), m_data.frame.left(), m_data.frame.top(), - m_data.frame.right(), m_data.frame.bottom()); + m_data.frame.right(), m_data.frame.bottom(), + m_data.customMargins.left(), m_data.customMargins.top(), + m_data.customMargins.right(), m_data.customMargins.bottom(), + window()->minimumWidth(), window()->minimumHeight(), + window()->maximumWidth(), window()->maximumHeight()); } } else { QPlatformWindow::setGeometry(rect); @@ -1170,7 +1211,7 @@ QRect QWindowsWindow::frameGeometry_sys() const QRect QWindowsWindow::geometry_sys() const { - return frameGeometry_sys() - frameMargins(); + return frameGeometry_sys().marginsRemoved(frameMargins()); } /*! @@ -1497,7 +1538,7 @@ QMargins QWindowsWindow::frameMargins() const m_data.frame = QWindowsGeometryHint::frame(style(), exStyle()); clearFlag(FrameDirty); } - return m_data.frame; + return m_data.frame + m_data.customMargins; } void QWindowsWindow::setOpacity(qreal level) @@ -1655,7 +1696,7 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled) #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const { - const QWindowsGeometryHint hint(window()); + const QWindowsGeometryHint hint(window(), m_data.customMargins); hint.applyToMinMaxInfo(m_data.hwnd, mmi); if (QWindowsContext::verboseWindows) qDebug() << __FUNCTION__ << window() << *mmi; @@ -1671,20 +1712,42 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const void QWindowsWindow::applyCursor() { #ifndef QT_NO_CURSOR - SetCursor(m_cursor.handle()); + if (m_cursor.isNull()) { // Recurse up to parent with non-null cursor. + if (const QWindow *p = window()->parent()) + QWindowsWindow::baseWindowOf(p)->applyCursor(); + } else { + SetCursor(m_cursor.handle()); + } #endif } +// Check whether to apply a new cursor. Either the window in question is +// currently under mouse, or it is the parent of the window under mouse and +// there is no other window with an explicitly set cursor in-between. +static inline bool applyNewCursor(const QWindow *w) +{ + const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse(); + if (underMouse == w) + return true; + for (const QWindow *p = underMouse; p ; p = p->parent()) { + if (p == w) + return true; + if (!QWindowsWindow::baseWindowOf(p)->cursor().isNull()) + return false; + } + return false; +} + void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) { #ifndef QT_NO_CURSOR if (c.handle() != m_cursor.handle()) { - const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window(); + const bool apply = applyNewCursor(window()); if (QWindowsContext::verboseWindows) qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape() - << " isWUM=" << underMouse; + << " doApply=" << apply; m_cursor = c; - if (underMouse) + if (apply) applyCursor(); } #endif @@ -1857,4 +1920,32 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) } } +/*! + \brief Sets custom margins to be added to the default margins determined by + the windows style in the handling of the WM_NCCALCSIZE message. + + This is currently used to give the Aero-style QWizard a smaller top margin. + The property can be set using QPlatformNativeInterface::setWindowProperty() or, + before platform window creation, by setting a dynamic property + on the QWindow (see QWindowsIntegration::createPlatformWindow()). +*/ + +void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) +{ + if (newCustomMargins != m_data.customMargins) { + const QMargins oldCustomMargins = m_data.customMargins; + m_data.customMargins = newCustomMargins; + // Re-trigger WM_NCALCSIZE with wParam=1 by passing SWP_FRAMECHANGED + const QRect currentFrameGeometry = frameGeometry_sys(); + const QPoint topLeft = currentFrameGeometry.topLeft(); + QRect newFrame = currentFrameGeometry.marginsRemoved(oldCustomMargins) + m_data.customMargins; + newFrame.moveTo(topLeft); + setFlag(FrameDirty); + if (QWindowsContext::verboseWindows) + qDebug() << __FUNCTION__ << oldCustomMargins << "->" << newCustomMargins + << currentFrameGeometry << "->" << newFrame; + SetWindowPos(m_data.hwnd, 0, newFrame.x(), newFrame.y(), newFrame.width(), newFrame.height(), SWP_NOZORDER | SWP_FRAMECHANGED); + } +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index f3b480b0af..87397f1c1d 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -67,8 +67,9 @@ class QWindowsEGLStaticContext; struct QWindowsGeometryHint { QWindowsGeometryHint() {} - explicit QWindowsGeometryHint(const QWindow *w); + explicit QWindowsGeometryHint(const QWindow *w, const QMargins &customMargins); static QMargins frame(DWORD style, DWORD exStyle); + static bool handleCalculateSize(const QMargins &customMargins, const MSG &msg, LRESULT *result); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const; void applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const; @@ -84,11 +85,13 @@ struct QWindowsGeometryHint QSize minimumSize; QSize maximumSize; + QMargins customMargins; }; struct QWindowCreationContext { QWindowCreationContext(const QWindow *w, const QRect &r, + const QMargins &customMargins, DWORD style, DWORD exStyle); #ifndef Q_OS_WINCE //MinMax maybe define struct if not available void applyToMinMaxInfo(MINMAXINFO *mmi) const @@ -101,6 +104,7 @@ struct QWindowCreationContext QRect requestedGeometry; QRect obtainedGeometry; QMargins margins; + QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE int frameX; // Passed on to CreateWindowEx(), including frame. int frameY; int frameWidth; @@ -137,6 +141,7 @@ public: Qt::WindowFlags flags; QRect geometry; QMargins frame; // Do not use directly for windows, see FrameDirty. + QMargins customMargins; // User-defined, additional frame for NCCALCSIZE HWND hwnd; bool embedded; @@ -191,6 +196,9 @@ public: void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } + QMargins customMargins() const { return m_data.customMargins; } + void setCustomMargins(const QMargins &m); + #ifdef QT_OPENGL_ES_2 EGLSurface eglSurfaceHandle() const { return m_eglSurface;} EGLSurface ensureEglSurfaceHandle(const QWindowsEGLStaticContextPtr &staticContext, EGLConfig config); @@ -290,17 +298,6 @@ private: HICON m_iconBig; }; -// Conveniences for window frames. -inline QRect operator+(const QRect &r, const QMargins &m) -{ - return r.adjusted(-m.left(), -m.top(), m.right(), m.bottom()); -} - -inline QRect operator-(const QRect &r, const QMargins &m) -{ - return r.adjusted(m.left(), m.top(), -m.right(), -m.bottom()); -} - // Debug QDebug operator<<(QDebug d, const RECT &r); #ifndef Q_OS_WINCE // maybe available on some SDKs revisit WM_GETMINMAXINFO/WM_NCCALCSIZE @@ -371,4 +368,6 @@ inline void QWindowsWindow::destroyIcon() QT_END_NAMESPACE +Q_DECLARE_METATYPE(QMargins) + #endif // QWINDOWSWINDOW_H |