diff options
author | Liang Qi <liang.qi@qt.io> | 2016-06-06 08:34:03 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2016-06-06 09:04:55 +0200 |
commit | 57057f76add416d0faf2e09a90c126baafb6198e (patch) | |
tree | 07d54f8e5daeb3ed1161723542e94c324d745166 /src/plugins | |
parent | dc0ae02ebc8e221f952829230c0301a718a6f10b (diff) | |
parent | fd70978693bd92761e9989bc1c76bf83c1e8c987 (diff) |
Merge remote-tracking branch 'origin/5.6' into 5.7
Conflicts:
.qmake.conf
config.tests/unix/nis/nis.cpp
mkspecs/unsupported/freebsd-g++/qplatformdefs.h
src/corelib/tools/qdatetime.cpp
src/corelib/tools/qsimd.cpp
src/corelib/tools/qsimd_p.h
src/network/access/access.pri
src/network/access/qnetworkreplynsurlconnectionimpl.mm
src/network/access/qnetworkreplynsurlconnectionimpl_p.h
src/plugins/platforms/cocoa/qnsview.mm
src/plugins/printsupport/windows/qwindowsprintdevice.cpp
tests/auto/corelib/kernel/qobject/tst_qobject.cpp
tests/auto/network/access/qnetworkreply/BLACKLIST
tests/auto/widgets/widgets/qopenglwidget/BLACKLIST
Change-Id: I4b32055bbf922392ef0264fd403405416fffee57
Diffstat (limited to 'src/plugins')
22 files changed, 1255 insertions, 130 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index eaa0170748..91ce91004f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -153,7 +153,7 @@ static bool isMouseEvent(NSEvent *ev) if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO)) { - QNSView *contentView = (QNSView *)pw->contentView(); + QNSView *contentView = pw->m_qtView; [contentView handleFrameStrutMouseEvent: theEvent]; } } @@ -1188,7 +1188,11 @@ NSView *QCocoaWindow::contentView() const void QCocoaWindow::setContentView(NSView *contentView) { // Remove and release the previous content view - [m_contentView removeFromSuperview]; + if (m_nsWindow) + [m_nsWindow setContentView:nil]; + else + [m_contentView removeFromSuperview]; + [m_contentView release]; // Insert and retain the new content view diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 5972cf9504..3469166fdc 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -663,7 +663,9 @@ QT_WARNING_POP - (BOOL)becomeFirstResponder { - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if (!m_platformWindow->windowIsPopupType() && !m_isMenuView) QWindowSystemInterface::handleWindowActivated([self topLevelWindow]); @@ -672,11 +674,13 @@ QT_WARNING_POP - (BOOL)acceptsFirstResponder { + if (!m_window || !m_platformWindow) + return NO; if (m_isMenuView) return NO; if (m_platformWindow->shouldRefuseKeyWindowAndFirstResponder()) return NO; - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; if ((m_window->flags() & Qt::ToolTip) == Qt::ToolTip) return NO; @@ -686,7 +690,9 @@ QT_WARNING_POP - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { Q_UNUSED(theEvent) - if (m_window && (m_window->flags() & Qt::WindowTransparentForInput) ) + if (!m_window || !m_platformWindow) + return NO; + if (m_window->flags() & Qt::WindowTransparentForInput) return NO; return YES; } @@ -2199,7 +2205,11 @@ static QPoint mapWindowCoordinates(QWindow *source, QWindow *target, QPoint poin // keep our state, and QGuiApplication state (buttons member) in-sync, // or future mouse events will be processed incorrectly - m_buttons &= ~(m_sendUpAsRightButton ? Qt::RightButton : Qt::LeftButton); + NSUInteger pmb = [NSEvent pressedMouseButtons]; + for (int buttonNumber = 0; buttonNumber < 32; buttonNumber++) { // see cocoaButton2QtButton() for the 32 value + if (!(pmb & (1 << buttonNumber))) + m_buttons &= ~cocoaButton2QtButton(buttonNumber); + } NSPoint windowPoint = [self convertPoint: point fromView: nil]; QPoint qtWindowPoint(windowPoint.x, windowPoint.y); diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp index abff88b4bd..5b779d6732 100644 --- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmcursor.cpp @@ -132,7 +132,7 @@ void QEglFSKmsGbmCursor::updateMouseStatus() m_state = visible ? CursorPendingVisible : CursorPendingHidden; #ifndef QT_NO_CURSOR - changeCursor(nullptr, m_screen->topLevelAt(pos())); + changeCursor(Q_NULLPTR, m_screen->topLevelAt(pos())); #endif } diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 82bcf48855..2b0643f26e 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -42,6 +42,7 @@ #include <UIKit/UIKit.h> +#include <QtCore/qlocale.h> #include <QtGui/qevent.h> #include <QtGui/qtransform.h> #include <qpa/qplatforminputcontext.h> @@ -52,6 +53,7 @@ const char kImePlatformDataReturnKeyType[] = "returnKeyType"; QT_BEGIN_NAMESPACE +@class QIOSLocaleListener; @class QIOSKeyboardListener; @class QIOSTextInputResponder; @protocol KeyboardState; @@ -98,6 +100,8 @@ public: void reset() Q_DECL_OVERRIDE; void commit() Q_DECL_OVERRIDE; + QLocale locale() const Q_DECL_OVERRIDE; + void clearCurrentFocusObject(); void setFocusObject(QObject *object) Q_DECL_OVERRIDE; @@ -118,6 +122,7 @@ public: private: UIView* scrollableRootView(); + QIOSLocaleListener *m_localeListener; QIOSKeyboardListener *m_keyboardHideGesture; QIOSTextInputResponder *m_textResponder; KeyboardState m_keyboardState; diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index d553d16698..ef93d68cf0 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -62,6 +62,39 @@ static QUIView *focusView() // ------------------------------------------------------------------------- +@interface QIOSLocaleListener : NSObject +@end + +@implementation QIOSLocaleListener + +- (id)init +{ + if (self = [super init]) { + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + [notificationCenter addObserver:self + selector:@selector(localeDidChange:) + name:NSCurrentLocaleDidChangeNotification object:nil]; + } + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super dealloc]; +} + +- (void)localeDidChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + QIOSInputContext::instance()->emitLocaleChanged(); +} + +@end + +// ------------------------------------------------------------------------- + @interface QIOSKeyboardListener : UIGestureRecognizer <UIGestureRecognizerDelegate> { @private QIOSInputContext *m_context; @@ -291,6 +324,7 @@ QIOSInputContext *QIOSInputContext::instance() QIOSInputContext::QIOSInputContext() : QPlatformInputContext() + , m_localeListener([QIOSLocaleListener new]) , m_keyboardHideGesture([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_textResponder(0) { @@ -304,6 +338,7 @@ QIOSInputContext::QIOSInputContext() QIOSInputContext::~QIOSInputContext() { + [m_localeListener release]; [m_keyboardHideGesture.view removeGestureRecognizer:m_keyboardHideGesture]; [m_keyboardHideGesture release]; @@ -663,3 +698,8 @@ void QIOSInputContext::commit() [m_textResponder unmarkText]; [m_textResponder notifyInputDelegate:Qt::ImSurroundingText]; } + +QLocale QIOSInputContext::locale() const +{ + return QLocale(QString::fromNSString([[NSLocale currentLocale] objectForKey:NSLocaleIdentifier])); +} diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index eb12739e98..37d5557794 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -359,6 +359,7 @@ - (void)sendKeyPressRelease:(Qt::Key)key modifiers:(Qt::KeyboardModifiers)modifiers { + QScopedValueRollback<BOOL> rollback(m_inSendEventToFocusObject, true); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyPress, key, modifiers); QWindowSystemInterface::handleKeyEvent(qApp->focusWindow(), QEvent::KeyRelease, key, modifiers); QWindowSystemInterface::flushWindowSystemEvents(); diff --git a/src/plugins/platforms/minimal/minimal.pro b/src/plugins/platforms/minimal/minimal.pro index 0d31d6605b..bd6f2d8e6f 100644 --- a/src/plugins/platforms/minimal/minimal.pro +++ b/src/plugins/platforms/minimal/minimal.pro @@ -11,6 +11,7 @@ HEADERS = qminimalintegration.h \ OTHER_FILES += minimal.json CONFIG += qpa/genericunixfontdatabase +darwin: DEFINES += QT_NO_FONTCONFIG PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QMinimalIntegrationPlugin diff --git a/src/plugins/platforms/platforms.pro b/src/plugins/platforms/platforms.pro index cb3764f5df..a5b64636af 100644 --- a/src/plugins/platforms/platforms.pro +++ b/src/plugins/platforms/platforms.pro @@ -2,9 +2,9 @@ TEMPLATE = subdirs android: SUBDIRS += android -SUBDIRS += minimal +!android: SUBDIRS += minimal -!win32|contains(QT_CONFIG, freetype):SUBDIRS += offscreen +!android:if(!win32|contains(QT_CONFIG, freetype)): SUBDIRS += offscreen contains(QT_CONFIG, xcb) { SUBDIRS += xcb diff --git a/src/plugins/platforms/windows/qwindowseglcontext.cpp b/src/plugins/platforms/windows/qwindowseglcontext.cpp index a3f9f0b44b..0ce4e87e52 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.cpp +++ b/src/plugins/platforms/windows/qwindowseglcontext.cpp @@ -205,26 +205,9 @@ QWindowsEGLStaticContext::QWindowsEGLStaticContext(EGLDisplay display) { } -QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +bool QWindowsEGLStaticContext::initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor) { - const HDC dc = QWindowsContext::instance()->displayContext(); - if (!dc){ - qWarning("%s: No Display", __FUNCTION__); - return 0; - } - - if (!libEGL.init()) { - qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); - return 0; - } - if (!libGLESv2.init()) { - qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); - return 0; - } - - EGLDisplay display = EGL_NO_DISPLAY; - EGLint major = 0; - EGLint minor = 0; #ifdef EGL_ANGLE_platform_angle if (libEGL.eglGetPlatformDisplayEXT && (preferredType & QWindowsOpenGLTester::AngleBackendMask)) { @@ -242,16 +225,52 @@ QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester: else if (preferredType & QWindowsOpenGLTester::AngleRendererD3d11Warp) attributes = anglePlatformAttributes[2]; if (attributes) { - display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); - if (!libEGL.eglInitialize(display, &major, &minor)) { - display = EGL_NO_DISPLAY; - major = minor = 0; + *display = libEGL.eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, dc, attributes); + if (!libEGL.eglInitialize(*display, major, minor)) { + libEGL.eglTerminate(*display); + *display = EGL_NO_DISPLAY; + *major = *minor = 0; + return false; } } } #else // EGL_ANGLE_platform_angle - Q_UNUSED(preferredType) + Q_UNUSED(preferredType); + Q_UNUSED(dc); + Q_UNUSED(display); + Q_UNUSED(major); + Q_UNUSED(minor); #endif + return true; +} + +QWindowsEGLStaticContext *QWindowsEGLStaticContext::create(QWindowsOpenGLTester::Renderers preferredType) +{ + const HDC dc = QWindowsContext::instance()->displayContext(); + if (!dc){ + qWarning("%s: No Display", __FUNCTION__); + return 0; + } + + if (!libEGL.init()) { + qWarning("%s: Failed to load and resolve libEGL functions", __FUNCTION__); + return 0; + } + if (!libGLESv2.init()) { + qWarning("%s: Failed to load and resolve libGLESv2 functions", __FUNCTION__); + return 0; + } + + EGLDisplay display = EGL_NO_DISPLAY; + EGLint major = 0; + EGLint minor = 0; + + if (!initializeAngle(preferredType, dc, &display, &major, &minor) + && (preferredType & QWindowsOpenGLTester::AngleRendererD3d11)) { + preferredType &= ~QWindowsOpenGLTester::AngleRendererD3d11; + initializeAngle(preferredType, dc, &display, &major, &minor); + } + if (display == EGL_NO_DISPLAY) display = libEGL.eglGetDisplay(dc); if (!display) { diff --git a/src/plugins/platforms/windows/qwindowseglcontext.h b/src/plugins/platforms/windows/qwindowseglcontext.h index c7f7cee3c2..48a19f81e5 100644 --- a/src/plugins/platforms/windows/qwindowseglcontext.h +++ b/src/plugins/platforms/windows/qwindowseglcontext.h @@ -131,6 +131,8 @@ public: private: explicit QWindowsEGLStaticContext(EGLDisplay display); + static bool initializeAngle(QWindowsOpenGLTester::Renderers preferredType, HDC dc, + EGLDisplay *display, EGLint *major, EGLint *minor); const EGLDisplay m_display; }; diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index 806af6458b..4f3df32f16 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1346,8 +1346,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, fontEngine->fontDef.pixelSize, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fedw->fontDef.weight = fontEngine->fontDef.weight; + fedw->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fedw->fontDef.style = fontEngine->fontDef.style; fedw->fontDef.family = fam; @@ -1364,8 +1363,7 @@ QFontEngine *QWindowsMultiFontEngine::loadEngine(int at) // reason QFontEngine *fe = new QWindowsFontEngine(fam, lf, data); - if (fontEngine->fontDef.weight > QFont::Normal) - fe->fontDef.weight = fontEngine->fontDef.weight; + fe->fontDef.weight = fontEngine->fontDef.weight; if (fontEngine->fontDef.style > QFont::StyleNormal) fe->fontDef.style = fontEngine->fontDef.style; fe->fontDef.family = fam; diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 96940f3f4c..4bf424f5f6 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -138,7 +138,16 @@ public: explicit ShGetFileInfoFunction(const wchar_t *fn, DWORD a, SHFILEINFO *i, UINT f, bool *r) : m_fileName(fn), m_attributes(a), m_flags(f), m_info(i), m_result(r) {} - void operator()() const { *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); } + void operator()() const + { +#ifndef Q_OS_WINCE + const UINT oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); +#endif + *m_result = SHGetFileInfo(m_fileName, m_attributes, m_info, sizeof(SHFILEINFO), m_flags); +#ifndef Q_OS_WINCE + SetErrorMode(oldErrorMode); +#endif + } private: const wchar_t *m_fileName; diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 31c9984559..76a36851ce 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1677,11 +1677,12 @@ bool QWindowsWindow::handleWmPaint(HWND hwnd, UINT message, return false; PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + // Observed painting problems with Aero style disabled (QTBUG-7865). + // 5.8: Consider making it dependent on !DwmIsCompositionEnabled(). if (testFlag(OpenGLSurface) && testFlag(OpenGLDoubleBuffered)) - InvalidateRect(hwnd, 0, false); - - BeginPaint(hwnd, &ps); + SelectClipRgn(ps.hdc, NULL); // If the a window is obscured by another window (such as a child window) // we still need to send isExposed=true, for compatibility. diff --git a/src/plugins/platforms/winrt/qwinrtdrag.cpp b/src/plugins/platforms/winrt/qwinrtdrag.cpp new file mode 100644 index 0000000000..2ef50aa4e2 --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.cpp @@ -0,0 +1,893 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwinrtdrag.h" + +#include <QtCore/qglobal.h> +#include <QtCore/QMimeData> +#include <QtCore/QStringList> +#include <QtGui/QGuiApplication> +#include <qpa/qwindowsysteminterface.h> + +#include <qfunctions_winrt.h> +#include <private/qeventdispatcher_winrt_p.h> + +#include <Windows.ApplicationModel.datatransfer.h> +#include <windows.ui.xaml.h> +#include <windows.foundation.collections.h> +#include <windows.graphics.imaging.h> +#include <windows.storage.streams.h> +#include <functional> +#include <robuffer.h> + +using namespace ABI::Windows::ApplicationModel::DataTransfer; +using namespace ABI::Windows::ApplicationModel::DataTransfer::DragDrop; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Graphics::Imaging; +using namespace ABI::Windows::Storage; +using namespace ABI::Windows::Storage::Streams; +using namespace ABI::Windows::UI::Xaml; +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") + +ComPtr<IBuffer> createIBufferFromData(const char *data, qint32 size) +{ + static ComPtr<IBufferFactory> bufferFactory; + HRESULT hr; + if (!bufferFactory) { + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr<IBuffer> buffer; + const UINT32 length = size; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(length); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = buffer.As(&byteArrayAccess); + Q_ASSERT_SUCCEEDED(hr); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, data, length); + return buffer; +} + +class DragThreadTransferData : public QObject +{ + Q_OBJECT +public slots: + void handleDrag(); +public: + explicit DragThreadTransferData(QObject *parent = Q_NULLPTR); + QWindow *window; + QWinRTInternalMimeData *mime; + QPoint point; + Qt::DropActions actions; + bool dropAction; + ComPtr<IDragEventArgs> nativeArgs; + ComPtr<IDragOperationDeferral> deferral; +}; + +inline QString hStringToQString(const HString &hString) +{ + quint32 l; + const wchar_t *raw = hString.GetRawBuffer(&l); + return (QString::fromWCharArray(raw, l)); +} + +inline HString qStringToHString(const QString &qString) +{ + HString h; + h.Set(reinterpret_cast<const wchar_t*>(qString.utf16()), qString.size()); + return h; +} + +namespace NativeFormatStrings { + static ComPtr<IStandardDataFormatsStatics> dataStatics; + static HSTRING text; // text/plain + static HSTRING html; // text/html + static HSTRING storage; // text/uri-list +} + +static inline DataPackageOperation translateFromQDragDropActions(const Qt::DropAction action) +{ + switch (action) { + case Qt::CopyAction: + return DataPackageOperation_Copy; + case Qt::MoveAction: + return DataPackageOperation_Move; + case Qt::LinkAction: + return DataPackageOperation_Link; + case Qt::IgnoreAction: + default: + return DataPackageOperation_None; + } +} + +static inline Qt::DropActions translateToQDragDropActions(const DataPackageOperation op) +{ + Qt::DropActions actions = Qt::IgnoreAction; + // None needs to be interpreted as the sender being able to handle + // anything and let the receiver decide + if (op == DataPackageOperation_None) + actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction; + if (op & DataPackageOperation_Link) + actions |= Qt::LinkAction; + if (op & DataPackageOperation_Copy) + actions |= Qt::CopyAction; + if (op & DataPackageOperation_Move) + actions |= Qt::MoveAction; + return actions; +} + +QWinRTInternalMimeData::QWinRTInternalMimeData() + : QInternalMimeData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + if (!NativeFormatStrings::dataStatics) { + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_StandardDataFormats).Get(), + IID_PPV_ARGS(&NativeFormatStrings::dataStatics)); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Text(&NativeFormatStrings::text); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_Html(&NativeFormatStrings::html); + Q_ASSERT_SUCCEEDED(hr); + hr = NativeFormatStrings::dataStatics->get_StorageItems(&NativeFormatStrings::storage); + Q_ASSERT_SUCCEEDED(hr); + } +} + +QWinRTInternalMimeData::~QWinRTInternalMimeData() +{ +} + +bool QWinRTInternalMimeData::hasFormat_sys(const QString &mimetype) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype; + + if (!dataView) + return false; + + return formats_sys().contains(mimetype); +} + +QStringList QWinRTInternalMimeData::formats_sys() const +{ + qCDebug(lcQpaMime) << __FUNCTION__; + + if (!dataView) + return QStringList(); + + if (!formats.isEmpty()) + return formats; + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([this]() { + boolean contains; + HRESULT hr; + hr = dataView->Contains(NativeFormatStrings::text, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/plain")); + + hr = dataView->Contains(NativeFormatStrings::html, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/html")); + + hr = dataView->Contains(NativeFormatStrings::storage, &contains); + if (SUCCEEDED(hr) && contains) + formats.append(QLatin1String("text/uri-list")); + + // We need to add any additional format as well, for legacy windows + // reasons, but also in case someone adds custom formats. + ComPtr<IVectorView<HSTRING>> availableFormats; + hr = dataView->get_AvailableFormats(&availableFormats); + RETURN_OK_IF_FAILED("Could not query available formats."); + + quint32 size; + hr = availableFormats->get_Size(&size); + RETURN_OK_IF_FAILED("Could not query format vector size."); + for (quint32 i = 0; i < size; ++i) { + HString str; + hr = availableFormats->GetAt(i, str.GetAddressOf()); + if (FAILED(hr)) + continue; + formats.append(hStringToQString(str)); + } + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + + return formats; +} + +QVariant QWinRTInternalMimeData::retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const +{ + qCDebug(lcQpaMime) << __FUNCTION__ << mimetype << preferredType; + + if (!dataView || !formats.contains(mimetype)) + return QVariant(); + + QVariant result; + HRESULT hr; + if (mimetype == QLatin1String("text/plain")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetTextAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/uri-list")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<IVectorView<IStorageItem*>*>> op; + hr = dataView->GetStorageItemsAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IVectorView<IStorageItem*>> nativeItems; + hr = QWinRTFunctions::await(op, nativeItems.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + QList<QVariant> items; + quint32 count; + hr = nativeItems->get_Size(&count); + for (quint32 i = 0; i < count; ++i) { + ComPtr<IStorageItem> item; + hr = nativeItems->GetAt(i, &item); + Q_ASSERT_SUCCEEDED(hr); + HString path; + hr = item->get_Path(path.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + items.append(QUrl::fromLocalFile(hStringToQString(path))); + } + result.setValue(items); + return S_OK; + }); + } else if (mimetype == QLatin1String("text/html")) { + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result]() { + HRESULT hr; + ComPtr<IAsyncOperation<HSTRING>> op; + HString res; + hr = dataView->GetHtmlFormatAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(res)); + return S_OK; + }); + } else { + // Asking for custom data + hr = QEventDispatcherWinRT::runOnXamlThread([this, &result, mimetype]() { + HRESULT hr; + ComPtr<IAsyncOperation<IInspectable*>> op; + ComPtr<IInspectable> res; + HString type; + type.Set(reinterpret_cast<const wchar_t*>(mimetype.utf16()), mimetype.size()); + hr = dataView->GetDataAsync(type.Get(), &op); + RETURN_OK_IF_FAILED("Could not query custom drag data."); + hr = QWinRTFunctions::await(op, res.GetAddressOf()); + if (FAILED(hr) || !res) { + qCDebug(lcQpaMime) << "Custom drop data operation returned no results or failed."; + return S_OK; + } + + // Test for properties + ComPtr<IPropertyValue> propertyValue; + hr = res.As(&propertyValue); + if (SUCCEEDED(hr)) { + // We need to check which type of custom data we are receiving + PropertyType type; + propertyValue->get_Type(&type); + switch (type) { + case PropertyType_UInt8: { + quint8 v; + hr = propertyValue->GetUInt8(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int16: { + qint16 v; + hr = propertyValue->GetInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt16: { + quint16 v; + hr = propertyValue->GetUInt16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int32: { + qint32 v; + hr = propertyValue->GetInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt32: { + quint32 v; + hr = propertyValue->GetUInt32(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Int64: { + qint64 v; + hr = propertyValue->GetInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_UInt64: { + quint64 v; + hr = propertyValue->GetUInt64(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Single: { + float v; + hr = propertyValue->GetSingle(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Double: { + double v; + hr = propertyValue->GetDouble(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_Char16: { + wchar_t v; + hr = propertyValue->GetChar16(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(QString::fromWCharArray(&v, 1)); + return S_OK; + } + case PropertyType_Boolean: { + boolean v; + hr = propertyValue->GetBoolean(&v); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(v); + return S_OK; + } + case PropertyType_String: { + HString stringProperty; + hr = propertyValue->GetString(stringProperty.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + result.setValue(hStringToQString(stringProperty)); + return S_OK; + } + default: + qCDebug(lcQpaMime) << "Unknown property type dropped:" << type; + } + return S_OK; + } + + // Custom data can be read via input streams + ComPtr<IRandomAccessStream> randomAccessStream; + hr = res.As(&randomAccessStream); + if (SUCCEEDED(hr)) { + UINT64 size; + hr = randomAccessStream->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + ComPtr<IInputStream> stream; + hr = randomAccessStream.As(&stream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IBufferFactory> bufferFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + IID_PPV_ARGS(&bufferFactory)); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 length = qBound(quint64(0), quint64(size), quint64(UINT_MAX)); + ComPtr<IBuffer> buffer; + hr = bufferFactory->Create(length, &buffer); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> readOp; + hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &readOp); + + ComPtr<IBuffer> effectiveBuffer; + hr = QWinRTFunctions::await(readOp, effectiveBuffer.GetAddressOf()); + + hr = effectiveBuffer->get_Length(&length); + + ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess; + hr = effectiveBuffer.As(&byteArrayAccess); + + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + QByteArray array((char *)bytes, length); + result.setValue(array); + return S_OK; + } + + HSTRING runtimeClass; + hr = res->GetRuntimeClassName(&runtimeClass); + Q_ASSERT_SUCCEEDED(hr); + HString converted; + converted.Set(runtimeClass); + qCDebug(lcQpaMime) << "Unknown drop data type received (" << hStringToQString(converted) + << "). Ignoring..."; + return S_OK; + }); + } + return result; +} + +void QWinRTInternalMimeData::setDataView(const Microsoft::WRL::ComPtr<IDataPackageView> &d) +{ + dataView = d; + formats.clear(); +} + +static HRESULT qt_drag_enter(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_over(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e); + return S_OK; +} + +static HRESULT qt_drag_leave(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + // Qt internally checks for new drags and auto sends leave events + // Also there is no QPA function for handling leave + Q_UNUSED(sender); + Q_UNUSED(e); + return S_OK; +} + +static HRESULT qt_drop(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e) +{ + QWinRTDrag::instance()->handleNativeDragEvent(sender, e, true); + return S_OK; +} + +#define Q_DECLARE_DRAGHANDLER(name,func) \ +class QtDragEventHandler##name : public IDragEventHandler \ +{ \ +public: \ + virtual HRESULT STDMETHODCALLTYPE Invoke(IInspectable *sender, \ + ABI::Windows::UI::Xaml::IDragEventArgs *e) \ + { \ + return qt_##func(sender, e);\ + } \ + \ + STDMETHODIMP \ + QueryInterface(REFIID riid, void FAR* FAR* ppvObj) \ + { \ + if (riid == IID_IUnknown || riid == IID_IDragEventHandler) { \ + *ppvObj = this; \ + AddRef(); \ + return NOERROR; \ + } \ + *ppvObj = NULL; \ + return ResultFromScode(E_NOINTERFACE); \ + } \ + \ + STDMETHODIMP_(ULONG) \ + AddRef(void) \ + { \ + return ++m_refs; \ + } \ + \ + STDMETHODIMP_(ULONG) \ + Release(void) \ + { \ + if (--m_refs == 0) { \ + delete this; \ + return 0; \ + } \ + return m_refs; \ + } \ +private: \ +ULONG m_refs{0}; \ +}; + +Q_DECLARE_DRAGHANDLER(Enter, drag_enter) +Q_DECLARE_DRAGHANDLER(Over, drag_over) +Q_DECLARE_DRAGHANDLER(Leave, drag_leave) +Q_DECLARE_DRAGHANDLER(Drop, drop) + +#define Q_INST_DRAGHANDLER(name) QtDragEventHandler##name() + +Q_GLOBAL_STATIC(QWinRTDrag, gDrag); + +extern ComPtr<ABI::Windows::UI::Input::IPointerPoint> qt_winrt_lastPointerPoint; // qwinrtscreen.cpp + +QWinRTDrag::QWinRTDrag() + : QPlatformDrag() + , m_dragTarget(0) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_enter = new Q_INST_DRAGHANDLER(Enter); + m_over = new Q_INST_DRAGHANDLER(Over); + m_leave = new Q_INST_DRAGHANDLER(Leave); + m_drop = new Q_INST_DRAGHANDLER(Drop); + m_mimeData = new QWinRTInternalMimeData; +} + +QWinRTDrag::~QWinRTDrag() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + delete m_enter; + delete m_over; + delete m_leave; + delete m_drop; + delete m_mimeData; +} + +QWinRTDrag *QWinRTDrag::instance() +{ + return gDrag; +} + +inline HRESULT resetUiElementDrag(ComPtr<IUIElement3> &elem3, EventRegistrationToken startingToken) +{ + return QEventDispatcherWinRT::runOnXamlThread([elem3, startingToken]() { + HRESULT hr; + hr = elem3->put_CanDrag(false); + Q_ASSERT_SUCCEEDED(hr); + hr = elem3->remove_DragStarting(startingToken); + Q_ASSERT_SUCCEEDED(hr); + return S_OK; + }); +} + +Qt::DropAction QWinRTDrag::drag(QDrag *drag) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << drag; + + if (!qt_winrt_lastPointerPoint) { + Q_ASSERT_X(qt_winrt_lastPointerPoint, Q_FUNC_INFO, "No pointerpoint known"); + return Qt::IgnoreAction; + } + + ComPtr<IUIElement3> elem3; + HRESULT hr = m_ui.As(&elem3); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperation<ABI::Windows::ApplicationModel::DataTransfer::DataPackageOperation>> op; + EventRegistrationToken startingToken; + + hr = QEventDispatcherWinRT::runOnXamlThread([drag, &op, &hr, elem3, &startingToken, this]() { + + hr = elem3->put_CanDrag(true); + Q_ASSERT_SUCCEEDED(hr); + + auto startingCallback = Callback<ITypedEventHandler<UIElement*, DragStartingEventArgs*>> ([drag](IInspectable *, IDragStartingEventArgs *args) { + qCDebug(lcQpaMime) << "Drag starting" << args; + + ComPtr<IDataPackage> dataPackage; + HRESULT hr; + hr = args->get_Data(dataPackage.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + Qt::DropAction action = drag->defaultAction(); + hr = dataPackage->put_RequestedOperation(translateFromQDragDropActions(action)); + Q_ASSERT_SUCCEEDED(hr); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragStartingEventArgs2> args2; + hr = args->QueryInterface(IID_PPV_ARGS(&args2)); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropActions actions = drag->supportedActions(); + DataPackageOperation allowedOperations = DataPackageOperation_None; + if (actions & Qt::CopyAction) + allowedOperations |= DataPackageOperation_Copy; + if (actions & Qt::MoveAction) + allowedOperations |= DataPackageOperation_Move; + if (actions & Qt::LinkAction) + allowedOperations |= DataPackageOperation_Link; + hr = args2->put_AllowedOperations(allowedOperations); + Q_ASSERT_SUCCEEDED(hr); +#endif // QT_WINRT_LIMITED_DRAGANDDROP + QMimeData *mimeData = drag->mimeData(); + if (mimeData->hasText()) { + hr = dataPackage->SetText(qStringToHString(mimeData->text()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + if (mimeData->hasHtml()) { + hr = dataPackage->SetHtmlFormat(qStringToHString(mimeData->html()).Get()); + Q_ASSERT_SUCCEEDED(hr); + } + // ### TODO: Missing: weblink, image + + if (!drag->pixmap().isNull()) { + const QImage image2 = drag->pixmap().toImage(); + const QImage image = image2.convertToFormat(QImage::Format_ARGB32); + if (!image.isNull()) { + // Create IBuffer containing image + ComPtr<IBuffer> imageBuffer = createIBufferFromData(reinterpret_cast<const char*>(image.bits()), image.byteCount()); + + ComPtr<ISoftwareBitmapFactory> bitmapFactory; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Graphics_Imaging_SoftwareBitmap).Get(), + IID_PPV_ARGS(&bitmapFactory)); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<ISoftwareBitmap> bitmap; + hr = bitmapFactory->Create(BitmapPixelFormat_Rgba8, image.width(), image.height(), &bitmap); + Q_ASSERT_SUCCEEDED(hr); + + hr = bitmap->CopyFromBuffer(imageBuffer.Get()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IDragUI> dragUi; + hr = args->get_DragUI(dragUi.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + hr = dragUi->SetContentFromSoftwareBitmap(bitmap.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + } + + const QStringList formats = mimeData->formats(); + for (auto item : formats) { + QByteArray data = mimeData->data(item); + + ComPtr<IBuffer> buffer = createIBufferFromData(data.constData(), data.size()); + + // We cannot push the buffer to the data package as the result on + // recipient side is different from native events. It still sends a + // buffer, but that potentially cannot be parsed. Hence we need to create + // a IRandomAccessStream which gets forwarded as is to the drop side. + ComPtr<IRandomAccessStream> ras; + hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream).Get(), &ras); + Q_ASSERT_SUCCEEDED(hr); + + hr = ras->put_Size(data.size()); + ComPtr<IOutputStream> outputStream; + hr = ras->GetOutputStreamAt(0, &outputStream); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr<IAsyncOperationWithProgress<UINT32,UINT32>> writeOp; + hr = outputStream->WriteAsync(buffer.Get(), &writeOp); + Q_ASSERT_SUCCEEDED(hr); + + UINT32 result; + hr = QWinRTFunctions::await(writeOp, &result); + Q_ASSERT_SUCCEEDED(hr); + + unsigned char flushResult; + ComPtr<IAsyncOperation<bool>> flushOp; + hr = outputStream->FlushAsync(&flushOp); + Q_ASSERT_SUCCEEDED(hr); + + hr = QWinRTFunctions::await(flushOp, &flushResult); + Q_ASSERT_SUCCEEDED(hr); + + hr = dataPackage->SetData(qStringToHString(item).Get(), ras.Get()); + Q_ASSERT_SUCCEEDED(hr); + } + return S_OK; + }); + + hr = elem3->add_DragStarting(startingCallback.Get(), &startingToken); + Q_ASSERT_SUCCEEDED(hr); + + hr = elem3->StartDragAsync(qt_winrt_lastPointerPoint.Get(), &op); + Q_ASSERT_SUCCEEDED(hr); + + return hr; + }); + if (!op || FAILED(hr)) { + qCDebug(lcQpaMime) << "Drag failed:" << hr; + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + return Qt::IgnoreAction; + } + + DataPackageOperation nativeOperationType; + // Do not yield, as that can cause deadlocks when dropping inside the same app + hr = QWinRTFunctions::await(op, &nativeOperationType, QWinRTFunctions::ProcessThreadEvents); + Q_ASSERT_SUCCEEDED(hr); + + hr = resetUiElementDrag(elem3, startingToken); + Q_ASSERT_SUCCEEDED(hr); + + Qt::DropAction resultAction; + switch (nativeOperationType) { + case DataPackageOperation_Link: + resultAction = Qt::LinkAction; + break; + case DataPackageOperation_Copy: + resultAction = Qt::CopyAction; + break; + case DataPackageOperation_Move: + resultAction = Qt::MoveAction; + break; + case DataPackageOperation_None: + default: + resultAction = Qt::IgnoreAction; + break; + } + + return resultAction; +} + +void QWinRTDrag::setDropTarget(QWindow *target) +{ + qCDebug(lcQpaMime) << __FUNCTION__ << target; + m_dragTarget = target; +} + +QMimeData *QWinRTDrag::platformDropData() +{ + qCDebug(lcQpaMime) << __FUNCTION__; + return m_mimeData; +} + +void QWinRTDrag::setUiElement(ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element) +{ + qCDebug(lcQpaMime) << __FUNCTION__; + m_ui = element; + // We set the element to always accept drops and then evaluate during + // runtime + HRESULT hr = element->put_AllowDrop(TRUE); + EventRegistrationToken tok; + hr = element->add_DragEnter(m_enter, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragEnter handler."); + hr = element->add_DragOver(m_over, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragOver handler."); + hr = element->add_DragLeave(m_leave, &tok); + RETURN_VOID_IF_FAILED("Failed to add DragLeave handler."); + hr = element->add_Drop(m_drop, &tok); + RETURN_VOID_IF_FAILED("Failed to add Drop handler."); +} + +void QWinRTDrag::handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop) +{ + Q_UNUSED(sender); + + if (!m_dragTarget) + return; + + HRESULT hr; + Point relativePoint; + hr = e->GetPosition(m_ui.Get(), &relativePoint); + RETURN_VOID_IF_FAILED("Could not query drag position."); + const QPoint p(relativePoint.X, relativePoint.Y); + + ComPtr<IDragEventArgs2> e2; + hr = e->QueryInterface(IID_PPV_ARGS(&e2)); + RETURN_VOID_IF_FAILED("Could not convert drag event args"); + + DragDropModifiers modifiers; + hr = e2->get_Modifiers(&modifiers); + +#ifndef QT_WINRT_LIMITED_DRAGANDDROP + ComPtr<IDragEventArgs3> e3; + hr = e->QueryInterface(IID_PPV_ARGS(&e3)); + Q_ASSERT_SUCCEEDED(hr); + + DataPackageOperation dataOp; + hr = e3->get_AllowedOperations(&dataOp); + if (FAILED(hr)) + qCDebug(lcQpaMime) << __FUNCTION__ << "Could not query drag operations"; + + const Qt::DropActions actions = translateToQDragDropActions(dataOp); +#else // !QT_WINRT_LIMITED_DRAGANDDROP + const Qt::DropActions actions = Qt::LinkAction | Qt::CopyAction | Qt::MoveAction;; +#endif // !QT_WINRT_LIMITED_DRAGANDDROP + + ComPtr<IDataPackageView> dataView; + hr = e2->get_DataView(&dataView); + Q_ASSERT_SUCCEEDED(hr); + + m_mimeData->setDataView(dataView); + + // We use deferral as we need to jump to the Qt thread to handle + // the drag event + ComPtr<IDragOperationDeferral> deferral; + hr = e2->GetDeferral(&deferral); + Q_ASSERT_SUCCEEDED(hr); + + DragThreadTransferData *transferData = new DragThreadTransferData; + transferData->moveToThread(qGuiApp->thread()); + transferData->window = m_dragTarget; + transferData->point = p; + transferData->mime = m_mimeData; + transferData->actions = actions; + transferData->dropAction = drop; + transferData->nativeArgs = e; + transferData->deferral = deferral; + QMetaObject::invokeMethod(transferData, "handleDrag", Qt::QueuedConnection); +} + +DragThreadTransferData::DragThreadTransferData(QObject *parent) + : QObject(parent) + , dropAction(false) +{ +} + +void DragThreadTransferData::handleDrag() +{ + bool accepted = false; + Qt::DropAction acceptedAction; + if (dropAction) { + QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } else { + QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(window, mime, point, actions); + accepted = response.isAccepted(); + acceptedAction = response.acceptedAction(); + } + + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([accepted, acceptedAction, this]() { + HRESULT hr; + hr = nativeArgs->put_Handled(accepted); + if (acceptedAction != Qt::IgnoreAction) { + ComPtr<IDragEventArgs2> e2; + hr = nativeArgs.As(&e2); + if (SUCCEEDED(hr)) + hr = e2->put_AcceptedOperation(translateFromQDragDropActions(acceptedAction)); + } + deferral->Complete(); + return S_OK; + }); + Q_ASSERT_SUCCEEDED(hr); + deleteLater(); +} + +QT_END_NAMESPACE + +#include "qwinrtdrag.moc" diff --git a/src/plugins/platforms/winrt/qwinrtdrag.h b/src/plugins/platforms/winrt/qwinrtdrag.h new file mode 100644 index 0000000000..97079d831b --- /dev/null +++ b/src/plugins/platforms/winrt/qwinrtdrag.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qpa/qplatformdrag.h> + +#include <QtCore/QLoggingCategory> +#include <QtCore/QMimeData> +#include <QtGui/private/qdnd_p.h> // QInternalMime + +#include <wrl.h> + +namespace ABI { + namespace Windows { + namespace ApplicationModel { + namespace DataTransfer { + struct IDataPackageView; + } + } + namespace UI { + namespace Xaml { + struct IUIElement; + struct IDragEventArgs; + struct IDragOperationDeferral; + //struct IDataPackageView; + } + } + } +} + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaMime) + +class QtDragEventHandlerEnter; +class QtDragEventHandlerOver; +class QtDragEventHandlerLeave; +class QtDragEventHandlerDrop; +class QWinRTInternalMimeData; + +class QWinRTInternalMimeData : public QInternalMimeData { +public: + QWinRTInternalMimeData(); + virtual ~QWinRTInternalMimeData(); + + bool hasFormat_sys(const QString &mimetype) const Q_DECL_OVERRIDE; + QStringList formats_sys() const Q_DECL_OVERRIDE; + QVariant retrieveData_sys(const QString &mimetype, QVariant::Type preferredType) const Q_DECL_OVERRIDE; + + void setDataView(const Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> &d); +private: + Microsoft::WRL::ComPtr<ABI::Windows::ApplicationModel::DataTransfer::IDataPackageView> dataView; + mutable QStringList formats; +}; + +class QWinRTDrag : public QPlatformDrag { +public: + QWinRTDrag(); + virtual ~QWinRTDrag(); + static QWinRTDrag *instance(); + + QMimeData *platformDropData(void) Q_DECL_OVERRIDE; + Qt::DropAction drag(QDrag *) Q_DECL_OVERRIDE; + + void setDropTarget(QWindow *target); + + // Native integration and registration + void setUiElement(Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> &element); + + void handleNativeDragEvent(IInspectable *sender, ABI::Windows::UI::Xaml::IDragEventArgs *e, bool drop = false); +private: + Microsoft::WRL::ComPtr<ABI::Windows::UI::Xaml::IUIElement> m_ui; + QWindow *m_dragTarget; + QtDragEventHandlerEnter *m_enter; + QtDragEventHandlerOver *m_over; + QtDragEventHandlerLeave *m_leave; + QtDragEventHandlerDrop *m_drop; + QWinRTInternalMimeData *m_mimeData; +}; + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/winrt/qwinrtintegration.cpp b/src/plugins/platforms/winrt/qwinrtintegration.cpp index 8ef560ba20..42b7f7e909 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.cpp +++ b/src/plugins/platforms/winrt/qwinrtintegration.cpp @@ -48,6 +48,9 @@ #include "qwinrtfontdatabase.h" #include "qwinrttheme.h" #include "qwinrtclipboard.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include <QtGui/QOffscreenSurface> #include <QtGui/QOpenGLContext> @@ -312,6 +315,17 @@ QPlatformClipboard *QWinRTIntegration::clipboard() const return d->clipboard; } +#ifndef QT_NO_DRAGANDDROP +QPlatformDrag *QWinRTIntegration::drag() const +{ +#if _MSC_VER >= 1900 + return QWinRTDrag::instance(); +#else + return QPlatformIntegration::drag(); +#endif +} +#endif // QT_NO_DRAGANDDROP + Qt::KeyboardModifiers QWinRTIntegration::queryKeyboardModifiers() const { Q_D(const QWinRTIntegration); diff --git a/src/plugins/platforms/winrt/qwinrtintegration.h b/src/plugins/platforms/winrt/qwinrtintegration.h index 9e28beb8fa..7b4d5531fc 100644 --- a/src/plugins/platforms/winrt/qwinrtintegration.h +++ b/src/plugins/platforms/winrt/qwinrtintegration.h @@ -97,6 +97,10 @@ public: QPlatformInputContext *inputContext() const Q_DECL_OVERRIDE; QPlatformServices *services() const Q_DECL_OVERRIDE; QPlatformClipboard *clipboard() const Q_DECL_OVERRIDE; +#ifndef QT_NO_DRAGANDDROP + QPlatformDrag *drag() const Q_DECL_OVERRIDE; +#endif + Qt::KeyboardModifiers queryKeyboardModifiers() const Q_DECL_OVERRIDE; QStringList themeNames() const Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index aed33f6b48..ad32e63cad 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -42,6 +42,9 @@ #include "qwinrtbackingstore.h" #include "qwinrtinputcontext.h" #include "qwinrtcursor.h" +#ifndef QT_NO_DRAGANDDROP +#include "qwinrtdrag.h" +#endif #include "qwinrtwindow.h" #include <private/qeventdispatcher_winrt_p.h> @@ -556,6 +559,9 @@ QWinRTScreen::QWinRTScreen() ComPtr<Xaml::IUIElement> uiElement; hr = canvas.As(&uiElement); Q_ASSERT_SUCCEEDED(hr); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setUiElement(uiElement); +#endif hr = window->put_Content(uiElement.Get()); Q_ASSERT_SUCCEEDED(hr); hr = canvas.As(&d->canvas); @@ -764,6 +770,10 @@ void QWinRTScreen::addWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); + +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + QWinRTDrag::instance()->setDropTarget(window); +#endif } void QWinRTScreen::removeWindow(QWindow *window) @@ -778,6 +788,10 @@ void QWinRTScreen::removeWindow(QWindow *window) QWindowSystemInterface::handleWindowActivated(window, Qt::OtherFocusReason); handleExpose(); QWindowSystemInterface::flushWindowSystemEvents(); +#if _MSC_VER >= 1900 && !defined(QT_NO_DRAGANDDROP) + if (wasTopWindow) + QWinRTDrag::instance()->setDropTarget(topWindow()); +#endif } void QWinRTScreen::raise(QWindow *window) @@ -973,6 +987,9 @@ HRESULT QWinRTScreen::onPointerExited(ICoreWindow *, IPointerEventArgs *args) return S_OK; } +// Required for qwinrtdrag.cpp +ComPtr<IPointerPoint> qt_winrt_lastPointerPoint; + HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) { Q_D(QWinRTScreen); @@ -980,6 +997,7 @@ HRESULT QWinRTScreen::onPointerUpdated(ICoreWindow *, IPointerEventArgs *args) if (FAILED(args->get_CurrentPoint(&pointerPoint))) return E_INVALIDARG; + qt_winrt_lastPointerPoint = pointerPoint; // Common traits - point, modifiers, properties Point point; pointerPoint->get_Position(&point); diff --git a/src/plugins/platforms/winrt/winrt.pro b/src/plugins/platforms/winrt/winrt.pro index 144c581015..28456f66ec 100644 --- a/src/plugins/platforms/winrt/winrt.pro +++ b/src/plugins/platforms/winrt/winrt.pro @@ -14,6 +14,7 @@ SOURCES = \ qwinrtbackingstore.cpp \ qwinrtclipboard.cpp \ qwinrtcursor.cpp \ + qwinrtdrag.cpp \ qwinrteglcontext.cpp \ qwinrteventdispatcher.cpp \ qwinrtfiledialoghelper.cpp \ @@ -32,6 +33,7 @@ HEADERS = \ qwinrtbackingstore.h \ qwinrtclipboard.h \ qwinrtcursor.h \ + qwinrtdrag.h \ qwinrteglcontext.h \ qwinrteventdispatcher.h \ qwinrtfiledialoghelper.h \ @@ -47,6 +49,15 @@ HEADERS = \ OTHER_FILES += winrt.json +WINRT_SDK_VERSION_STRING = $$(UCRTVersion) +WINRT_SDK_VERSION = $$member($$list($$split(WINRT_SDK_VERSION_STRING, .)), 2) +lessThan(WINRT_SDK_VERSION, 14322): DEFINES += QT_WINRT_LIMITED_DRAGANDDROP + +*-msvc2013|contains(DEFINES, QT_NO_DRAGANDDROP) { + SOURCES -= qwinrtdrag.cpp + HEADERS -= qwinrtdrag.h +} + PLUGIN_TYPE = platforms PLUGIN_CLASS_NAME = QWinRTIntegrationPlugin !equals(TARGET, $$QT_DEFAULT_QPA_PLUGIN): PLUGIN_EXTENDS = - diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 4b0e94eda0..bc62b500e5 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> #include <QtCore/QDebug> #include "qxcbconnection.h" @@ -264,6 +265,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) } else { screen = createScreen(virtualDesktop, output, outputInfo.data()); qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled"; + QHighDpiScaling::updateHighDpiScaling(); } } } else if (screen) { diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 55660ce31c..0f4207c636 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -6,7 +6,7 @@ qtHaveModule(network):!contains(QT_DISABLED_FEATURES, bearermanagement): SUBDIRS qtHaveModule(gui) { SUBDIRS *= platforms platforminputcontexts platformthemes !contains(QT_DISABLED_FEATURES, imageformatplugin): SUBDIRS *= imageformats - !contains(QT_DISABLED_FEATURES, library): SUBDIRS *= generic + !android:!contains(QT_DISABLED_FEATURES, library): SUBDIRS *= generic } !winrt:!wince*:qtHaveModule(widgets):!contains(QT_DISABLED_FEATURES, printer) { diff --git a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp index 6f5fb417c6..7e6eb7c559 100644 --- a/src/plugins/printsupport/windows/qwindowsprintdevice.cpp +++ b/src/plugins/printsupport/windows/qwindowsprintdevice.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 John Layt <jlayt@kde.org> +** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -80,6 +81,22 @@ static QPrint::InputSlot paperBinToInputSlot(int windowsId, const QString &name) return slot; } +static LPDEVMODE getDevmode(HANDLE hPrinter, const QString &printerId) +{ + LPWSTR printerIdUtf16 = const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(printerId.utf16())); + // Allocate the required DEVMODE buffer + LONG dmSize = DocumentProperties(NULL, hPrinter, printerIdUtf16, NULL, NULL, 0); + if (dmSize < 0) + return Q_NULLPTR; + LPDEVMODE pDevMode = reinterpret_cast<LPDEVMODE>(malloc(dmSize)); + // Get the default DevMode + LONG result = DocumentProperties(NULL, hPrinter, printerIdUtf16, pDevMode, NULL, DM_OUT_BUFFER); + if (result != IDOK) { + free(pDevMode); + pDevMode = Q_NULLPTR; + } + return pDevMode; +} QWindowsPrintDevice::QWindowsPrintDevice() : QPlatformPrintDevice(), @@ -197,26 +214,21 @@ QPageSize QWindowsPrintDevice::defaultPageSize() const QPageSize pageSize; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default paper size - if (result == IDOK && pDevMode->dmFields & DM_PAPERSIZE) { - // Find the supported page size that matches, in theory default should be one of them - foreach (const QPageSize &ps, m_pageSizes) { - if (ps.windowsId() == pDevMode->dmPaperSize) { - pageSize = ps; - break; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default paper size + if (pDevMode->dmFields & DM_PAPERSIZE) { + // Find the supported page size that matches, in theory default should be one of them + foreach (const QPageSize &ps, m_pageSizes) { + if (ps.windowsId() == pDevMode->dmPaperSize) { + pageSize = ps; + break; + } } } + // Clean-up + free(pDevMode); } - // Clean-up - free(pDevMode); return pageSize; } @@ -232,20 +244,14 @@ QMarginsF QWindowsPrintDevice::printableMargins(const QPageSize &pageSize, QScopedArrayPointer<BYTE> buffer(new BYTE[needed]); if (GetPrinter(m_hPrinter, 2, buffer.data(), needed, &needed)) { PPRINTER_INFO_2 info = reinterpret_cast<PPRINTER_INFO_2>(buffer.data()); - DEVMODE *devMode = info->pDevMode; + LPDEVMODE devMode = info->pDevMode; bool separateDevMode = false; if (!devMode) { // GetPrinter() didn't include the DEVMODE. Get it a different way. - LONG result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), - NULL, NULL, 0); - devMode = (DEVMODE *)malloc(result); - separateDevMode = true; - result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), - devMode, NULL, DM_OUT_BUFFER); - if (result != IDOK) { - free(devMode); + devMode = getDevmode(m_hPrinter, m_id); + if (!devMode) return margins; - } + separateDevMode = true; } HDC pDC = CreateDC(NULL, (LPWSTR)m_id.utf16(), NULL, devMode); @@ -297,23 +303,17 @@ int QWindowsPrintDevice::defaultResolution() const { int resolution = 72; // TODO Set a sensible default? - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default resolution - if (result == IDOK && pDevMode->dmFields & DM_YRESOLUTION) { - if (pDevMode->dmPrintQuality > 0) - resolution = pDevMode->dmPrintQuality; - else - resolution = pDevMode->dmYResolution; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default resolution + if (pDevMode->dmFields & DM_YRESOLUTION) { + if (pDevMode->dmPrintQuality > 0) + resolution = pDevMode->dmPrintQuality; + else + resolution = pDevMode->dmYResolution; + } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return resolution; } @@ -346,26 +346,20 @@ QPrint::InputSlot QWindowsPrintDevice::defaultInputSlot() const { QPrint::InputSlot inputSlot = QPlatformPrintDevice::defaultInputSlot();; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default input slot - if (result == IDOK && pDevMode->dmFields & DM_DEFAULTSOURCE) { - QPrint::InputSlot tempSlot = paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); - foreach (const QPrint::InputSlot &slot, supportedInputSlots()) { - if (slot.key == tempSlot.key) { - inputSlot = slot; - break; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default input slot + if (pDevMode->dmFields & DM_DEFAULTSOURCE) { + QPrint::InputSlot tempSlot = paperBinToInputSlot(pDevMode->dmDefaultSource, QString()); + foreach (const QPrint::InputSlot &slot, supportedInputSlots()) { + if (slot.key == tempSlot.key) { + inputSlot = slot; + break; + } } } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return inputSlot; } @@ -392,23 +386,17 @@ QPrint::DuplexMode QWindowsPrintDevice::defaultDuplexMode() const { QPrint::DuplexMode duplexMode = QPrint::DuplexNone; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default duplex mode - if (result == IDOK && pDevMode->dmFields & DM_DUPLEX) { - if (pDevMode->dmDuplex == DMDUP_VERTICAL) - duplexMode = QPrint::DuplexLongSide; - else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) - duplexMode = QPrint::DuplexShortSide; + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default duplex mode + if (pDevMode->dmFields & DM_DUPLEX) { + if (pDevMode->dmDuplex == DMDUP_VERTICAL) + duplexMode = QPrint::DuplexLongSide; + else if (pDevMode->dmDuplex == DMDUP_HORIZONTAL) + duplexMode = QPrint::DuplexShortSide; + } + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return duplexMode; } @@ -430,21 +418,13 @@ QPrint::ColorMode QWindowsPrintDevice::defaultColorMode() const QPrint::ColorMode colorMode = QPrint::GrayScale; - // Allocate the required DEVMODE buffer - DWORD dmSize = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), NULL, NULL, 0); - LPDEVMODE pDevMode = (LPDEVMODE)malloc(dmSize); - - // Get the default DevMode - DWORD result = DocumentProperties(NULL, m_hPrinter, (LPWSTR)m_id.utf16(), pDevMode, NULL, DM_OUT_BUFFER); - - // Get the default color mode - if (result == IDOK && pDevMode->dmFields & DM_COLOR) { - if (pDevMode->dmColor == DMCOLOR_COLOR) + if (LPDEVMODE pDevMode = getDevmode(m_hPrinter, m_id)) { + // Get the default color mode + if (pDevMode->dmFields & DM_COLOR && pDevMode->dmColor == DMCOLOR_COLOR) colorMode = QPrint::Color; + // Clean-up + free(pDevMode); } - - // Clean-up - free(pDevMode); return colorMode; } |