diff options
29 files changed, 340 insertions, 96 deletions
diff --git a/examples/opengl/contextinfo/main.cpp b/examples/opengl/contextinfo/main.cpp index f8bca02527..88ab4a7334 100644 --- a/examples/opengl/contextinfo/main.cpp +++ b/examples/opengl/contextinfo/main.cpp @@ -53,6 +53,15 @@ int main(int argc, char **argv) { + for (int i = 1; i < argc; ++i) { + if (!qstrcmp(argv[i], "-g")) + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); + else if (!qstrcmp(argv[i], "-s")) + QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL); + else if (!qstrcmp(argv[i], "-d")) + QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); + } + QApplication app(argc, argv); Widget w; diff --git a/examples/opengl/contextinfo/widget.cpp b/examples/opengl/contextinfo/widget.cpp index a3bda7b420..fc21983f38 100644 --- a/examples/opengl/contextinfo/widget.cpp +++ b/examples/opengl/contextinfo/widget.cpp @@ -60,10 +60,12 @@ #include <QPushButton> #include <QTextEdit> #include <QSplitter> +#include <QGuiApplication> #include <QSurfaceFormat> #include <QOpenGLContext> #include <QOpenGLFunctions> #include <QDebug> +#include <QTextStream> struct Version { const char *str; @@ -235,6 +237,24 @@ Widget::Widget(QWidget *parent) m_renderWindowContainer = new QWidget; addRenderWindow(); + QString description; + QTextStream str(&description); + str << "Qt " << QT_VERSION_STR << ' ' << QGuiApplication::platformName(); + const char *openGlVariables[] = + {"QT_ANGLE_PLATFORM", "QT_OPENGL", "QT_OPENGL_BUGLIST", "QT_OPENGL_DLL"}; + const size_t variableCount = sizeof(openGlVariables) / sizeof(openGlVariables[0]); + for (size_t v = 0; v < variableCount; ++v) { + if (qEnvironmentVariableIsSet(openGlVariables[v])) + str << ' ' << openGlVariables[v] << '=' << qgetenv(openGlVariables[v]); + } + if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES)) + str << " Qt::AA_UseOpenGLES"; + if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL)) + str << " Qt::AA_UseSoftwareOpenGL"; + if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL)) + str << " Qt::AA_UseSoftwareOpenGL"; + layout->addWidget(new QLabel(description)); + setLayout(layout); } diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index de37731b1a..4851d1ce6f 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -988,15 +988,18 @@ struct QMessagePattern { #endif #ifdef QLOGGING_HAVE_BACKTRACE struct BacktraceParams { - QString backtraceSeparator; - int backtraceDepth; + QString backtraceSeparator; + int backtraceDepth; }; - QList<BacktraceParams> backtraceArgs; // backtrace argumens in sequence of %{backtrace + QVector<BacktraceParams> backtraceArgs; // backtrace argumens in sequence of %{backtrace #endif bool fromEnvironment; static QBasicMutex mutex; }; +#ifdef QLOGGING_HAVE_BACKTRACE +Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_MOVABLE_TYPE); +#endif QBasicMutex QMessagePattern::mutex; diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp index 7255414bdc..03dc5fc660 100644 --- a/src/corelib/io/qlockfile_unix.cpp +++ b/src/corelib/io/qlockfile_unix.cpp @@ -151,12 +151,14 @@ static bool fcntlWorksAfterFlock(const QString &fn) if (fcntlOK.isDestroyed()) return QLockFilePrivate::checkFcntlWorksAfterFlock(fn); bool *worksPtr = fcntlOK->object(fn); - if (!worksPtr) { - worksPtr = new bool(QLockFilePrivate::checkFcntlWorksAfterFlock(fn)); - fcntlOK->insert(fn, worksPtr); - } + if (worksPtr) + return *worksPtr; + + const bool val = QLockFilePrivate::checkFcntlWorksAfterFlock(fn); + worksPtr = new bool(val); + fcntlOK->insert(fn, worksPtr); - return *worksPtr; + return val; } static bool setNativeLocks(const QString &fileName, int fd) diff --git a/src/corelib/io/qloggingregistry.cpp b/src/corelib/io/qloggingregistry.cpp index 818772a399..eaebbc1ffc 100644 --- a/src/corelib/io/qloggingregistry.cpp +++ b/src/corelib/io/qloggingregistry.cpp @@ -365,9 +365,6 @@ void QLoggingRegistry::setApiRules(const QString &content) */ void QLoggingRegistry::updateRules() { - if (categoryFilter != defaultCategoryFilter) - return; - rules = qtConfigRules + configRules + apiRules + envRules; for (auto it = categories.keyBegin(), end = categories.keyEnd(); it != end; ++it) diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp index 5d8b567c8c..ed540f9e5b 100644 --- a/src/corelib/io/qprocess_win.cpp +++ b/src/corelib/io/qprocess_win.cpp @@ -49,6 +49,7 @@ #include <qfileinfo.h> #include <qregexp.h> #include <qwineventnotifier.h> +#include <private/qsystemlibrary_p.h> #include <private/qthread_p.h> #include <qdebug.h> @@ -817,8 +818,45 @@ qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen) return stdinChannel.writer->write(data, maxlen); } +// Use ShellExecuteEx() to trigger an UAC prompt when CreateProcess()fails +// with ERROR_ELEVATION_REQUIRED. +static bool startDetachedUacPrompt(const QString &programIn, const QStringList &arguments, + const QString &workingDir, qint64 *pid) +{ + typedef BOOL (WINAPI *ShellExecuteExType)(SHELLEXECUTEINFOW *); + + static const ShellExecuteExType shellExecuteEx = // XP ServicePack 1 onwards. + reinterpret_cast<ShellExecuteExType>(QSystemLibrary::resolve(QLatin1String("shell32"), + "ShellExecuteExW")); + if (!shellExecuteEx) + return false; + + const QString args = qt_create_commandline(QString(), arguments); // needs arguments only + SHELLEXECUTEINFOW shellExecuteExInfo; + memset(&shellExecuteExInfo, 0, sizeof(SHELLEXECUTEINFOW)); + shellExecuteExInfo.cbSize = sizeof(SHELLEXECUTEINFOW); + shellExecuteExInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_UNICODE | SEE_MASK_FLAG_NO_UI; + shellExecuteExInfo.lpVerb = L"runas"; + const QString program = QDir::toNativeSeparators(programIn); + shellExecuteExInfo.lpFile = reinterpret_cast<LPCWSTR>(program.utf16()); + if (!args.isEmpty()) + shellExecuteExInfo.lpParameters = reinterpret_cast<LPCWSTR>(args.utf16()); + if (!workingDir.isEmpty()) + shellExecuteExInfo.lpDirectory = reinterpret_cast<LPCWSTR>(workingDir.utf16()); + shellExecuteExInfo.nShow = SW_SHOWNORMAL; + + if (!shellExecuteEx(&shellExecuteExInfo)) + return false; + if (pid) + *pid = qint64(GetProcessId(shellExecuteExInfo.hProcess)); + CloseHandle(shellExecuteExInfo.hProcess); + return true; +} + bool QProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDir, qint64 *pid) { + static const DWORD errorElevationRequired = 740; + QString args = qt_create_commandline(program, arguments); bool success = false; PROCESS_INFORMATION pinfo; @@ -838,6 +876,8 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a CloseHandle(pinfo.hProcess); if (pid) *pid = pinfo.dwProcessId; + } else if (GetLastError() == errorElevationRequired) { + success = startDetachedUacPrompt(program, arguments, workingDir, pid); } return success; diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index bff6a9ac15..d866d3bcf7 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -520,7 +520,7 @@ QMimeType QMimeDatabase::mimeTypeForUrl(const QUrl &url) const return mimeTypeForFile(url.toLocalFile()); const QString scheme = url.scheme(); - if (scheme.startsWith(QLatin1String("http"))) + if (scheme.startsWith(QLatin1String("http")) || scheme == QLatin1String("mailto")) return mimeTypeForName(d->defaultMimeType()); return mimeTypeForFile(url.path()); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 9bfc46f32e..c3aae2fd22 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -10092,6 +10092,9 @@ static inline int qt_find_latin1_string(const QChar *haystack, int size, QLatin1String needle, int from, Qt::CaseSensitivity cs) { + if (size < needle.size()) + return -1; + const char *latin1 = needle.latin1(); int len = needle.size(); QVarLengthArray<ushort> s(len); diff --git a/src/dbus/qdbusargument_p.h b/src/dbus/qdbusargument_p.h index 296918961f..ca0a2cde13 100644 --- a/src/dbus/qdbusargument_p.h +++ b/src/dbus/qdbusargument_p.h @@ -72,7 +72,7 @@ public: inline QDBusArgumentPrivate(int flags = 0) : message(0), ref(1), capabilities(flags) { } - ~QDBusArgumentPrivate(); + virtual ~QDBusArgumentPrivate(); static bool checkRead(QDBusArgumentPrivate *d); static bool checkReadAndDetach(QDBusArgumentPrivate *&d); diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 935c17dfc1..c6173791a6 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1956,7 +1956,7 @@ void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected() void QNetworkReplyHttpImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState) { if (sessionState == QNetworkSession::Disconnected - && (state != Idle || state != Reconnecting)) { + && state != Idle && state != Reconnecting) { error(QNetworkReplyImpl::NetworkSessionFailedError, QCoreApplication::translate("QNetworkReply", "Network session error.")); finished(); diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 59a0cd8ddc..d69d5983cb 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -325,7 +325,7 @@ void QNetworkReplyImplPrivate::_q_networkSessionConnected() void QNetworkReplyImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState) { if (sessionState == QNetworkSession::Disconnected - && (state != Idle || state != Reconnecting)) { + && state != Idle && state != Reconnecting) { error(QNetworkReplyImpl::NetworkSessionFailedError, QCoreApplication::translate("QNetworkReply", "Network session error.")); finished(); diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index a874c1b43f..7bf9a6de7a 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -2181,7 +2181,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs) } do { - if (state() != ConnectedState) + if (state() != ConnectedState && state() != BoundState) return false; bool readyToRead = false; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 4d2af84e8e..4d5f5d74f2 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -72,6 +72,7 @@ typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow; QCocoaWindow *_platformWindow; BOOL _grabbingMouse; BOOL _releaseOnMouseUp; + QPointer<QObject> _watcher; } @property (nonatomic, readonly) QCocoaNSWindow *window; @@ -321,6 +322,11 @@ public: // for QNSView }; QHash<quintptr, BorderRange> m_contentBorderAreas; // identifer -> uppper/lower QHash<quintptr, bool> m_enabledContentBorderAreas; // identifer -> enabled state (true/false) + + // This object is tracked by a 'watcher' + // object in a window helper, preventing use of dangling + // pointers. + QObject sentinel; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 29cc4130ed..01e72303be 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -99,6 +99,7 @@ static bool isMouseEvent(NSEvent *ev) // make sure that m_nsWindow stays valid until the // QCocoaWindow is deleted by Qt. [_window setReleasedWhenClosed:NO]; + _watcher = &_platformWindow->sentinel; } return self; @@ -107,7 +108,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)handleWindowEvent:(NSEvent *)theEvent { QCocoaWindow *pw = self.platformWindow; - if (pw && pw->m_forwardWindow) { + if (_watcher && pw && pw->m_forwardWindow) { if (theEvent.type == NSLeftMouseUp || theEvent.type == NSLeftMouseDragged) { QNSView *forwardView = pw->m_qtView; if (theEvent.type == NSLeftMouseUp) { @@ -146,7 +147,7 @@ static bool isMouseEvent(NSEvent *ev) if (!self.window.delegate) return; // Already detached, pending NSAppKitDefined event - if (pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) { + if (_watcher && pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) { NSPoint loc = [theEvent locationInWindow]; NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]]; NSRect contentFrame = [[self.window contentView] frame]; @@ -162,6 +163,7 @@ static bool isMouseEvent(NSEvent *ev) - (void)detachFromPlatformWindow { _platformWindow = 0; + _watcher.clear(); [self.window.delegate release]; self.window.delegate = nil; } diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 9486dd84f0..b524adb2cb 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -374,13 +374,11 @@ QWindowsWindow *QWindowsIntegration::createPlatformWindowHelper(QWindow *window, QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() { #if defined(QT_OPENGL_DYNAMIC) - const QWindowsOpenGLTester::Renderers supportedRenderers = QWindowsOpenGLTester::supportedRenderers(); - QWindowsOpenGLTester::Renderer requestedRenderer = QWindowsOpenGLTester::requestedRenderer(); switch (requestedRenderer) { case QWindowsOpenGLTester::DesktopGl: if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) { - if ((supportedRenderers & QWindowsOpenGLTester::DisableRotationFlag) + if ((QWindowsOpenGLTester::supportedRenderers() & QWindowsOpenGLTester::DisableRotationFlag) && !QWindowsScreen::setOrientationPreference(Qt::LandscapeOrientation)) { qCWarning(lcQpaGl, "Unable to disable rotation."); } @@ -406,6 +404,7 @@ QWindowsStaticOpenGLContext *QWindowsStaticOpenGLContext::doCreate() break; } + const QWindowsOpenGLTester::Renderers supportedRenderers = QWindowsOpenGLTester::supportedRenderers(); if (supportedRenderers & QWindowsOpenGLTester::DesktopGl) { if (QWindowsStaticOpenGLContext *glCtx = QOpenGLStaticContext::create()) { if ((supportedRenderers & QWindowsOpenGLTester::DisableRotationFlag) diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index aec09a260b..f3667aaa0d 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -1135,6 +1135,8 @@ HRESULT QWinRTScreen::onActivated(ICoreWindow *, IWindowActivatedEventArgs *args return S_OK; } + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive); + // Activate topWindow if (!d->visibleWindows.isEmpty()) { Qt::FocusReason focusReason = activationState == CoreWindowActivationState_PointerActivated diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 2e51576e3b..5ad349fdc2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -567,6 +567,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , m_buttons(0) , m_focusWindow(0) , m_mouseGrabber(0) + , m_mousePressWindow(0) , m_clientLeader(0) , m_systemTrayTracker(0) , m_glIntegration(Q_NULLPTR) @@ -1379,6 +1380,11 @@ void QXcbConnection::setFocusWindow(QXcbWindow *w) void QXcbConnection::setMouseGrabber(QXcbWindow *w) { m_mouseGrabber = w; + m_mousePressWindow = Q_NULLPTR; +} +void QXcbConnection::setMousePressWindow(QXcbWindow *w) +{ + m_mousePressWindow = w; } void QXcbConnection::grabServer() diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index e8af82ec8c..68d7b5622f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -481,6 +481,8 @@ public: void setFocusWindow(QXcbWindow *); QXcbWindow *mouseGrabber() const { return m_mouseGrabber; } void setMouseGrabber(QXcbWindow *); + QXcbWindow *mousePressWindow() const { return m_mousePressWindow; } + void setMousePressWindow(QXcbWindow *); QByteArray startupId() const { return m_startupId; } void setStartupId(const QByteArray &nextId) { m_startupId = nextId; } @@ -668,6 +670,7 @@ private: QXcbWindow *m_focusWindow; QXcbWindow *m_mouseGrabber; + QXcbWindow *m_mousePressWindow; xcb_window_t m_clientLeader; QByteArray m_startupId; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 5e4aaa37d9..c507ac09b6 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -592,8 +592,12 @@ QXcbWindow::~QXcbWindow() { if (window()->type() != Qt::ForeignWindow) destroy(); - else if (connection()->mouseGrabber() == this) - connection()->setMouseGrabber(Q_NULLPTR); + else { + if (connection()->mouseGrabber() == this) + connection()->setMouseGrabber(Q_NULLPTR); + if (connection()->mousePressWindow() == this) + connection()->setMousePressWindow(Q_NULLPTR); + } } void QXcbWindow::destroy() @@ -851,6 +855,16 @@ void QXcbWindow::hide() if (connection()->mouseGrabber() == this) connection()->setMouseGrabber(Q_NULLPTR); + if (QPlatformWindow *w = connection()->mousePressWindow()) { + // Unset mousePressWindow when it (or one of its parents) is unmapped + while (w) { + if (w == this) { + connection()->setMousePressWindow(Q_NULLPTR); + break; + } + w = w->parent(); + } + } m_mapped = false; @@ -2199,6 +2213,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in return; } + connection()->setMousePressWindow(this); + handleMouseEvent(timestamp, local, global, modifiers, source); } @@ -2213,19 +2229,44 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, return; } + if (connection()->buttons() == Qt::NoButton) + connection()->setMousePressWindow(Q_NULLPTR); + handleMouseEvent(timestamp, local, global, modifiers, source); } -static bool ignoreLeaveEvent(quint8 mode, quint8 detail) +static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) +{ + /* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted + * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated + * enter/leave events on Alt+Tab switching on some WMs with XInput2 events. + * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is + * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events. + */ + if (conn) { + const bool mouseButtonsPressed = (conn->buttons() != Qt::NoButton); +#ifdef XCB_USE_XINPUT22 + return mouseButtonsPressed || (conn->isAtLeastXI22() && conn->xi2MouseEvents()); +#else + return mouseButtonsPressed; +#endif + } + return true; +} + +static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) { - return (mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM + return ((doCheckUnGrabAncestor(conn) + && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) + || (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR) || detail == XCB_NOTIFY_DETAIL_VIRTUAL - || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL; + || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); } -static bool ignoreEnterEvent(quint8 mode, quint8 detail) +static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR) { - return ((mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM + return ((doCheckUnGrabAncestor(conn) + && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) || (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB) || detail == XCB_NOTIFY_DETAIL_VIRTUAL || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); @@ -2259,9 +2300,7 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in const QPoint global = QPoint(root_x, root_y); - if (ignoreEnterEvent(mode, detail) - || (connection()->buttons() != Qt::NoButton - && QGuiApplicationPrivate::lastCursorPosition != global)) + if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; const QPoint local(event_x, event_y); @@ -2273,11 +2312,7 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, { connection()->setTime(timestamp); - const QPoint global(root_x, root_y); - - if (ignoreLeaveEvent(mode, detail) - || (connection()->buttons() != Qt::NoButton - && QGuiApplicationPrivate::lastCursorPosition != global)) + if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow()) return; EnterEventChecker checker; @@ -2300,6 +2335,11 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i { QPoint local(event_x, event_y); QPoint global(root_x, root_y); + + // "mousePressWindow" can be NULL i.e. if a window will be grabbed or umnapped, so set it again here + if (connection()->buttons() != Qt::NoButton && connection()->mousePressWindow() == Q_NULLPTR) + connection()->setMousePressWindow(this); + handleMouseEvent(timestamp, local, global, modifiers, source); } diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index c72761f2ae..f639be809c 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -867,9 +867,11 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in if (newName == idx.data().toString()) return true; + const QString parentPath = filePath(parent(idx)); + if (newName.isEmpty() || QDir::toNativeSeparators(newName).contains(QDir::separator()) - || !QDir(filePath(parent(idx))).rename(oldName, newName)) { + || !QDir(parentPath).rename(oldName, newName)) { #ifndef QT_NO_MESSAGEBOX QMessageBox::information(0, QFileSystemModel::tr("Invalid filename"), QFileSystemModel::tr("<b>The name \"%1\" can not be used.</b><p>Try using another name, with fewer characters or no punctuations marks.") @@ -896,7 +898,7 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in parentNode->visibleChildren.removeAt(visibleLocation); QFileSystemModelPrivate::QFileSystemNode * oldValue = parentNode->children.value(oldName); parentNode->children[newName] = oldValue; - QFileInfo info(d->rootDir, newName); + QFileInfo info(parentPath, newName); oldValue->fileName = newName; oldValue->parent = parentNode; #ifndef QT_NO_FILESYSTEMWATCHER @@ -908,7 +910,7 @@ bool QFileSystemModel::setData(const QModelIndex &idx, const QVariant &value, in parentNode->visibleChildren.insert(visibleLocation, newName); d->delayedSort(); - emit fileRenamed(filePath(idx.parent()), oldName, newName); + emit fileRenamed(parentPath, oldName, newName); } return true; } diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 9306b20043..7b393463a6 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -3013,30 +3013,41 @@ QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) c { Q_D(const QHeaderView); const int max = d->modelSectionCount(); - if (d->orientation == Qt::Horizontal) { - int left = max; - int right = 0; - int rangeLeft, rangeRight; - for (const auto &r : selection) { - if (r.parent().isValid() || !r.isValid()) - continue; // we only know about toplevel items and we don't want invalid ranges - // FIXME an item inside the range may be the leftmost or rightmost - rangeLeft = visualIndex(r.left()); - if (rangeLeft == -1) // in some cases users may change the selections - continue; // before we have a chance to do the layout - rangeRight = visualIndex(r.right()); - if (rangeRight == -1) // in some cases users may change the selections - continue; // before we have a chance to do the layout - if (rangeLeft < left) - left = rangeLeft; - if (rangeRight > right) - right = rangeRight; + if (d->orientation == Qt::Horizontal) { + int logicalLeft = max; + int logicalRight = 0; + + if (d->visualIndices.empty()) { + // If no reordered sections, skip redundant visual-to-logical transformations + for (const auto &r : selection) { + if (r.parent().isValid() || !r.isValid()) + continue; // we only know about toplevel items and we don't want invalid ranges + if (r.left() < logicalLeft) + logicalLeft = r.left(); + if (r.right() > logicalRight) + logicalRight = r.right(); + } + } else { + int left = max; + int right = 0; + for (const auto &r : selection) { + if (r.parent().isValid() || !r.isValid()) + continue; // we only know about toplevel items and we don't want invalid ranges + for (int k = r.left(); k <= r.right(); ++k) { + int visual = visualIndex(k); + if (visual == -1) // in some cases users may change the selections + continue; // before we have a chance to do the layout + if (visual < left) + left = visual; + if (visual > right) + right = visual; + } + } + logicalLeft = logicalIndex(left); + logicalRight = logicalIndex(right); } - int logicalLeft = logicalIndex(left); - int logicalRight = logicalIndex(right); - if (logicalLeft < 0 || logicalLeft >= count() || logicalRight < 0 || logicalRight >= count()) return QRegion(); @@ -3047,31 +3058,44 @@ QRegion QHeaderView::visualRegionForSelection(const QItemSelection &selection) c return QRect(leftPos, 0, rightPos - leftPos, height()); } // orientation() == Qt::Vertical - int top = max; - int bottom = 0; - int rangeTop, rangeBottom; - - for (const auto &r : selection) { - if (r.parent().isValid() || !r.isValid()) - continue; // we only know about toplevel items - // FIXME an item inside the range may be the leftmost or rightmost - rangeTop = visualIndex(r.top()); - if (rangeTop == -1) // in some cases users may change the selections - continue; // before we have a chance to do the layout - rangeBottom = visualIndex(r.bottom()); - if (rangeBottom == -1) // in some cases users may change the selections - continue; // before we have a chance to do the layout - if (rangeTop < top) - top = rangeTop; - if (rangeBottom > bottom) - bottom = rangeBottom; - } - - int logicalTop = logicalIndex(top); - int logicalBottom = logicalIndex(bottom); - - if (logicalTop == -1 || logicalBottom == -1) - return QRect(); + int logicalTop = max; + int logicalBottom = 0; + + if (d->visualIndices.empty()) { + // If no reordered sections, skip redundant visual-to-logical transformations + for (const auto &r : selection) { + if (r.parent().isValid() || !r.isValid()) + continue; // we only know about toplevel items and we don't want invalid ranges + if (r.top() < logicalTop) + logicalTop = r.top(); + if (r.bottom() > logicalBottom) + logicalBottom = r.bottom(); + } + } else { + int top = max; + int bottom = 0; + + for (const auto &r : selection) { + if (r.parent().isValid() || !r.isValid()) + continue; // we only know about toplevel items and we don't want invalid ranges + for (int k = r.top(); k <= r.bottom(); ++k) { + int visual = visualIndex(k); + if (visual == -1) // in some cases users may change the selections + continue; // before we have a chance to do the layout + if (visual < top) + top = visual; + if (visual > bottom) + bottom = visual; + } + } + + logicalTop = logicalIndex(top); + logicalBottom = logicalIndex(bottom); + } + + if (logicalTop < 0 || logicalTop >= count() || + logicalBottom < 0 || logicalBottom >= count()) + return QRegion(); int topPos = sectionViewportPosition(logicalTop); int bottomPos = sectionViewportPosition(logicalBottom) + sectionSize(logicalBottom); diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 281567cede..5826116a96 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -5263,7 +5263,9 @@ QPixmap QWidget::grab(const QRect &rectangle) if (!r.intersects(rect())) return QPixmap(); - QPixmap res(r.size()); + const qreal dpr = devicePixelRatioF(); + QPixmap res((QSizeF(r.size()) * dpr).toSize()); + res.setDevicePixelRatio(dpr); if (!d->isOpaque) res.fill(Qt::transparent); d->render(&res, QPoint(), QRegion(r), renderFlags); diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 5b6e47e2c6..0dd3610164 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -3422,12 +3422,28 @@ QRect QFusionStyle::subControlRect(ComplexControl control, const QStyleOptionCom QSize textSize = option->fontMetrics.boundingRect(groupBox->text).size() + QSize(2, 2); int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget); int indicatorHeight = proxy()->pixelMetric(PM_IndicatorHeight, option, widget); + + const int width = textSize.width() + + (option->subControls & QStyle::SC_GroupBoxCheckBox ? indicatorWidth + 5 : 0); + rect = QRect(); + + if (option->rect.width() > width) { + switch (groupBox->textAlignment & Qt::AlignHorizontal_Mask) { + case Qt::AlignHCenter: + rect.moveLeft((option->rect.width() - width) / 2); + break; + case Qt::AlignRight: + rect.moveLeft(option->rect.width() - width); + break; + } + } + if (subControl == SC_GroupBoxCheckBox) { rect.setWidth(indicatorWidth); rect.setHeight(indicatorHeight); rect.moveTop(textSize.height() > indicatorHeight ? (textSize.height() - indicatorHeight) / 2 : 0); - rect.moveLeft(1); + rect.translate(1, 0); } else if (subControl == SC_GroupBoxLabel) { rect.setSize(textSize); rect.moveTop(1); diff --git a/src/widgets/styles/qwindowsvistastyle.cpp b/src/widgets/styles/qwindowsvistastyle.cpp index a0bc924dd9..dce0a93e10 100644 --- a/src/widgets/styles/qwindowsvistastyle.cpp +++ b/src/widgets/styles/qwindowsvistastyle.cpp @@ -1149,7 +1149,7 @@ void QWindowsVistaStyle::drawControl(ControlElement element, const QStyleOption if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) alignment |= Qt::TextHideMnemonic; - if (widget) { // Not needed for QtQuick Controls + if (widget && mbi->palette.color(QPalette::Window) != Qt::transparent) { // Not needed for QtQuick Controls //The rect adjustment is a workaround for the menu not really filling its background. XPThemeData theme(widget, painter, QWindowsXPStylePrivate::MenuTheme, diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 5154d84a75..593706ac0d 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -513,6 +513,8 @@ void tst_QMimeDatabase::mimeTypeForUrl() QVERIFY(db.mimeTypeForUrl(QUrl::fromEncoded("http://foo/bar.png")).isDefault()); // HTTP can't know before downloading QCOMPARE(db.mimeTypeForUrl(QUrl::fromEncoded("ftp://foo/bar.png")).name(), QString::fromLatin1("image/png")); QCOMPARE(db.mimeTypeForUrl(QUrl::fromEncoded("ftp://foo/bar")).name(), QString::fromLatin1("application/octet-stream")); // unknown extension + QCOMPARE(db.mimeTypeForUrl(QUrl("mailto:something@example.com")).name(), QString::fromLatin1("application/octet-stream")); // unknown + QCOMPARE(db.mimeTypeForUrl(QUrl("mailto:something@polish.pl")).name(), QString::fromLatin1("application/octet-stream")); // unknown, NOT perl ;) } void tst_QMimeDatabase::mimeTypeForData_data() diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp index 8408e703c0..fdf1c48adf 100644 --- a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp +++ b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp @@ -29,9 +29,16 @@ #include <QtCore> #include <QtNetwork> +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) && defined(Q_CC_MSVC) +# include <crtdbg.h> +#endif int main(int argc, char *argv[]) { + // Windows: Suppress crash notification dialog. +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) && defined(Q_CC_MSVC) + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif QCoreApplication app(argc, argv); QTcpServer server; diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp index 17c10bb43e..7939d88c73 100644 --- a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -693,6 +693,7 @@ void tst_QFileSystemModel::nameFilters() } void tst_QFileSystemModel::setData_data() { + QTest::addColumn<QString>("subdirName"); QTest::addColumn<QStringList>("files"); QTest::addColumn<QString>("oldFileName"); QTest::addColumn<QString>("newFileName"); @@ -702,7 +703,15 @@ void tst_QFileSystemModel::setData_data() << QDir::temp().absolutePath() + '/' + "a" << false; */ - QTest::newRow("in current dir") << (QStringList() << "a" << "b" << "c") + QTest::newRow("in current dir") + << QString() + << (QStringList() << "a" << "b" << "c") + << "a" + << "d" + << true; + QTest::newRow("in subdir") + << "s" + << (QStringList() << "a" << "b" << "c") << "a" << "d" << true; @@ -711,15 +720,25 @@ void tst_QFileSystemModel::setData_data() void tst_QFileSystemModel::setData() { QSignalSpy spy(model, SIGNAL(fileRenamed(QString,QString,QString))); - QString tmp = flatDirTestPath; + QFETCH(QString, subdirName); QFETCH(QStringList, files); QFETCH(QString, oldFileName); QFETCH(QString, newFileName); QFETCH(bool, success); + QString tmp = flatDirTestPath; + if (!subdirName.isEmpty()) { + QDir dir(tmp); + QVERIFY(dir.mkdir(subdirName)); + tmp.append('/' + subdirName); + } QVERIFY(createFiles(tmp, files)); - QModelIndex root = model->setRootPath(tmp); - QTRY_COMPARE(model->rowCount(root), files.count()); + QModelIndex tmpIdx = model->setRootPath(flatDirTestPath); + if (!subdirName.isEmpty()) { + tmpIdx = model->index(tmp); + model->fetchMore(tmpIdx); + } + QTRY_COMPARE(model->rowCount(tmpIdx), files.count()); QModelIndex idx = model->index(tmp + '/' + oldFileName); QCOMPARE(idx.isValid(), true); @@ -727,16 +746,21 @@ void tst_QFileSystemModel::setData() model->setReadOnly(false); QCOMPARE(model->setData(idx, newFileName), success); + model->setReadOnly(true); if (success) { QCOMPARE(spy.count(), 1); QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(model->data(idx, QFileSystemModel::FileNameRole).toString(), newFileName); + QCOMPARE(model->fileInfo(idx).filePath(), tmp + '/' + newFileName); QCOMPARE(model->index(arguments.at(0).toString()), model->index(tmp)); QCOMPARE(arguments.at(1).toString(), oldFileName); QCOMPARE(arguments.at(2).toString(), newFileName); QCOMPARE(QFile::rename(tmp + '/' + newFileName, tmp + '/' + oldFileName), true); } - QTRY_COMPARE(model->rowCount(root), files.count()); + QTRY_COMPARE(model->rowCount(tmpIdx), files.count()); + // cleanup + if (!subdirName.isEmpty()) + QVERIFY(QDir(tmp).removeRecursively()); } void tst_QFileSystemModel::sortPersistentIndex() diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index ca22eee514..47ec93ce16 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -73,6 +73,7 @@ public: void testEvent(); void testhorizontalOffset(); void testverticalOffset(); + void testVisualRegionForSelection(); friend class tst_QHeaderView; }; @@ -204,6 +205,7 @@ private slots: void QTBUG8650_crashOnInsertSections(); void QTBUG12268_hiddenMovedSectionSorting(); void QTBUG14242_hideSectionAutoSize(); + void QTBUG50171_visualRegionForSwappedItems(); void ensureNoIndexAtLength(); void offsetConsistent(); @@ -2281,6 +2283,24 @@ void tst_QHeaderView::QTBUG14242_hideSectionAutoSize() QCOMPARE(calced_length, afterlength); } +void tst_QHeaderView::QTBUG50171_visualRegionForSwappedItems() +{ + protected_QHeaderView headerView(Qt::Horizontal); + QtTestModel model; + model.rows = 2; + model.cols = 3; + headerView.setModel(&model); + headerView.swapSections(1, 2); + headerView.hideSection(0); + headerView.testVisualRegionForSelection(); +} + +void protected_QHeaderView::testVisualRegionForSelection() +{ + QRegion r = visualRegionForSelection(QItemSelection(model()->index(1, 0), model()->index(1, 2))); + QCOMPARE(r.boundingRect().contains(QRect(1, 1, length() - 2, 1)), true); +} + void tst_QHeaderView::ensureNoIndexAtLength() { QTableView qtv; diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 1a9d7ec4d2..3183fc7375 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -3030,8 +3030,23 @@ protected: QSize sizeHint() const { return QSize(500, 500); } }; +// Scale to remove devicePixelRatio should scaling be active. +static QPixmap grabFromWidget(QWidget *w, const QRect &rect) +{ + QPixmap pixmap = w->grab(rect); + const qreal devicePixelRatio = pixmap.devicePixelRatioF(); + if (!qFuzzyCompare(devicePixelRatio, qreal(1))) { + pixmap = pixmap.scaled((QSizeF(pixmap.size()) / devicePixelRatio).toSize(), + Qt::KeepAspectRatio, Qt::SmoothTransformation); + pixmap.setDevicePixelRatio(1); + } + return pixmap; +} + void tst_QWidget::testContentsPropagation() { + if (!qFuzzyCompare(qApp->devicePixelRatio(), qreal(1))) + QSKIP("This test does not work with scaling."); ContentsPropagationWidget widget; widget.setFixedSize(500, 500); widget.setContentsPropagation(false); @@ -9203,7 +9218,7 @@ void tst_QWidget::rectOutsideCoordinatesLimit_task144779() QPixmap correct(main.size()); correct.fill(Qt::green); - const QPixmap mainPixmap = main.grab(QRect(QPoint(0, 0), QSize(-1, -1))); + const QPixmap mainPixmap = grabFromWidget(&main, QRect(QPoint(0, 0), QSize(-1, -1))); QTRY_COMPARE(mainPixmap.toImage().convertToFormat(QImage::Format_RGB32), correct.toImage().convertToFormat(QImage::Format_RGB32)); @@ -9667,10 +9682,10 @@ void tst_QWidget::grab() p.drawTiledPixmap(0, 0, 128, 128, pal.brush(QPalette::Window).texture(), 0, 0); p.end(); - QPixmap actual = widget.grab(QRect(64, 64, 64, 64)); + QPixmap actual = grabFromWidget(&widget, QRect(64, 64, 64, 64)); QVERIFY(lenientCompare(actual, expected)); - actual = widget.grab(QRect(64, 64, -1, -1)); + actual = grabFromWidget(&widget, QRect(64, 64, -1, -1)); QVERIFY(lenientCompare(actual, expected)); // Make sure a widget that is not yet shown is grabbed correctly. |