diff options
34 files changed, 510 insertions, 304 deletions
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) 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; - } + return false; // WinRT has no stderr +#endif - // no controlling TTY - return false; -# else -# error "Not Unix and not Windows?" -# endif + return true; +} + +/*! + 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 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; } -Q_CORE_EXPORT bool qt_logging_to_console() + +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 const bool logToConsole = willLogToConsole(); - return logToConsole; + 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<void (QtMsgType, const QMessageLogContext &, const QS static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (qt_logging_to_console()) - return false; + if (shouldLogToStderr()) + return false; // Leave logging up to stderr handler QString formattedMessage = qFormatLogMessage(type, context, message); formattedMessage.append(QLatin1Char('\n')); @@ -1528,8 +1575,8 @@ static bool systemd_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (qt_logging_to_console()) - return false; + if (shouldLogToStderr()) + return false; // Leave logging up to stderr handler QString formattedMessage = qFormatLogMessage(type, context, message); @@ -1567,8 +1614,8 @@ static bool systemd_default_message_handler(QtMsgType type, #if QT_CONFIG(syslog) static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (qt_logging_to_console()) - return false; + if (shouldLogToStderr()) + return false; // Leave logging up to stderr handler QString formattedMessage = qFormatLogMessage(type, context, message); @@ -1602,8 +1649,8 @@ static bool android_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (qt_logging_to_console()) - return false; + if (shouldLogToStderr()) + return false; // Leave logging up to stderr handler QString formattedMessage = qFormatLogMessage(type, context, message); @@ -1627,8 +1674,8 @@ static bool android_default_message_handler(QtMsgType type, #ifdef Q_OS_WIN static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message) { - if (qt_logging_to_console()) - return false; + if (shouldLogToStderr()) + return false; // Leave logging up to stderr handler QString formattedMessage = qFormatLogMessage(type, context, message); formattedMessage.append(QLatin1Char('\n')); @@ -1756,7 +1803,7 @@ static void qt_message_print(const QString &message) OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16())); return; #elif defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED) - if (!qt_logging_to_console()) { + if (!shouldLogToStderr()) { OutputDebugString(reinterpret_cast<const wchar_t*>(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/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<T, Prealloc> &operator<<(const T &t) { append(t); return *this; } + inline QVarLengthArray<T, Prealloc> &operator<<(T &&t) + { append(t); return *this; } inline QVarLengthArray<T, Prealloc> &operator+=(const T &t) { append(t); return *this; } + inline QVarLengthArray<T, Prealloc> &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<class T, int Prealloc> QVarLengthArray<T, Prealloc> &QVarLengthArray<T, Prealloc>::operator<<(T &&value) + \since 5.11 + + \overload + + \sa append(), operator+=() +*/ + /*! \fn template<class T, int Prealloc> QVarLengthArray<T, Prealloc> &QVarLengthArray<T, Prealloc>::operator+=(const T &value) \since 4.8 @@ -840,6 +848,14 @@ \sa append(), operator<<() */ +/*! \fn template<class T, int Prealloc> QVarLengthArray<T, Prealloc> &QVarLengthArray<T, Prealloc>::operator+=(T &&value) + \since 5.11 + + \overload + + \sa append(), operator<<() +*/ + /*! \fn template<class T, int Prealloc> int QVarLengthArray<T, Prealloc>::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<T> &operator<<(const QVector<T> &l) { *this += l; return *this; } + inline QVector<T> &operator+=(T &&t) + { append(t); return *this; } + inline QVector<T> &operator<<(T &&t) + { append(t); return *this; } QList<T> 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 <typename T> void QVector<T>::operator+=(T &&value) + \since 5.11 + + \overload + + \sa append(), operator<<() +*/ + /*! \fn template <typename T> QVector<T> QVector<T>::operator+(const QVector<T> &other) const Returns a vector that contains all the items in this vector @@ -1178,6 +1186,15 @@ \sa append(), operator+=() */ +/*! \fn template <typename T> QVector<T> &QVector<T>::operator<<(T &&value) + \since 5.11 + + \overload + + \sa append(), operator+=() +*/ + + /*! \fn template <typename T> QVector<T> &QVector<T>::operator<<(const QVector<T> &other) Appends \a other to the vector and returns a reference to the 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<QStandardItem*> &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<QStandardItem*> &item bool QStandardItemPrivate::insertRows(int row, int count, const QList<QStandardItem*> &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<QStandardI bool QStandardItemPrivate::insertColumns(int column, int count, const QList<QStandardItem*> &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/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 148dad5d6c..49b3e76606 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); @@ -494,4 +493,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<QCocoaMenu> 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<QCocoaMenu> 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<QCocoaMenuBar *>(self.qpaMenu->menuParent())) + return QGuiApplication::modalWindow()->handle() == mb->cocoaWindow() ? YES : NO; + return YES; +} + +- (void)qt_itemFired:(NSMenuItem *)item +{ + auto *qpaItem = reinterpret_cast<QCocoaMenuItem *>(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<QCocoaMenuItem *>(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<QCocoaMenuItem *>(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<QCocoaMenuItem *>(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<QCocoaNSMenu *>(self).qpaMenu; - if (auto *mb = qobject_cast<QCocoaMenuBar *>(qpaMenu->menuParent())) - return QGuiApplication::modalWindow()->handle() == mb->cocoaWindow() ? YES : NO; - return YES; -} - @end #undef CHECK_MENU_CLASS 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; 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) : 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 <libpq-fe.h> #include <pg_config.h> -#include <stdlib.h> -#include <math.h> -// 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 <cmath> // 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 <class FloatType> 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/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; }; 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 <QtTest/private/qbenchmark_p.h> #include <QtTest/private/qbenchmarkmetric_p.h> +#include <QtCore/private/qlogging_p.h> + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -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; } diff --git a/src/testlib/qtestsystem.h b/src/testlib/qtestsystem.h index a6cea26884..de6bd177d3 100644 --- a/src/testlib/qtestsystem.h +++ b/src/testlib/qtestsystem.h @@ -112,7 +112,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. @@ -120,7 +120,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); 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<QPersistentMode if (stretchLastSection && lastSectionLogicalIdx >= 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/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<QTabWidget*>(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/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<QModelIndex> rcParent; QVector<int> 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<QStandardItem*>()); + si->insertRows(0, 0); + si->insertRows(0, QList<QStandardItem*>()); } 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<QStandardItem*>()); + si->insertColumns(0, 0); } void tst_QStandardItemModel::removeRows() 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 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 <QtTest/QtTest> - -#include <qcoreapplication.h> - -#include <qobject.h> - -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<SimpleReceiverObject *> 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/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() { 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<QStandardItem *> 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 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" diff --git a/tests/benchmarks/corelib/kernel/qobject/main.cpp b/tests/benchmarks/corelib/kernel/qobject/main.cpp index 13c1439e43..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,13 +129,36 @@ void QObjectBenchmark::signal_slot_benchmark() } } +void QObjectBenchmark::signal_many_receivers_data() +{ + QTest::addColumn<int>("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<Object> receivers(receiverCount); + + for (Object &receiver : receivers) + QObject::connect(&sender, &Object::signal0, &receiver, &Object::slot0); + + QBENCHMARK { + sender.emitSignal0(); + } +} + void QObjectBenchmark::qproperty_benchmark_data() { QTest::addColumn<QByteArray>("name"); 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()); } } |