From 4a04eea4f4316684e20c509352c6c533cf39306e Mon Sep 17 00:00:00 2001 From: David Faure Date: Thu, 1 Mar 2018 11:04:00 +0100 Subject: [PATCH 01/15] QHeaderView: fix inconsistent saved state, ignored during restore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code that updates a section size must also update length, otherwise saveState can end up saving inconsistent state, and restoreState() goes to an early-return, not doing anything. The actual bug was fixed meanwhile because _q_sectionsChanged is called again, which recalculates length. I still see this only as a safety measure, every other code path that changes section sizes updates length right away. Change-Id: I6cc16261692d93b3640afafef600a5bdff8dca0c Reviewed-by: Thorbjørn Lund Martsum --- src/widgets/itemviews/qheaderview.cpp | 6 ++- .../itemviews/qtreeview/tst_qtreeview.cpp | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/widgets/itemviews/qheaderview.cpp b/src/widgets/itemviews/qheaderview.cpp index 5cbf642802..b7048d1616 100644 --- a/src/widgets/itemviews/qheaderview.cpp +++ b/src/widgets/itemviews/qheaderview.cpp @@ -2191,7 +2191,11 @@ void QHeaderViewPrivate::_q_sectionsAboutToBeChanged(const QList= 0 && lastSectionLogicalIdx < sectionItems.count()) { const int visual = visualIndex(lastSectionLogicalIdx); if (visual >= 0 && visual < sectionItems.size()) { - sectionItems[visual].size = lastSectionSize; + auto &itemRef = sectionItems[visual]; + if (itemRef.size != lastSectionSize) { + length += lastSectionSize - itemRef.size; + itemRef.size = lastSectionSize; + } } } for (int i = 0; i < sectionItems.size(); ++i) { diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 5293ba487a..347d2a81e6 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -162,6 +162,7 @@ private slots: void renderToPixmap(); void styleOptionViewItem(); void keyboardNavigationWithDisabled(); + void saveRestoreState(); void statusTip_data(); void statusTip(); @@ -4076,6 +4077,58 @@ void tst_QTreeView::keyboardNavigationWithDisabled() QCOMPARE(view.currentIndex(), model.index(6, 0)); } +class RemoveColumnOne : public QSortFilterProxyModel +{ +public: + bool filterAcceptsColumn(int source_column, const QModelIndex &) const override + { + if (m_removeColumn) + return source_column != 1; + return true; + } + void removeColumn() + { + m_removeColumn = true; + invalidate(); + } +private: + bool m_removeColumn = false; +}; + + +void tst_QTreeView::saveRestoreState() +{ + QStandardItemModel model; + for (int i = 0; i < 100; i++) { + QList items; + items << new QStandardItem(QLatin1String("item ") + QString::number(i)) << new QStandardItem(QStringLiteral("hidden by proxy")) << new QStandardItem(QStringLiteral("hidden by user")); + model.appendRow(items); + } + QCOMPARE(model.columnCount(), 3); + + RemoveColumnOne proxy; + proxy.setSourceModel(&model); + QCOMPARE(proxy.columnCount(), 3); + + QTreeView view; + view.setModel(&proxy); + view.resize(500, 500); + view.show(); + view.header()->hideSection(2); + QVERIFY(view.header()->isSectionHidden(2)); + proxy.removeColumn(); + QCOMPARE(proxy.columnCount(), 2); + QVERIFY(view.header()->isSectionHidden(1)); + const QByteArray data = view.header()->saveState(); + + QTreeView view2; + view2.setModel(&proxy); + view2.resize(500, 500); + view2.show(); + view2.header()->restoreState(data); + QVERIFY(view2.header()->isSectionHidden(1)); +} + class Model_11466 : public QAbstractItemModel { Q_OBJECT From f7b8f2e8b51f244ed459d869747f5abc48aceeb0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 5 Mar 2018 13:15:11 +0100 Subject: [PATCH 02/15] Windows QPA: Fix sending of the mouse release in case of a Drop Use the new API of QWindowSystemInterface to send the release event which does not depend on the state of QGuiApplicationPrivate::mouse_buttons. Amends 1f6bd8bfb2206480ca5b5c267da38659e6cff20e. Task-number: QTBUG-66447 Change-Id: Iae889ea416b633c9307da9535dfb51ad1dbf288e Reviewed-by: Shawn Rutledge --- src/plugins/platforms/windows/qwindowsdrag.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index db06f81434..593ff3cef1 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -412,7 +412,8 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos); QWindowSystemInterface::handleMouseEvent(m_windowUnderMouse.data(), QPointF(localPos), QPointF(globalPos), - QWindowsMouseHandler::queryMouseButtons()); + QWindowsMouseHandler::queryMouseButtons(), + Qt::LeftButton, QEvent::MouseButtonRelease); } } m_currentButtons = Qt::NoButton; From 8abbb6ec725c41e08db8421f3e10b15b66ba28b1 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 2 Mar 2018 14:59:57 +0100 Subject: [PATCH 03/15] Windows QPA: Fix scaling of tablet coordinates for High DPI scaling Determine the virtual desktop geometry in native pixels from the QPlatformScreen siblings. Use the platform window to determine the local position. Task-number: QTBUG-36937 Change-Id: Ic250f1b41d8e74099558796112dbd70a7e1f6956 Reviewed-by: Shawn Rutledge --- src/plugins/platforms/windows/qwindowsscreen.cpp | 9 +++++++++ src/plugins/platforms/windows/qwindowsscreen.h | 2 ++ src/plugins/platforms/windows/qwindowstabletsupport.cpp | 7 +++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index c0781df973..66d3b140e2 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -318,6 +318,15 @@ void QWindowsScreen::handleChanges(const QWindowsScreenData &newData) } } +QRect QWindowsScreen::virtualGeometry(const QPlatformScreen *screen) // cf QScreen::virtualGeometry() +{ + QRect result; + const auto siblings = screen->virtualSiblings(); + for (const QPlatformScreen *sibling : siblings) + result |= sibling->geometry(); + return result; +} + enum OrientationPreference // matching Win32 API ORIENTATION_PREFERENCE #if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC) : DWORD diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index 7cf73f03af..5753e605da 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -113,6 +113,8 @@ public: const QWindowsScreenData &data() const { return m_data; } + static QRect virtualGeometry(const QPlatformScreen *screen); + private: QWindowsScreenData m_data; #ifndef QT_NO_CURSOR diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp index 5fdc664603..78e33e57ab 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -421,7 +421,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() // in which case we snap the position to the mouse position. // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext // area is always the virtual desktop. - const QRect virtualDesktopArea = QGuiApplication::primaryScreen()->virtualGeometry(); + const QRect virtualDesktopArea = + QWindowsScreen::virtualGeometry(QGuiApplication::primaryScreen()->handle()); qCDebug(lcQpaTablet) << __FUNCTION__ << "processing " << packetCount << "target:" << QGuiApplicationPrivate::tabletDevicePoint(uniqueId).target; @@ -455,7 +456,9 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() if (!target) continue; - const QPoint localPos = target->mapFromGlobal(globalPos); + const QPlatformWindow *platformWindow = target->handle(); + Q_ASSERT(platformWindow); + const QPoint localPos = platformWindow->mapFromGlobal(globalPos); const qreal pressureNew = packet.pkButtons && (currentPointer == QTabletEvent::Pen || currentPointer == QTabletEvent::Eraser) ? m_devices.at(m_currentDevice).scalePressure(packet.pkNormalPressure) : From f49c55a50546bbc3202fc92977ab539ceea3c087 Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Tue, 6 Mar 2018 14:20:07 +0200 Subject: [PATCH 04/15] Revert "Make qWaitForWindowActive more robust" This reverts commit 2eec3272c78a3af1e362906654aa6d50b33a4c50. The change to qWaitForWindowActive causes regressions in qtdeclarative, so we need a better fix for the problems we have with window waiting. Task-number: QTBUG-66866 Change-Id: I8300dabc870d6aeaa9ba6bfcf3d64146b13c1955 Reviewed-by: Sami Nurmenniemi Reviewed-by: Mitch Curtis --- src/testlib/qtestsystem.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testlib/qtestsystem.h b/src/testlib/qtestsystem.h index 046fe1a3e7..79fe68004e 100644 --- a/src/testlib/qtestsystem.h +++ b/src/testlib/qtestsystem.h @@ -114,7 +114,7 @@ namespace QTest { bool becameActive = qWaitFor([&]() { return window->isActive(); }, timeout); - // Try ensuring the platform window receives the real position and geometry. + // Try ensuring the platform window receives the real position. // (i.e. that window->pos() reflects reality) // isActive() ( == FocusIn in case of X) does not guarantee this. It seems some WMs randomly // send the final ConfigureNotify (the one with the non-bogus 0,0 position) after the FocusIn. @@ -122,7 +122,7 @@ namespace QTest // qWaitForWindowShown() will generate bogus results. if (becameActive) { int waitNo = 0; // 0, 0 might be a valid position after all, so do not wait for ever - while (window->frameGeometry().isNull()) { + while (window->position().isNull()) { if (waitNo++ > timeout / 10) break; qWait(10); From 45bace2ff99ba9b6c56087c1233092d50b6c49c7 Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Fri, 2 Mar 2018 16:43:15 +0200 Subject: [PATCH 05/15] QObjectBenchmark: Don't try to set read-only properties Avoid lots of warnings about not being able to set "modal". Change-Id: I396718f14a55203f9989c03e20efc647c64795a9 Reviewed-by: Olivier Goffart (Woboq GmbH) --- tests/benchmarks/corelib/kernel/qobject/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/corelib/kernel/qobject/main.cpp b/tests/benchmarks/corelib/kernel/qobject/main.cpp index 13c1439e43..c11b13b7ea 100644 --- a/tests/benchmarks/corelib/kernel/qobject/main.cpp +++ b/tests/benchmarks/corelib/kernel/qobject/main.cpp @@ -133,7 +133,8 @@ void QObjectBenchmark::qproperty_benchmark_data() const QMetaObject *mo = &QTreeView::staticMetaObject; for (int i = 0; i < mo->propertyCount(); ++i) { QMetaProperty prop = mo->property(i); - QTest::newRow(prop.name()) << QByteArray(prop.name()); + if (prop.isWritable()) + QTest::newRow(prop.name()) << QByteArray(prop.name()); } } From 3d53cf976dc41d136c0508ec05fa757fcf27f71b Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Mon, 5 Mar 2018 13:37:31 +0200 Subject: [PATCH 06/15] Blacklist tst_QWindow::modalWithChildWindow on openSUSE Task-number: QTBUG-66851 Task-number: QTBUG-66216 Change-Id: Iea3d7823ccef718b52d097298250efddc981f3bb Reviewed-by: Friedemann Kleint --- tests/auto/gui/kernel/qwindow/BLACKLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST index 5c20e5fd43..734e928f7d 100644 --- a/tests/auto/gui/kernel/qwindow/BLACKLIST +++ b/tests/auto/gui/kernel/qwindow/BLACKLIST @@ -7,6 +7,8 @@ osx-10.12 ci ubuntu-16.04 [modalWithChildWindow] ubuntu-16.04 +# QTBUG-66851 +opensuse [setVisible] [modalWindowEnterEventOnHide_QTBUG35109] ubuntu-16.04 From 67d5f79fe6f86726eff0461bd6f3bb928801723c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 8 Feb 2018 12:51:27 +0100 Subject: [PATCH 07/15] logging: Clarify and document how we look for the presence of a console The privately exported qt_logging_to_console() function has been renamed to shouldLogToStderr, and exported in the QtPrivate namespace for QtTestLib. [ChangeLog][Important behavior changes][Logging (including qDebug and qWarning)] The QT_LOGGING_TO_CONSOLE environment variable has been deprecated. Use the more specific QT_ASSUME_STDERR_HAS_CONSOLE or QT_FORCE_STDERR_LOGGING, depending on your usecase. Change-Id: Ie29228eeac3b700c3de94fee022d5d66d9b5c210 Reviewed-by: Simon Hausmann --- src/corelib/configure.json | 2 +- src/corelib/global/qlogging.cpp | 153 ++++++++++++++++++++----------- src/corelib/global/qlogging_p.h | 64 +++++++++++++ src/testlib/qplaintestlogger.cpp | 10 +- 4 files changed, 169 insertions(+), 60 deletions(-) create mode 100644 src/corelib/global/qlogging_p.h diff --git a/src/corelib/configure.json b/src/corelib/configure.json index 7c3b513b0b..5e48024def 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -909,7 +909,7 @@ Mozilla License) is included. The data is then also used in QNetworkCookieJar::v "condition": "features.journald || features.syslog || (config.qnx && features.slog2)", "message": "journald, syslog or slog2 integration is enabled. If your users intend to develop applications against this build, -ensure that the IDEs they use either set QT_LOGGING_TO_CONSOLE to 1 +ensure that the IDEs they use either set QT_FORCE_STDERR_LOGGING to 1 or are able to read the logged output from journald, syslog or slog2." }, { diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index 9f1fe7cabb..99c57c3b7a 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -41,6 +41,7 @@ #include "qglobal_p.h" #include "qlogging.h" +#include "qlogging_p.h" #include "qlist.h" #include "qbytearray.h" #include "qstring.h" @@ -204,56 +205,102 @@ static bool isDefaultCategory(const char *category) return !category || strcmp(category, "default") == 0; } -static bool willLogToConsole() +/*! + Returns true if writing to \c stderr is supported. + + \sa stderrHasConsoleAttached() +*/ +static bool systemHasStderr() { #if defined(Q_OS_WINRT) - // these systems have no stderr, so always log to the system log - return false; -#else - // rules to determine if we'll log preferably to the console: - // 1) if QT_LOGGING_TO_CONSOLE is set, it determines behavior: - // - if it's set to 0, we will not log to console - // - if it's set to 1, we will log to console - // 2) otherwise, we will log to console if we have a console window (Windows) - // or a controlling TTY (Unix). This is done even if stderr was redirected - // to the blackhole device (NUL or /dev/null). - - bool ok = true; - uint envcontrol = qgetenv("QT_LOGGING_TO_CONSOLE").toUInt(&ok); - if (ok) - return envcontrol; - -# ifdef Q_OS_WIN - return GetConsoleWindow(); -# elif defined(Q_OS_UNIX) -# ifndef _PATH_TTY -# define _PATH_TTY "/dev/tty" -# endif - // if /dev/tty exists, we can only open it if we have a controlling TTY - int devtty = qt_safe_open(_PATH_TTY, O_RDONLY); - if (devtty == -1 && (errno == ENOENT || errno == EPERM || errno == ENXIO)) { - // no /dev/tty, fall back to isatty on stderr - return isatty(STDERR_FILENO); - } else if (devtty != -1) { - // there is a /dev/tty and we could open it: we have a controlling TTY - qt_safe_close(devtty); - return true; - } - - // no controlling TTY - return false; -# else -# error "Not Unix and not Windows?" -# endif + return false; // WinRT has no stderr #endif + + return true; } -Q_CORE_EXPORT bool qt_logging_to_console() +/*! + Returns true if writing to \c stderr will end up in a console/terminal visible to the user. + + This is typically the case if the application was started from the command line. + + If the application is started without a controlling console/terminal, but the parent + process reads \c stderr and presents it to the user in some other way, the parent process + may override the detection in this function by setting the QT_ASSUME_STDERR_HAS_CONSOLE + environment variable to \c 1. + + \note Qt Creator does not implement a pseudo TTY, nor does it launch apps with + the override environment variable set, but it will read stderr and print it to + the user, so in effect this function can not be used to conclude that stderr + output will _not_ be visible to the user, as even if this function returns false, + the output might still end up visible to the user. For this reason, we don't guard + the stderr output in the default message handler with stderrHasConsoleAttached(). + + \sa systemHasStderr() +*/ +bool stderrHasConsoleAttached() { - static const bool logToConsole = willLogToConsole(); - return logToConsole; + static const bool stderrHasConsoleAttached = []() -> bool { + if (!systemHasStderr()) + return false; + + if (qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) { + fprintf(stderr, "warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use\n" + "QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.\n"); + return true; + } + + if (qEnvironmentVariableIntValue("QT_ASSUME_STDERR_HAS_CONSOLE")) + return true; + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + return GetConsoleWindow(); +#elif defined(Q_OS_UNIX) +# ifndef _PATH_TTY +# define _PATH_TTY "/dev/tty" +# endif + + // If we can open /dev/tty, we have a controlling TTY + int ttyDevice = -1; + if ((ttyDevice = qt_safe_open(_PATH_TTY, O_RDONLY)) >= 0) { + qt_safe_close(ttyDevice); + return true; + } else if (errno == ENOENT || errno == EPERM || errno == ENXIO) { + // Fall back to isatty for some non-critical errors + return isatty(STDERR_FILENO); + } else { + return false; + } +#else + return false; // No way to detect if stderr has a console attached +#endif + }(); + + return stderrHasConsoleAttached; } + +namespace QtPrivate { + +/*! + Returns true if logging \c stderr should be ensured. + + This is normally the case if \c stderr has a console attached, but may be overridden + by the user by setting the QT_FORCE_STDERR_LOGGING environment variable to \c 1. + + \sa stderrHasConsoleAttached() +*/ +bool shouldLogToStderr() +{ + static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING"); + return forceStderrLogging || stderrHasConsoleAttached(); +} + + +} // QtPrivate + +using namespace QtPrivate; + /*! \class QMessageLogContext \inmodule QtCore @@ -1472,8 +1519,8 @@ static QBasicAtomicPointer(message.utf16())); return; #elif defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) - if (!qt_logging_to_console()) { + if (!shouldLogToStderr()) { OutputDebugString(reinterpret_cast(message.utf16())); return; } diff --git a/src/corelib/global/qlogging_p.h b/src/corelib/global/qlogging_p.h new file mode 100644 index 0000000000..3d0c097ffe --- /dev/null +++ b/src/corelib/global/qlogging_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOGGING_P_H +#define QLOGGING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + +Q_CORE_EXPORT bool shouldLogToStderr(); + +} + +QT_END_NAMESPACE + +#endif // QLOGGING_P_H diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp index a6797012c7..e5226b7820 100644 --- a/src/testlib/qplaintestlogger.cpp +++ b/src/testlib/qplaintestlogger.cpp @@ -44,6 +44,8 @@ #include #include +#include + #include #include #include @@ -205,15 +207,11 @@ namespace QTest { } } -#if defined(Q_OS_WIN) -Q_CORE_EXPORT bool qt_logging_to_console(); // defined in qlogging.cpp -#endif - void QPlainTestLogger::outputMessage(const char *str) { #if defined(Q_OS_WIN) - // log to system log only if output is not redirected, and no console is attached - if (!qt_logging_to_console() && stream == stdout) { + // Log to system log only if output is not redirected and stderr not preferred + if (stream == stdout && !QtPrivate::shouldLogToStderr()) { OutputDebugStringA(str); return; } From 70cfe551b285b4a3e770aabbca8f8bdcfadffb09 Mon Sep 17 00:00:00 2001 From: Marcus Calhoun-Lopez Date: Sun, 22 Nov 2015 10:24:04 -0700 Subject: [PATCH 08/15] Apple: resolve SDK version of QMAKE_LINK_C and QMAKE_LINK_C_SHLIB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Apple OSes, both compilers and linkers are given an absolute path. For consistency, the same should be done with the C linkers. The change is also a convenience to the MacPorts project, which actively discourages ambiguous compiler names. (https://trac.macports.org/wiki/UsingTheRightCompiler). Change-Id: Ic1885aed825340696e9fde766788eebf51de3ff6 Reviewed-by: Tor Arne Vestbø --- mkspecs/features/mac/sdk.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkspecs/features/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index 3f6dc076ca..bbcad8125c 100644 --- a/mkspecs/features/mac/sdk.prf +++ b/mkspecs/features/mac/sdk.prf @@ -40,7 +40,7 @@ QMAKE_INCDIR_OPENGL = $$sysrootified QMAKESPEC_NAME = $$basename(QMAKESPEC) # Resolve SDK version of various tools -for(tool, $$list(QMAKE_CC QMAKE_CXX QMAKE_FIX_RPATH QMAKE_AR QMAKE_RANLIB QMAKE_LINK QMAKE_LINK_SHLIB QMAKE_ACTOOL)) { +for(tool, $$list(QMAKE_CC QMAKE_CXX QMAKE_FIX_RPATH QMAKE_AR QMAKE_RANLIB QMAKE_LINK QMAKE_LINK_SHLIB QMAKE_ACTOOL QMAKE_LINK_C QMAKE_LINK_C_SHLIB)) { tool_variable = QMAKE_MAC_SDK.$${QMAKESPEC_NAME}.$${QMAKE_MAC_SDK}.$${tool} !isEmpty($$tool_variable) { $$tool = $$eval($$tool_variable) From 3185b40d5de1092ed2bdd83f72478a344c5fc9e9 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Wed, 7 Feb 2018 20:12:33 +0100 Subject: [PATCH 09/15] SQL/Postgres: Fix support for nan, inf and -inf Postgresql needs a special value for nan and +/- inf. This was considered during insert but not during select. Also remove some pre-c++11 inf/nan - handling and replace it with Qt equivalents. Change-Id: I044ca58e9cf673f4b100b05a0d8e25c8a9c29ec5 Reviewed-by: Robert Szefner Reviewed-by: Andy Shaw --- src/plugins/sqldrivers/psql/qsql_psql.cpp | 51 +++++-------------- .../kernel/qsqldatabase/tst_qsqldatabase.cpp | 42 +++++++++++++++ 2 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/plugins/sqldrivers/psql/qsql_psql.cpp b/src/plugins/sqldrivers/psql/qsql_psql.cpp index f67c78b2bb..b4eb69e6cf 100644 --- a/src/plugins/sqldrivers/psql/qsql_psql.cpp +++ b/src/plugins/sqldrivers/psql/qsql_psql.cpp @@ -57,29 +57,7 @@ #include #include -#include -#include -// below code taken from an example at http://www.gnu.org/software/hello/manual/autoconf/Function-Portability.html -#ifndef isnan - # define isnan(x) \ - (sizeof (x) == sizeof (long double) ? isnan_ld (x) \ - : sizeof (x) == sizeof (double) ? isnan_d (x) \ - : isnan_f (x)) - static inline int isnan_f (float x) { return x != x; } - static inline int isnan_d (double x) { return x != x; } - static inline int isnan_ld (long double x) { return x != x; } -#endif - -#ifndef isinf - # define isinf(x) \ - (sizeof (x) == sizeof (long double) ? isinf_ld (x) \ - : sizeof (x) == sizeof (double) ? isinf_d (x) \ - : isinf_f (x)) - static inline int isinf_f (float x) { return isnan (x - x); } - static inline int isinf_d (double x) { return isnan (x - x); } - static inline int isinf_ld (long double x) { return isnan (x - x); } -#endif - +#include // workaround for postgres defining their OIDs in a private header file #define QBOOLOID 16 @@ -135,7 +113,7 @@ static const StatementId InvalidStatementId = 0; class QPSQLResultPrivate; -class QPSQLResult: public QSqlResult +class QPSQLResult final : public QSqlResult { Q_DECLARE_PRIVATE(QPSQLResult) @@ -164,7 +142,7 @@ protected: bool exec() override; }; -class QPSQLDriverPrivate : public QSqlDriverPrivate +class QPSQLDriverPrivate final : public QSqlDriverPrivate { Q_DECLARE_PUBLIC(QPSQLDriver) public: @@ -671,7 +649,7 @@ QVariant QPSQLResult::data(int i) return QString::fromLatin1(val).toULongLong(); case QVariant::Int: return atoi(val); - case QVariant::Double: + case QVariant::Double: { if (ptype == QNUMERICOID) { if (numericalPrecisionPolicy() != QSql::HighPrecision) { QVariant retval; @@ -689,7 +667,12 @@ QVariant QPSQLResult::data(int i) } return QString::fromLatin1(val); } + if (qstricmp(val, "Infinity") == 0) + return qInf(); + if (qstricmp(val, "-Infinity") == 0) + return -qInf(); return QString::fromLatin1(val).toDouble(); + } case QVariant::Date: if (val[0] == '\0') { return QVariant(QDate()); @@ -1497,18 +1480,10 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const template inline void assignSpecialPsqlFloatValue(FloatType val, QString *target) { - if (isnan(val)) { - *target = QLatin1String("'NaN'"); - } else { - switch (isinf(val)) { - case 1: - *target = QLatin1String("'Infinity'"); - break; - case -1: - *target = QLatin1String("'-Infinity'"); - break; - } - } + if (qIsNaN(val)) + *target = QStringLiteral("'NaN'"); + else if (qIsInf(val)) + *target = (val < 0) ? QStringLiteral("'-Infinity'") : QStringLiteral("'Infinity'"); } QString QPSQLDriver::formatValue(const QSqlField &field, bool trimStrings) const diff --git a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp index 427bc092a5..89978319a0 100644 --- a/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp +++ b/tests/auto/sql/kernel/qsqldatabase/tst_qsqldatabase.cpp @@ -126,6 +126,8 @@ private slots: void formatValueTrimStrings(); void precisionPolicy_data() { generic_data(); } void precisionPolicy(); + void infinityAndNan_data() { generic_data(); } + void infinityAndNan(); void multipleThreads_data() { generic_data(); } void multipleThreads(); @@ -1467,6 +1469,46 @@ void tst_QSqlDatabase::precisionPolicy() db.setNumericalPrecisionPolicy(oldPrecision); } +void tst_QSqlDatabase::infinityAndNan() +{ + QFETCH(QString, dbName); + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + + if (tst_Databases::getDatabaseType(db) != QSqlDriver::PostgreSQL) + QSKIP("checking for infinity/nan currently only works for PostgreSQL"); + + QSqlQuery q(db); + const QString tableName(qTableName("infititytest", __FILE__, db)); + tst_Databases::safeDropTables(db, {tableName}); + QVERIFY_SQL(q, exec(QString("CREATE TABLE %1 (id smallint, val double precision)").arg(tableName))); + + QVERIFY_SQL(q, prepare(QString("INSERT INTO %1 VALUES (?, ?)").arg(tableName))); + + q.bindValue(0, 1); + q.bindValue(1, qQNaN()); + QVERIFY_SQL(q, exec()); + q.bindValue(0, 2); + q.bindValue(1, qInf()); + QVERIFY_SQL(q, exec()); + q.bindValue(0, 3); + q.bindValue(1, -qInf()); + QVERIFY_SQL(q, exec()); + + QVERIFY_SQL(q, exec(QString("SELECT val FROM %1 ORDER BY id").arg(tableName))); + + QVERIFY_SQL(q, next()); + QVERIFY(qIsNaN(q.value(0).toDouble())); + + QVERIFY_SQL(q, next()); + QVERIFY(qIsInf(q.value(0).toDouble())); + QVERIFY(q.value(0).toDouble() > 0); + + QVERIFY_SQL(q, next()); + QVERIFY(qIsInf(q.value(0).toDouble())); + QVERIFY(q.value(0).toDouble() < 0); +} + // This test needs a ODBC data source containing MYSQL in it's name void tst_QSqlDatabase::mysqlOdbc_unsignedIntegers() { From b64f3f6ca97e299176ca8990402650552a90f704 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sat, 17 Feb 2018 20:46:33 +0100 Subject: [PATCH 10/15] QTabWidget: Do not add tabbar size during sizeHint() when it is hidden Since Qt 5.4 the QTabBar can be automatically hidden when it has less then 2 tabs. Therefore the sizeHint should not consider the tabbars size when the tabbar is hidden. Task-number: QTBUG-64715 Change-Id: I2f248f88d9070de5354f7344c7628a78442ab499 Reviewed-by: Friedemann Kleint Reviewed-by: Richard Moe Gustavsen --- src/widgets/widgets/qtabwidget.cpp | 36 ++++++++++++------- .../widgets/qtabwidget/tst_qtabwidget.cpp | 29 +++++++++++++++ 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/widgets/widgets/qtabwidget.cpp b/src/widgets/widgets/qtabwidget.cpp index 60a924510a..894053ec19 100644 --- a/src/widgets/widgets/qtabwidget.cpp +++ b/src/widgets/widgets/qtabwidget.cpp @@ -195,6 +195,11 @@ public: void _q_removeTab(int); void _q_tabMoved(int from, int to); void init(); + bool isAutoHidden() const + { + // see QTabBarPrivate::autoHideTabs() + return (tabs->autoHide() && tabs->count() <= 1); + } void initBasicStyleOption(QStyleOptionTabWidgetFrame *option) const; @@ -841,11 +846,14 @@ QSize QTabWidget::sizeHint() const that->setUpLayout(true); } QSize s(d->stack->sizeHint()); - QSize t(d->tabs->sizeHint()); - if(usesScrollButtons()) - t = t.boundedTo(QSize(200,200)); - else - t = t.boundedTo(QDesktopWidgetPrivate::size()); + QSize t; + if (!d->isAutoHidden()) { + t = d->tabs->sizeHint(); + if (usesScrollButtons()) + t = t.boundedTo(QSize(200,200)); + else + t = t.boundedTo(QDesktopWidgetPrivate::size()); + } QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t); @@ -873,7 +881,9 @@ QSize QTabWidget::minimumSizeHint() const that->setUpLayout(true); } QSize s(d->stack->minimumSizeHint()); - QSize t(d->tabs->minimumSizeHint()); + QSize t; + if (!d->isAutoHidden()) + t = d->tabs->minimumSizeHint(); QSize sz = basicSize(d->pos == North || d->pos == South, lc, rc, s, t); @@ -908,12 +918,14 @@ int QTabWidget::heightForWidth(int width) const QTabWidget *that = const_cast(this); that->setUpLayout(true); } - QSize t(d->tabs->sizeHint()); - - if(usesScrollButtons()) - t = t.boundedTo(QSize(200,200)); - else - t = t.boundedTo(QDesktopWidgetPrivate::size()); + QSize t; + if (!d->isAutoHidden()) { + t = d->tabs->sizeHint(); + if (usesScrollButtons()) + t = t.boundedTo(QSize(200,200)); + else + t = t.boundedTo(QDesktopWidgetPrivate::size()); + } const bool tabIsHorizontal = (d->pos == North || d->pos == South); const int contentsWidth = width - padding.width(); diff --git a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp index 2a2331c3bf..b5ffb90e42 100644 --- a/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp +++ b/tests/auto/widgets/widgets/qtabwidget/tst_qtabwidget.cpp @@ -98,6 +98,7 @@ private slots: void heightForWidth(); void tabBarClicked(); void moveCurrentTab(); + void autoHide(); private: int addPage(); @@ -713,5 +714,33 @@ void tst_QTabWidget::moveCurrentTab() QCOMPARE(tabWidget.currentWidget(), secondTab); } +void tst_QTabWidget::autoHide() +{ + QTabWidget tabWidget; + QWidget* firstTab = new QWidget(&tabWidget); + tabWidget.addTab(firstTab, "0"); + const auto sizeHint1 = tabWidget.sizeHint(); + const auto minSizeHint1 = tabWidget.minimumSizeHint(); + const auto heightForWidth1 = tabWidget.heightForWidth(20); + + QWidget* secondTab = new QWidget(&tabWidget); + tabWidget.addTab(secondTab, "1"); + const auto sizeHint2 = tabWidget.sizeHint(); + const auto minSizeHint2 = tabWidget.minimumSizeHint(); + const auto heightForWidth2 = tabWidget.heightForWidth(20); + + tabWidget.setTabBarAutoHide(true); + // size should not change + QCOMPARE(sizeHint2, tabWidget.sizeHint()); + QCOMPARE(minSizeHint2, tabWidget.minimumSizeHint()); + QCOMPARE(heightForWidth2, tabWidget.heightForWidth(20)); + + tabWidget.removeTab(1); + // this size should change now since the tab should be hidden + QVERIFY(sizeHint1.height() > tabWidget.sizeHint().height()); + QVERIFY(minSizeHint1.height() > tabWidget.sizeHint().height()); + QVERIFY(heightForWidth1 > tabWidget.heightForWidth(20)); +} + QTEST_MAIN(tst_QTabWidget) #include "tst_qtabwidget.moc" From 80693f171c9e904007137f109a83fb456c9cae48 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Fri, 23 Feb 2018 21:23:49 +0100 Subject: [PATCH 11/15] QStandardItem: add more sanity checks on insertRow(s)/Column(s) When QStandardItem::insertRows/Columns is called with an empty list or an insert count of 0, do not assert but return false. Task-number: QTBUG-66491 Change-Id: I328598e08584fb9b0a35075458bfeb31c57ebfee Reviewed-by: Samuel Gaist Reviewed-by: Richard Moe Gustavsen --- src/gui/itemmodels/qstandarditemmodel.cpp | 6 +++--- .../qstandarditemmodel/tst_qstandarditemmodel.cpp | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index d1e0604caf..050c9a662b 100644 --- a/src/gui/itemmodels/qstandarditemmodel.cpp +++ b/src/gui/itemmodels/qstandarditemmodel.cpp @@ -455,7 +455,7 @@ void QStandardItemModelPrivate::_q_emitItemChanged(const QModelIndex &topLeft, bool QStandardItemPrivate::insertRows(int row, const QList &items) { Q_Q(QStandardItem); - if ((row < 0) || (row > rowCount())) + if ((row < 0) || (row > rowCount()) || items.isEmpty()) return false; int count = items.count(); if (model) @@ -486,7 +486,7 @@ bool QStandardItemPrivate::insertRows(int row, const QList &item bool QStandardItemPrivate::insertRows(int row, int count, const QList &items) { Q_Q(QStandardItem); - if ((count < 1) || (row < 0) || (row > rowCount())) + if ((count < 1) || (row < 0) || (row > rowCount()) || count == 0) return false; if (model) model->d_func()->rowsAboutToBeInserted(q, row, row + count - 1); @@ -528,7 +528,7 @@ bool QStandardItemPrivate::insertRows(int row, int count, const QList &items) { Q_Q(QStandardItem); - if ((count < 1) || (column < 0) || (column > columnCount())) + if ((count < 1) || (column < 0) || (column > columnCount()) || count == 0) return false; if (model) model->d_func()->columnsAboutToBeInserted(q, column, column + count - 1); diff --git a/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp b/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp index d393ac70da..1ecfdb5e47 100644 --- a/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp +++ b/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp @@ -132,7 +132,7 @@ private slots: void taskQTBUG_45114_setItemData(); private: - QAbstractItemModel *m_model; + QStandardItemModel *m_model; QPersistentModelIndex persistent; QVector rcParent; QVector rcFirst; @@ -292,6 +292,12 @@ void tst_QStandardItemModel::insertRows() // check header data has moved QCOMPARE(m_model->headerData(3, Qt::Vertical).toString(), headerLabel); + + // do not assert on empty list + QStandardItem *si = m_model->invisibleRootItem(); + si->insertRow(0, QList()); + si->insertRows(0, 0); + si->insertRows(0, QList()); } void tst_QStandardItemModel::insertRowsItems() @@ -402,6 +408,11 @@ void tst_QStandardItemModel::insertColumns() // check header data has moved QCOMPARE(m_model->headerData(3, Qt::Horizontal).toString(), headerLabel); + + // do not assert on empty list + QStandardItem *si = m_model->invisibleRootItem(); + si->insertColumn(0, QList()); + si->insertColumns(0, 0); } void tst_QStandardItemModel::removeRows() From 808adeb7bc53208a1c30f425fce6359a17f00170 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Sun, 4 Mar 2018 20:44:37 +0100 Subject: [PATCH 12/15] QSqlField: rearrange QSqlFieldPrivate members Rearrange QSqlFieldPrivate members to only need 64Byte instead 72 in an 64bit environment. Change-Id: I620c18aca06a11b7ab4bd8e99b377b9d823da7f4 Reviewed-by: Andy Shaw --- src/sql/kernel/qsqlfield.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sql/kernel/qsqlfield.cpp b/src/sql/kernel/qsqlfield.cpp index 782ab0d71c..5a6d173b3b 100644 --- a/src/sql/kernel/qsqlfield.cpp +++ b/src/sql/kernel/qsqlfield.cpp @@ -48,52 +48,52 @@ class QSqlFieldPrivate public: QSqlFieldPrivate(const QString &name, QVariant::Type type, const QString &tableName) : - ref(1), nm(name), ro(false), type(type), req(QSqlField::Unknown), - len(-1), prec(-1), tp(-1), gen(true), autoval(false), table(tableName) - { - } + ref(1), nm(name), table(tableName), def(QVariant()), type(type), + req(QSqlField::Unknown), len(-1), prec(-1), tp(-1), + ro(false), gen(true), autoval(false) + {} QSqlFieldPrivate(const QSqlFieldPrivate &other) : ref(1), nm(other.nm), - ro(other.ro), + table(other.table), + def(other.def), type(other.type), req(other.req), len(other.len), prec(other.prec), - def(other.def), tp(other.tp), + ro(other.ro), gen(other.gen), - autoval(other.autoval), - table(other.table) + autoval(other.autoval) {} bool operator==(const QSqlFieldPrivate& other) const { return (nm == other.nm - && ro == other.ro + && table == other.table + && def == other.def && type == other.type && req == other.req && len == other.len && prec == other.prec - && def == other.def + && ro == other.ro && gen == other.gen - && autoval == other.autoval - && table == other.table); + && autoval == other.autoval); } QAtomicInt ref; QString nm; - uint ro: 1; + QString table; + QVariant def; QVariant::Type type; QSqlField::RequiredStatus req; int len; int prec; - QVariant def; int tp; - uint gen: 1; - uint autoval: 1; - QString table; + bool ro: 1; + bool gen: 1; + bool autoval: 1; }; From 4dee5446bee9c7417bf6f5dc44294a0b7238a9ba Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 5 Mar 2018 20:37:42 +0100 Subject: [PATCH 13/15] Add missing rvalue overloads of operator+=() and operator<<() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They were forgotten when the overloads for append()/push_back() were added in Qt 5.6 [ChangeLog][QtCore][QVarLengthArray] Added missing rvalue overload of operator+=() and operator<<() [ChangeLog][QtCore][QVector] Added missing rvalue overload of operator+=() and operator<<() Change-Id: I20fedfba2bf282773bd1f9cf2c8ec06f05896a7d Reviewed-by: Martin Smith Reviewed-by: Mårten Nordheim --- src/corelib/tools/qvarlengtharray.h | 4 ++++ src/corelib/tools/qvarlengtharray.qdoc | 16 ++++++++++++++++ src/corelib/tools/qvector.h | 4 ++++ src/corelib/tools/qvector.qdoc | 17 +++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index d0088471c7..bc22ea26f6 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -175,8 +175,12 @@ public: void append(const T *buf, int size); inline QVarLengthArray &operator<<(const T &t) { append(t); return *this; } + inline QVarLengthArray &operator<<(T &&t) + { append(t); return *this; } inline QVarLengthArray &operator+=(const T &t) { append(t); return *this; } + inline QVarLengthArray &operator+=(T &&t) + { append(t); return *this; } void prepend(T &&t); void prepend(const T &t); diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index 336f2afaca..bc8df82517 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -832,6 +832,14 @@ \sa append(), operator+=() */ +/*! \fn template QVarLengthArray &QVarLengthArray::operator<<(T &&value) + \since 5.11 + + \overload + + \sa append(), operator+=() +*/ + /*! \fn template QVarLengthArray &QVarLengthArray::operator+=(const T &value) \since 4.8 @@ -840,6 +848,14 @@ \sa append(), operator<<() */ +/*! \fn template QVarLengthArray &QVarLengthArray::operator+=(T &&value) + \since 5.11 + + \overload + + \sa append(), operator<<() +*/ + /*! \fn template int QVarLengthArray::indexOf(const T &value, int from = 0) const \since 5.3 diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index a01d5c8f3c..e4c7fd06d8 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -283,6 +283,10 @@ public: { append(t); return *this; } inline QVector &operator<<(const QVector &l) { *this += l; return *this; } + inline QVector &operator+=(T &&t) + { append(t); return *this; } + inline QVector &operator<<(T &&t) + { append(t); return *this; } QList toList() const; diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index cc250b72a5..9a08b030f7 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -1162,6 +1162,14 @@ \sa append(), operator<<() */ +/*! \fn template void QVector::operator+=(T &&value) + \since 5.11 + + \overload + + \sa append(), operator<<() +*/ + /*! \fn template QVector QVector::operator+(const QVector &other) const Returns a vector that contains all the items in this vector @@ -1178,6 +1186,15 @@ \sa append(), operator+=() */ +/*! \fn template QVector &QVector::operator<<(T &&value) + \since 5.11 + + \overload + + \sa append(), operator+=() +*/ + + /*! \fn template QVector &QVector::operator<<(const QVector &other) Appends \a other to the vector and returns a reference to the From e16c6dfb7286264ccdfd37d8e74c97f13ece7835 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Tue, 27 Feb 2018 10:43:48 -0800 Subject: [PATCH 14/15] QCocoaMenuItem: Make QCocoaNSMenu the item target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we made the NSMenu delegate a singleton we could not rely on it to have enough information to implement worksWhenModal. At the same time as the delegate change, we derived NSMenu into QCocoaNSMenu. This allows us to extend the menu functionality and, in this case, serve as target for the Cocoa menu items. We also refactor setting the item's target/action. Manually tested against menurama and bigmenucreator tests, the test-case for QTBUG-17291, and the richtext/textedit example. Change-Id: I222241f71db82611711b23d4a8c6122a741370ae Task-number: QTBUG-66676 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoamenu.h | 2 + src/plugins/platforms/cocoa/qcocoamenu.mm | 10 ++- src/plugins/platforms/cocoa/qcocoamenubar.mm | 6 +- src/plugins/platforms/cocoa/qcocoansmenu.h | 7 +- src/plugins/platforms/cocoa/qcocoansmenu.mm | 75 ++++++++++---------- 5 files changed, 54 insertions(+), 46 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index 2b4cf0ef98..6db4e04c61 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -96,6 +96,8 @@ public: void syncMenuItem_helper(QPlatformMenuItem *menuItem, bool menubarUpdate); + void setItemTargetAction(QCocoaMenuItem *item) const; + private: QCocoaMenuItem *itemOrNull(int index) const; void insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem); diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index b3c2d5ae90..a788ac7d45 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -132,9 +132,8 @@ void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem * void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem) { + setItemTargetAction(item); NSMenuItem *nativeItem = item->nsItem(); - nativeItem.target = m_nativeMenu.delegate; - nativeItem.action = @selector(itemFired:); // Someone's adding new items after aboutToShow() was emitted if (isOpen() && nativeItem && item->menu()) item->menu()->setAttachedItem(nativeItem); @@ -496,4 +495,11 @@ NSMenuItem *QCocoaMenu::attachedItem() const return m_attachedItem; } +void QCocoaMenu::setItemTargetAction(QCocoaMenuItem *item) const +{ + auto *nsItem = item->nsItem(); + nsItem.target = m_nativeMenu; + nsItem.action = @selector(qt_itemFired:); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 8091d00bda..61ac5eb7f0 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -307,14 +307,12 @@ void QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder() void QCocoaMenuBar::resetKnownMenuItemsToQt() { // Undo the effect of redirectKnownMenuItemsToFirstResponder(): - // set the menu items' actions to itemFired and their targets to - // the QCocoaMenuDelegate. + // reset the menu items' target/action. foreach (QCocoaMenuBar *mb, static_menubars) { foreach (QCocoaMenu *m, mb->m_menus) { foreach (QCocoaMenuItem *i, m->items()) { if (i->effectiveRole() >= QPlatformMenuItem::ApplicationSpecificRole) { - [i->nsItem() setTarget:m->nsMenu().delegate]; - [i->nsItem() setAction:@selector(itemFired:)]; + m->setItemTargetAction(i); } } } diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.h b/src/plugins/platforms/cocoa/qcocoansmenu.h index 8fb0a26f27..a9c3e4fff9 100644 --- a/src/plugins/platforms/cocoa/qcocoansmenu.h +++ b/src/plugins/platforms/cocoa/qcocoansmenu.h @@ -68,8 +68,6 @@ typedef QPointer QCocoaMenuPointer; forKey:(NSString *)key modifiers:(NSUInteger)modifiers; -- (BOOL)validateMenuItem:(NSMenuItem *)item; // NSMenuValidation - @end @interface QT_MANGLE_NAMESPACE(QCocoaNSMenu) : NSMenu @@ -78,6 +76,11 @@ typedef QPointer QCocoaMenuPointer; - (instancetype)initWithQPAMenu:(QCocoaMenu *)menu; +- (void)qt_itemFired:(NSMenuItem *)item; + +- (BOOL)worksWhenModal; +- (BOOL)validateMenuItem:(NSMenuItem*)item; // NSMenuValidation + @end QT_NAMESPACE_ALIAS_OBJC_CLASS(QCocoaNSMenu); diff --git a/src/plugins/platforms/cocoa/qcocoansmenu.mm b/src/plugins/platforms/cocoa/qcocoansmenu.mm index 6be2569dbc..19a0f950e4 100644 --- a/src/plugins/platforms/cocoa/qcocoansmenu.mm +++ b/src/plugins/platforms/cocoa/qcocoansmenu.mm @@ -82,6 +82,43 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string) return self; } +// Cocoa will query the menu item's target for the worksWhenModal selector. +// So we need to implement this to allow the items to be handled correctly +// when a modal dialog is visible. See documentation for NSMenuItem.target. +- (BOOL)worksWhenModal +{ + if (!QGuiApplication::modalWindow()) + return YES; + if (const auto *mb = qobject_cast(self.qpaMenu->menuParent())) + return QGuiApplication::modalWindow()->handle() == mb->cocoaWindow() ? YES : NO; + return YES; +} + +- (void)qt_itemFired:(NSMenuItem *)item +{ + auto *qpaItem = reinterpret_cast(item.tag); + // Menu-holding items also get a target to play nicely + // with NSMenuValidation but should not trigger. + if (!qpaItem || qpaItem->menu()) + return; + + QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); + QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; + + static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); + activatedSignal.invoke(qpaItem, Qt::QueuedConnection); +} + +- (BOOL)validateMenuItem:(NSMenuItem*)item +{ + auto *qpaItem = reinterpret_cast(item.tag); + // Menu-holding items are always enabled, as it's conventional in Cocoa + if (!qpaItem || qpaItem->menu()) + return YES; + + return qpaItem->isEnabled(); +} + @end #define CHECK_MENU_CLASS(menu) Q_ASSERT([menu isMemberOfClass:[QCocoaNSMenu class]]) @@ -160,31 +197,6 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string) emit qpaMenu->aboutToHide(); } -- (void)itemFired:(NSMenuItem *)item -{ - auto *qpaItem = reinterpret_cast(item.tag); - // Menu-holding items also get a target to play nicely - // with NSMenuValidation but should not trigger. - if (!qpaItem || qpaItem->menu()) - return; - - QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); - QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; - - static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); - activatedSignal.invoke(qpaItem, Qt::QueuedConnection); -} - -- (BOOL)validateMenuItem:(NSMenuItem*)item -{ - auto *qpaItem = reinterpret_cast(item.tag); - // Menu-holding items are always enabled, as it's conventional in Cocoa - if (!qpaItem || qpaItem->menu()) - return YES; - - return qpaItem->isEnabled(); -} - - (BOOL)menuHasKeyEquivalent:(NSMenu *)menu forEvent:(NSEvent *)event target:(id *)target action:(SEL *)action { /* @@ -293,19 +305,6 @@ static NSString *qt_mac_removePrivateUnicode(NSString* string) return nil; } -// Cocoa will query the menu item's target for the worksWhenModal selector. -// So we need to implement this to allow the items to be handled correctly -// when a modal dialog is visible. -- (BOOL)worksWhenModal -{ - if (!QGuiApplication::modalWindow()) - return YES; - const auto &qpaMenu = static_cast(self).qpaMenu; - if (auto *mb = qobject_cast(qpaMenu->menuParent())) - return QGuiApplication::modalWindow()->handle() == mb->cocoaWindow() ? YES : NO; - return YES; -} - @end #undef CHECK_MENU_CLASS From caa5a20479bd58fda4380181691f772a1f3c1da4 Mon Sep 17 00:00:00 2001 From: Kari Oikarinen Date: Fri, 2 Mar 2018 16:36:06 +0200 Subject: [PATCH 15/15] Make a benchmark out of tst_QObjectPerformance::emitToManyReceivers The test has been flaky on top of QEMU. The test is clearly a sort of manually rolled benchmark, not a regular autotest. Remove the test and replace it with a benchmark in QObjectBenchmark. Task-number: QTBUG-66823 Task-number: QTBUG-66216 Change-Id: I7a48293023f32141eed6fea50fbb63af18933a8f Reviewed-by: Olivier Goffart (Woboq GmbH) --- tests/auto/other/other.pro | 2 - .../auto/other/qobjectperformance/.gitignore | 1 - .../qobjectperformance/qobjectperformance.pro | 7 -- .../tst_qobjectperformance.cpp | 110 ------------------ .../corelib/kernel/qobject/main.cpp | 24 ++++ 5 files changed, 24 insertions(+), 120 deletions(-) delete mode 100644 tests/auto/other/qobjectperformance/.gitignore delete mode 100644 tests/auto/other/qobjectperformance/qobjectperformance.pro delete mode 100644 tests/auto/other/qobjectperformance/tst_qobjectperformance.cpp diff --git a/tests/auto/other/other.pro b/tests/auto/other/other.pro index 4119a40279..ea2e1dabf2 100644 --- a/tests/auto/other/other.pro +++ b/tests/auto/other/other.pro @@ -16,7 +16,6 @@ SUBDIRS=\ qcomplextext \ qfocusevent \ qnetworkaccessmanager_and_qprogressdialog \ - qobjectperformance \ qobjectrace \ qsharedpointer_and_qwidget \ qprocess_and_guieventloop \ @@ -44,7 +43,6 @@ SUBDIRS=\ lancelot \ networkselftest \ qnetworkaccessmanager_and_qprogressdialog \ - qobjectperformance cross_compile: SUBDIRS -= \ atwrapper \ diff --git a/tests/auto/other/qobjectperformance/.gitignore b/tests/auto/other/qobjectperformance/.gitignore deleted file mode 100644 index 028ca89aad..0000000000 --- a/tests/auto/other/qobjectperformance/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tst_qobjectperformance diff --git a/tests/auto/other/qobjectperformance/qobjectperformance.pro b/tests/auto/other/qobjectperformance/qobjectperformance.pro deleted file mode 100644 index 189f8eeb2a..0000000000 --- a/tests/auto/other/qobjectperformance/qobjectperformance.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -TARGET = tst_qobjectperformance -SOURCES += tst_qobjectperformance.cpp - -QT = core network testlib - - diff --git a/tests/auto/other/qobjectperformance/tst_qobjectperformance.cpp b/tests/auto/other/qobjectperformance/tst_qobjectperformance.cpp deleted file mode 100644 index cbff6b7caf..0000000000 --- a/tests/auto/other/qobjectperformance/tst_qobjectperformance.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include - -#include - -#include - -class tst_QObjectPerformance : public QObject -{ - Q_OBJECT - -public: - -private slots: - void emitToManyReceivers(); -}; - -class SimpleSenderObject : public QObject -{ - Q_OBJECT - -signals: - void signal(); - -public: - void emitSignal() - { - emit signal(); - } -}; - -class SimpleReceiverObject : public QObject -{ - Q_OBJECT - -public slots: - void slot() - { - } -}; - -void tst_QObjectPerformance::emitToManyReceivers() -{ - // ensure that emission times remain mostly linear as the number of receivers increase - - SimpleSenderObject sender; - int elapsed = 0; - const int increase = 3000; - const int base = 5000; - - for (int i = 0; i < 4; ++i) { - const int size = base + i * increase; - const double increaseRatio = double(size) / (double)(size - increase); - - QList receivers; - for (int k = 0; k < size; ++k) { - SimpleReceiverObject *receiver = new SimpleReceiverObject; - QObject::connect(&sender, SIGNAL(signal()), receiver, SLOT(slot())); - receivers.append(receiver); - } - - QTime timer; - timer.start(); - sender.emitSignal(); - int e = timer.elapsed(); - - if (elapsed > 1) { - qDebug() << size << "receivers, elapsed time" << e << "compared to previous time" << elapsed; - QVERIFY(double(e) / double(elapsed) <= increaseRatio * 2.0); - } else { - qDebug() << size << "receivers, elapsed time" << e << "cannot be compared to previous, unmeasurable time"; - } - elapsed = e; - - qDeleteAll(receivers); - receivers.clear(); - } -} - - -QTEST_MAIN(tst_QObjectPerformance) -#include "tst_qobjectperformance.moc" diff --git a/tests/benchmarks/corelib/kernel/qobject/main.cpp b/tests/benchmarks/corelib/kernel/qobject/main.cpp index c11b13b7ea..04ca69ad3b 100644 --- a/tests/benchmarks/corelib/kernel/qobject/main.cpp +++ b/tests/benchmarks/corelib/kernel/qobject/main.cpp @@ -43,6 +43,8 @@ Q_OBJECT private slots: void signal_slot_benchmark(); void signal_slot_benchmark_data(); + void signal_many_receivers(); + void signal_many_receivers_data(); void qproperty_benchmark_data(); void qproperty_benchmark(); void dynamic_property_benchmark(); @@ -127,6 +129,28 @@ void QObjectBenchmark::signal_slot_benchmark() } } +void QObjectBenchmark::signal_many_receivers_data() +{ + QTest::addColumn("receiverCount"); + QTest::newRow("100 receivers") << 100; + QTest::newRow("1 000 receivers") << 1000; + QTest::newRow("10 000 receivers") << 10000; +} + +void QObjectBenchmark::signal_many_receivers() +{ + QFETCH(int, receiverCount); + Object sender; + std::vector receivers(receiverCount); + + for (Object &receiver : receivers) + QObject::connect(&sender, &Object::signal0, &receiver, &Object::slot0); + + QBENCHMARK { + sender.emitSignal0(); + } +} + void QObjectBenchmark::qproperty_benchmark_data() { QTest::addColumn("name");