From e586d9ea7f45c39d00d0fff2fdda4e20aa1c19cc Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Thu, 4 Jan 2018 16:05:15 +0100 Subject: [PATCH 01/10] Revert "Blacklist tst_QDateTime::operator_eqeq" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove black-listing of test; the problem was due to misconfigured time-zone on the CI system - it was on some zone that presently coincides with UTC, not actually on UTC as supposed. This reverts commit 597b96b8fa2fc8b9331db8ecc85d742be83fbec5. Change-Id: I72ad1dfa38532362c05aef33cd874f7f79879a41 Reviewed-by: Tony Sarajärvi --- tests/auto/corelib/tools/qdatetime/BLACKLIST | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 tests/auto/corelib/tools/qdatetime/BLACKLIST diff --git a/tests/auto/corelib/tools/qdatetime/BLACKLIST b/tests/auto/corelib/tools/qdatetime/BLACKLIST deleted file mode 100644 index e78f5cfd87..0000000000 --- a/tests/auto/corelib/tools/qdatetime/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -[operator_eqeq] -ubuntu-16.04 -b2qt From 6ca48a847a1805c3826004c5b989b4ae14397a37 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 4 Jan 2018 09:02:23 +0100 Subject: [PATCH 02/10] Fix fallback fonts for non-common writing system When we request fallback fonts, we cannot discriminate the fonts based on the writing system support. This is especially important since common script is now merged with other scripts, meaning that a common script character will always go through the fallback mechanism when not supported by the main font. When drawing for instance a string of Devanagari characters on macOS, we would get a list of 33 fallback fonts, but almost all of them would be the default Devanagari font, since none of the other fallbacks would support that script. Meaning that we would just check the same font over and over, which makes no sense. The fallback list has been retrieved specifically for the given script, so we do not need to consider that when fetching the fonts. For most of the common set, we will not have noticed the bug, because at least one of the writing system-specific fallbacks will have had support for latin characters as well. But when trying to mix emojis and some non-common script, we would get a box in place of the emoji, which had been adopted to the main script and would only be looked for in the fonts supporting this. Note that this exposed an issue with the QRawFont test on some systems. When the sample text contained a space, it would be possible to get a fallback font for this character, since we now effectively support fallbacks. This is not the correct behavior, but it is unrelated to this fix, and it was not what the QRawFont::unsupportedWritingSystem() test was written to check. I have therefore removed the space from the sample text to make the test pass, and will make a separate task of fixing the issue of merging fonts for whitespace characters. [ChangeLog][QtGui][Text] Fixed a bug where mixing different writing systems with emojis could lead to missing glyphs. Task-number: QTBUG-61882 Change-Id: I00f6043bb01af1f2277723ccf643034aebf3e18f Reviewed-by: Konstantin Ritt --- src/gui/text/qfontengine.cpp | 9 +++++++-- tests/auto/gui/text/qrawfont/tst_qrawfont.cpp | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 596c79fd05..ebaeb9b49b 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -1847,7 +1847,12 @@ QFontEngine *QFontEngineMulti::loadEngine(int at) request.styleStrategy |= QFont::NoFontMerging; request.family = fallbackFamilyAt(at - 1); - if (QFontEngine *engine = QFontDatabase::findFont(request, m_script)) { + // At this point, the main script of the text has already been considered + // when fetching the list of fallback families from the database, and the + // info about the actual script of the characters may have been discarded, + // so we do not check for writing system support, but instead just load + // the family indiscriminately. + if (QFontEngine *engine = QFontDatabase::findFont(request, QFontDatabase::Any)) { engine->fontDef.weight = request.weight; if (request.style > QFont::StyleNormal) engine->fontDef.style = request.style; diff --git a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp index 3cf108ed62..ee775d4a31 100644 --- a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp +++ b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp @@ -887,7 +887,7 @@ void tst_QRawFont::unsupportedWritingSystem() QCOMPARE(rawFont.familyName(), QString::fromLatin1("QtBidiTestFont")); QCOMPARE(rawFont.pixelSize(), 12.0); - QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic); + QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic).simplified().remove(QLatin1Char(' ')); QTextLayout layout; layout.setFont(font); From 907a99f65b88cc5af85127ef8b665b173832b43e Mon Sep 17 00:00:00 2001 From: Jani Heikkinen Date: Tue, 19 Dec 2017 13:53:16 +0200 Subject: [PATCH 03/10] Fix license headers Old header.LGPL21 header was used at some files. Replase those with new header.LGPL one Remove old header.LGPL21 Task-number: QTBUG-57147 Change-Id: I650e39024ed4876bba27e954c7d61fdb025b46ef Reviewed-by: Kai Koehne Reviewed-by: Lars Knoll --- header.LGPL21 | 33 ------------------ mkspecs/integrity-armv8-rcar/qplatformdefs.h | 30 +++++++++------- .../tools/qmake/testdata/rawString/object2.h | 34 +++++++++++-------- 3 files changed, 38 insertions(+), 59 deletions(-) delete mode 100644 header.LGPL21 diff --git a/header.LGPL21 b/header.LGPL21 deleted file mode 100644 index 533390b078..0000000000 --- a/header.LGPL21 +++ /dev/null @@ -1,33 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the FOO module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - diff --git a/mkspecs/integrity-armv8-rcar/qplatformdefs.h b/mkspecs/integrity-armv8-rcar/qplatformdefs.h index 55afd0c3c7..a743326346 100644 --- a/mkspecs/integrity-armv8-rcar/qplatformdefs.h +++ b/mkspecs/integrity-armv8-rcar/qplatformdefs.h @@ -5,27 +5,33 @@ ** ** This file is part of the qmake spec of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** 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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** 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$ ** diff --git a/tests/auto/tools/qmake/testdata/rawString/object2.h b/tests/auto/tools/qmake/testdata/rawString/object2.h index 2ab77cd3bd..d29f0c72ec 100644 --- a/tests/auto/tools/qmake/testdata/rawString/object2.h +++ b/tests/auto/tools/qmake/testdata/rawString/object2.h @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2017 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:LGPL21$ +** $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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** 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 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** 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. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** 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$ ** From d196036024697a75868c1f1626525710495ca428 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 23 Nov 2017 14:25:04 +0100 Subject: [PATCH 04/10] Avoid providing bad pixelDeltas on X11 With libinput we now get a hardcoded resolution that is unrelated to the hardware. So avoid using that as a real pixel delta and document pixel deltas as being driver specific and unreliable on X11. Task-number: QTBUG-59261 Change-Id: I9fe86d80e7ccd290ed2e4091d7eafa52cb537d34 Reviewed-by: David Edmundson Reviewed-by: Marco Martin Reviewed-by: Gatis Paeglis --- src/gui/kernel/qevent.cpp | 1 + src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 06d52aa78d..c68f9afa56 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -971,6 +971,7 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, \li scrolling is about to begin, but the distance did not yet change (Qt::ScrollBegin), \li or scrolling has ended and the distance did not change anymore (Qt::ScrollEnd). \endlist + \note On X11 this value is driver specific and unreliable, use angleDelta() instead */ /*! diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index d1d97affe8..94f543fd39 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -953,10 +953,12 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin double delta = scrollingDevice.lastScrollPosition.y() - value; scrollingDevice.lastScrollPosition.setY(value); angleDelta.setY((delta / scrollingDevice.verticalIncrement) * 120); - // We do not set "pixel" delta if it is only measured in ticks. - if (scrollingDevice.verticalIncrement > 1) + // With most drivers the increment is 1 for wheels. + // For libinput it is hardcoded to a useless 15. + // For a proper touchpad driver it should be in the same order of magnitude as 120 + if (scrollingDevice.verticalIncrement > 15) rawDelta.setY(delta); - else if (scrollingDevice.verticalIncrement < -1) + else if (scrollingDevice.verticalIncrement < -15) rawDelta.setY(-delta); } } @@ -965,10 +967,10 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin double delta = scrollingDevice.lastScrollPosition.x() - value; scrollingDevice.lastScrollPosition.setX(value); angleDelta.setX((delta / scrollingDevice.horizontalIncrement) * 120); - // We do not set "pixel" delta if it is only measured in ticks. - if (scrollingDevice.horizontalIncrement > 1) + // See comment under vertical + if (scrollingDevice.horizontalIncrement > 15) rawDelta.setX(delta); - else if (scrollingDevice.horizontalIncrement < -1) + else if (scrollingDevice.horizontalIncrement < -15) rawDelta.setX(-delta); } } From fcbaa8ec385e796c18cf317e8f364bd8e3c2538b Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 20 Nov 2017 15:30:20 +0100 Subject: [PATCH 05/10] QFileSystemModel/Windows: Make file name checking case-insensitive Introduce a special hash modeled on the one used for QFileSystemWatcher on Windows. Task-number: QTBUG-31103 Task-number: QTBUG-64147 Change-Id: I69ebabe841716e4957ae3fb04fa5c43d233a3552 Reviewed-by: Oliver Wolff Reviewed-by: Richard Moe Gustavsen --- src/widgets/dialogs/qfilesystemmodel.cpp | 9 ++---- src/widgets/dialogs/qfilesystemmodel_p.h | 19 +++++++++++- .../qfilesystemmodel/tst_qfilesystemmodel.cpp | 29 ++++++++++++++++++- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index 7458cfdd80..bf88aaa467 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -1100,8 +1100,8 @@ void QFileSystemModelPrivate::sortChildren(int column, const QModelIndex &parent return; QVector values; - QHash::const_iterator iterator; - for(iterator = indexNode->children.constBegin() ; iterator != indexNode->children.constEnd() ; ++iterator) { + + for (auto iterator = indexNode->children.constBegin(), cend = indexNode->children.constEnd(); iterator != cend; ++iterator) { if (filtersAcceptsNode(iterator.value())) { values.append(iterator.value()); } else { @@ -1661,13 +1661,10 @@ void QFileSystemModelPrivate::_q_directoryChanged(const QString &directory, cons QStringList toRemove; QStringList newFiles = files; std::sort(newFiles.begin(), newFiles.end()); - QHash::const_iterator i = parentNode->children.constBegin(); - while (i != parentNode->children.constEnd()) { + for (auto i = parentNode->children.constBegin(), cend = parentNode->children.constEnd(); i != cend; ++i) { QStringList::iterator iterator = std::lower_bound(newFiles.begin(), newFiles.end(), i.value()->fileName); if ((iterator == newFiles.end()) || (i.value()->fileName < *iterator)) toRemove.append(i.value()->fileName); - - ++i; } for (int i = 0 ; i < toRemove.count() ; ++i ) removeNode(parentNode, toRemove[i]); diff --git a/src/widgets/dialogs/qfilesystemmodel_p.h b/src/widgets/dialogs/qfilesystemmodel_p.h index 640225529b..e8bae4f659 100644 --- a/src/widgets/dialogs/qfilesystemmodel_p.h +++ b/src/widgets/dialogs/qfilesystemmodel_p.h @@ -72,6 +72,23 @@ class ExtendedInformation; class QFileSystemModelPrivate; class QFileIconProvider; +#if defined(Q_OS_WIN) +class QFileSystemModelNodePathKey : public QString +{ +public: + QFileSystemModelNodePathKey() {} + QFileSystemModelNodePathKey(const QString &other) : QString(other) {} + QFileSystemModelNodePathKey(const QFileSystemModelNodePathKey &other) : QString(other) {} + bool operator==(const QFileSystemModelNodePathKey &other) const { return !compare(other, Qt::CaseInsensitive); } +}; + +Q_DECLARE_TYPEINFO(QFileSystemModelNodePathKey, Q_MOVABLE_TYPE); + +inline uint qHash(const QFileSystemModelNodePathKey &key) { return qHash(key.toCaseFolded()); } +#else // Q_OS_WIN +typedef QString QFileSystemModelNodePathKey; +#endif + class Q_AUTOTEST_EXPORT QFileSystemModelPrivate : public QAbstractItemModelPrivate { Q_DECLARE_PUBLIC(QFileSystemModel) @@ -189,7 +206,7 @@ public: bool populatedChildren; bool isVisible; - QHash children; + QHash children; QList visibleChildren; int dirtyChildrenIndex; QFileSystemNode *parent; diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp index 979d5c632e..71efe1d59a 100644 --- a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -43,6 +43,7 @@ #if defined(Q_OS_WIN) # include // for SetFileAttributes #endif +#include #include @@ -883,6 +884,18 @@ void tst_QFileSystemModel::deleteFile() QVERIFY(!newFile.exists()); } +static QString flipCase(QString s) +{ + for (int i = 0, size = s.size(); i < size; ++i) { + const QChar c = s.at(i); + if (c.isUpper()) + s[i] = c.toLower(); + else if (c.isLower()) + s[i] = c.toUpper(); + } + return s; +} + void tst_QFileSystemModel::caseSensitivity() { QString tmp = flatDirTestPath; @@ -890,9 +903,23 @@ void tst_QFileSystemModel::caseSensitivity() files << "a" << "c" << "C"; QVERIFY(createFiles(tmp, files)); QModelIndex root = model->index(tmp); + QStringList paths; + QModelIndexList indexes; QCOMPARE(model->rowCount(root), 0); for (int i = 0; i < files.count(); ++i) { - QVERIFY(model->index(tmp + '/' + files.at(i)).isValid()); + const QString path = tmp + '/' + files.at(i); + const QModelIndex index = model->index(path); + QVERIFY(index.isValid()); + paths.append(path); + indexes.append(index); + } + + if (!QFileSystemEngine::isCaseSensitive()) { + // QTBUG-31103, QTBUG-64147: Verify that files can be accessed by paths with fLipPeD case. + for (int i = 0; i < paths.count(); ++i) { + const QModelIndex flippedCaseIndex = model->index(flipCase(paths.at(i))); + QCOMPARE(indexes.at(i), flippedCaseIndex); + } } } From 12687ccfd5a6056ab24a792dbe28f1d5829fd88c Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 8 Jan 2018 13:29:38 +0100 Subject: [PATCH 06/10] CoreText: Order fallback fonts based on writing system support After we stopped sanitizing the fallback font list (with change 6ca48a847a1805c3826004c5b989b4ae14397a37), we now need to make sure it is ordered so that the fonts that support the writing system in question are always tested first, otherwise we can end up loading a lot of fonts that will never be used. Task-number: QTBUG-65605 Change-Id: Id2a65bbff3e64e6d6e6b4f72500778ee3e811e84 Reviewed-by: Konstantin Ritt --- src/gui/text/qfontdatabase.cpp | 35 +++++++++++++++++++ .../mac/qcoretextfontdatabase.mm | 3 ++ tests/auto/gui/text/qrawfont/tst_qrawfont.cpp | 35 +++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 2cc071d67b..33dc27983a 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -2837,6 +2837,41 @@ QString QFontDatabase::resolveFontFamilyAlias(const QString &family) return QGuiApplicationPrivate::platformIntegration()->fontDatabase()->resolveFontFamilyAlias(family); } +Q_GUI_EXPORT QStringList qt_sort_families_by_writing_system(QChar::Script script, const QStringList &families) +{ + size_t writingSystem = std::find(scriptForWritingSystem, + scriptForWritingSystem + QFontDatabase::WritingSystemsCount, + script) - scriptForWritingSystem; + if (writingSystem == QFontDatabase::Any + || writingSystem >= QFontDatabase::WritingSystemsCount) { + return families; + } + + QFontDatabasePrivate *db = privateDb(); + QMultiMap supported; + for (int i = 0; i < families.size(); ++i) { + const QString &family = families.at(i); + + QtFontFamily *testFamily = nullptr; + for (int x = 0; x < db->count; ++x) { + if (Q_UNLIKELY(matchFamilyName(family, db->families[x]))) { + testFamily = db->families[x]; + testFamily->ensurePopulated(); + break; + } + } + + uint order = i; + if (testFamily == nullptr + || (testFamily->writingSystems[writingSystem] & QtFontFamily::Supported) == 0) { + order |= 1 << 31; + } + + supported.insert(order, family); + } + + return supported.values(); +} QT_END_NAMESPACE diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 237e8a89a5..7397312820 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -544,6 +544,9 @@ QStringList QCoreTextFontDatabase::fallbacksForFamily(const QString &family, QFo fallbackList.append(QStringLiteral("Arial Unicode MS")); #endif + extern QStringList qt_sort_families_by_writing_system(QChar::Script, const QStringList &); + fallbackList = qt_sort_families_by_writing_system(script, fallbackList); + return fallbackList; } } diff --git a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp index ee775d4a31..37f94d0278 100644 --- a/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp +++ b/tests/auto/gui/text/qrawfont/tst_qrawfont.cpp @@ -90,6 +90,9 @@ private slots: void rawFontFromInvalidData(); void kernedAdvances(); + + void fallbackFontsOrder(); + private: QString testFont; QString testFontBoldItalic; @@ -1009,6 +1012,38 @@ void tst_QRawFont::kernedAdvances() QVERIFY(FUZZY_LTEQ(qAbs(advances.at(0).x() - expectedAdvanceWidth), errorMargin)); } +void tst_QRawFont::fallbackFontsOrder() +{ + QFontDatabase fontDatabase; + int id = fontDatabase.addApplicationFont(testFont); + + QFont font("QtBidiTestFont"); + font.setPixelSize(12.0); + + QString arabicText = QFontDatabase::writingSystemSample(QFontDatabase::Arabic); + + // If this fails, then the writing system sample has changed and we need to create + // a new text containing both a space and Arabic characters. + QVERIFY(arabicText.contains(QLatin1Char(' '))); + + QTextLayout layout; + layout.setFont(font); + layout.setText(arabicText); + layout.setCacheEnabled(true); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QList glyphRuns = layout.glyphRuns(); + + // Since QtBidiTestFont does not support Arabic nor the space, both should map to + // the same font. If this fails, it is an indication that the list of fallbacks fonts + // is not sorted by writing system support. + QCOMPARE(glyphRuns.size(), 1); + + fontDatabase.removeApplicationFont(id); +} + #endif // QT_NO_RAWFONT QTEST_MAIN(tst_QRawFont) From 02557c07da2ac251f36cb6bf7f424071c53f8b45 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 9 Jan 2018 08:33:12 +0100 Subject: [PATCH 07/10] Fix ZWJ and ZWNJ when fallback font is in use When applying fallback fonts to characters that are joined by ZWJ or ZWNJ, we also have to set the same font for the control characters, otherwise we will split the text and the necessary shaping will not take place. This was reported for emojis, but will probably also happen for Indic scripts where joiners are used predominately. [ChangeLog][QtGui][Text] Fixed ZWJ and ZWNJ control characters when fallback fonts are in use. Task-number: QTBUG-65519 Change-Id: Ia37233f3319b95af68ae6053c29997eac65448e0 Reviewed-by: Lars Knoll --- src/gui/text/qfontengine.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index ebaeb9b49b..29c48da7be 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -1905,8 +1905,33 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, int glyph_pos = 0; QStringIterator it(str, str + len); + + int lastFallback = -1; while (it.hasNext()) { const uint ucs4 = it.peekNext(); + + // If we applied a fallback font to previous glyph, and the current is either + // ZWJ or ZWNJ, we should also try applying the same fallback font to that, in order + // to get the correct shaping rules applied. + if (lastFallback >= 0 && (ucs4 == QChar(0x200d) || ucs4 == QChar(0x200c))) { + QFontEngine *engine = m_engines.at(lastFallback); + glyph_t glyph = engine->glyphIndex(ucs4); + if (glyph != 0) { + glyphs->glyphs[glyph_pos] = glyph; + if (!(flags & GlyphIndicesOnly)) { + QGlyphLayout g = glyphs->mid(glyph_pos, 1); + engine->recalcAdvances(&g, flags); + } + + // set the high byte to indicate which engine the glyph came from + glyphs->glyphs[glyph_pos] |= (lastFallback << 24); + } else { + lastFallback = -1; + } + } else { + lastFallback = -1; + } + if (glyphs->glyphs[glyph_pos] == 0 && ucs4 != QChar::LineSeparator && ucs4 != QChar::LineFeed @@ -1935,6 +1960,9 @@ bool QFontEngineMulti::stringToCMap(const QChar *str, int len, QGlyphLayout g = glyphs->mid(glyph_pos, 1); engine->recalcAdvances(&g, flags); } + + lastFallback = x; + // set the high byte to indicate which engine the glyph came from glyphs->glyphs[glyph_pos] |= (x << 24); break; From 43e444d1ffac9d63eeb2053465d02ce4a6cc7410 Mon Sep 17 00:00:00 2001 From: Kevin Funk Date: Fri, 29 Sep 2017 23:53:29 +0200 Subject: [PATCH 08/10] CMake: qt5_add_binary_resource: re-run if needed qt5_add_binary_resources() macro did not recompile for CMake generated input before this patch. Adding the input files to the DEPENDS option corrects this issue: Task-number: QTBUG-60714 Change-Id: I0f46918c6f1079fed7ee1b21305b18ff38f863f8 Reviewed-by: David Faure --- src/corelib/Qt5CoreMacros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/Qt5CoreMacros.cmake b/src/corelib/Qt5CoreMacros.cmake index 8b65db95cb..8d3dbe3ecf 100644 --- a/src/corelib/Qt5CoreMacros.cmake +++ b/src/corelib/Qt5CoreMacros.cmake @@ -257,7 +257,7 @@ function(QT5_ADD_BINARY_RESOURCES target ) add_custom_command(OUTPUT ${rcc_destination} COMMAND ${Qt5Core_RCC_EXECUTABLE} ARGS ${rcc_options} --binary --name ${target} --output ${rcc_destination} ${infiles} - DEPENDS ${rc_depends} ${out_depends} VERBATIM) + DEPENDS ${rc_depends} ${out_depends} ${infiles} VERBATIM) add_custom_target(${target} ALL DEPENDS ${rcc_destination}) endfunction() From 6ff8d8173d3056d8bd232e6c11da328a300915d5 Mon Sep 17 00:00:00 2001 From: Kavindra Palaraja Date: Mon, 9 Jan 2017 21:51:54 +0100 Subject: [PATCH 09/10] Clarify behavior of setData in QListWidget, QTableWidget, and QTreeWidget setData()'s default implementation for QListWidget, QTableWidget, and QTreeWidget treats Qt::EditRole and Qt::DisplayRole as referring to the same data. Used the same sentence from QStandardItem::setData for consistency. Task-number: QTBUG-11549 Change-Id: I41d06bdaaa8e7d4a86e24147b3d8222ad7823a6c Reviewed-by: Giuseppe D'Angelo Reviewed-by: Andy Shaw --- src/widgets/itemviews/qlistwidget.cpp | 3 +++ src/widgets/itemviews/qtablewidget.cpp | 3 +++ src/widgets/itemviews/qtreewidget.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/widgets/itemviews/qlistwidget.cpp b/src/widgets/itemviews/qlistwidget.cpp index 21747d4e6e..6c4746e086 100644 --- a/src/widgets/itemviews/qlistwidget.cpp +++ b/src/widgets/itemviews/qlistwidget.cpp @@ -690,6 +690,9 @@ QListWidgetItem *QListWidgetItem::clone() const Sets the data for a given \a role to the given \a value. Reimplement this function if you need extra roles or special behavior for certain roles. + \note The default implementation treats Qt::EditRole and Qt::DisplayRole as + referring to the same data. + \sa Qt::ItemDataRole, data() */ void QListWidgetItem::setData(int role, const QVariant &value) diff --git a/src/widgets/itemviews/qtablewidget.cpp b/src/widgets/itemviews/qtablewidget.cpp index 3ff75cc23b..d89fdc9000 100644 --- a/src/widgets/itemviews/qtablewidget.cpp +++ b/src/widgets/itemviews/qtablewidget.cpp @@ -1365,6 +1365,9 @@ QTableWidgetItem *QTableWidgetItem::clone() const /*! Sets the item's data for the given \a role to the specified \a value. + \note The default implementation treats Qt::EditRole and Qt::DisplayRole as + referring to the same data. + \sa Qt::ItemDataRole, data() */ void QTableWidgetItem::setData(int role, const QVariant &value) diff --git a/src/widgets/itemviews/qtreewidget.cpp b/src/widgets/itemviews/qtreewidget.cpp index 337089056d..762a19126e 100644 --- a/src/widgets/itemviews/qtreewidget.cpp +++ b/src/widgets/itemviews/qtreewidget.cpp @@ -1699,6 +1699,9 @@ Qt::ItemFlags QTreeWidgetItem::flags() const The \a role describes the type of data specified by \a value, and is defined by the Qt::ItemDataRole enum. + + \note The default implementation treats Qt::EditRole and Qt::DisplayRole as + referring to the same data. */ void QTreeWidgetItem::setData(int column, int role, const QVariant &value) { From a5ad605dfec2ab4e921d5c5843b23916ed5ae3bf Mon Sep 17 00:00:00 2001 From: Ryan Chu Date: Tue, 12 Dec 2017 15:06:43 +0100 Subject: [PATCH 10/10] QFtp: only use fall-back password for anonymous access The code used to fall back to anonymous login independently for username and password; however, it should only use a fall-back password if the username is missing or (case-insensitive) "anonymous". When a non-anonymous username is given without password, we should simply skip he PASS message to FTP. If the FTP server requests a password, in the latter case, QFtp will signal authenticationRequired; in all cases, if the server rejects the given credentials, QFtp signals authenticationFailed. Either way, the client code can then query the user for credentials as usual. Task-number: QTBUG-25033 Change-Id: I2a4a3b2725819ab19c8a7e4baa431af539edcd8d Reviewed-by: Edward Welbourne --- src/network/access/qftp.cpp | 12 +- tests/auto/network/access/qftp/tst_qftp.cpp | 209 +++++++++++++++++++- 2 files changed, 217 insertions(+), 4 deletions(-) diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp index 762ef00225..719e3536b4 100644 --- a/src/network/access/qftp.cpp +++ b/src/network/access/qftp.cpp @@ -1702,8 +1702,16 @@ int QFtp::connectToHost(const QString &host, quint16 port) int QFtp::login(const QString &user, const QString &password) { QStringList cmds; - cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n")); - cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n")); + + if (user.isNull() || user.compare(QLatin1String("anonymous"), Qt::CaseInsensitive) == 0) { + cmds << (QLatin1String("USER ") + (user.isNull() ? QLatin1String("anonymous") : user) + QLatin1String("\r\n")); + cmds << (QLatin1String("PASS ") + (password.isNull() ? QLatin1String("anonymous@") : password) + QLatin1String("\r\n")); + } else { + cmds << (QLatin1String("USER ") + user + QLatin1String("\r\n")); + if (!password.isNull()) + cmds << (QLatin1String("PASS ") + password + QLatin1String("\r\n")); + } + return d_func()->addCommand(new QFtpCommand(Login, cmds)); } diff --git a/tests/auto/network/access/qftp/tst_qftp.cpp b/tests/auto/network/access/qftp/tst_qftp.cpp index fba0508f04..44ae125d37 100644 --- a/tests/auto/network/access/qftp/tst_qftp.cpp +++ b/tests/auto/network/access/qftp/tst_qftp.cpp @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include "../../../network-settings.h" @@ -108,6 +110,9 @@ private slots: void qtbug7359Crash(); + void loginURL_data(); + void loginURL(); + protected slots: void stateChanged( int ); void listInfo( const QUrlInfo & ); @@ -396,11 +401,11 @@ void tst_QFtp::login_data() QTest::addColumn("success"); QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << 1; - QTest::newRow( "ok02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << 1; + QTest::newRow( "ok02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("") << 1; QTest::newRow( "ok03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << 1; QTest::newRow( "ok04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << 1; - QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString() << 0; + QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString("") << 0; QTest::newRow( "error02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString("bar") << 0; } @@ -2151,6 +2156,206 @@ void tst_QFtp::qtbug7359Crash() QCoreApplication::processEvents(QEventLoop::AllEvents, 2000 - elapsed); } +class FtpLocalServer : public QTcpServer +{ + Q_OBJECT + +public: + explicit FtpLocalServer(QObject *parent = 0) : QTcpServer(parent) {} + virtual ~FtpLocalServer() { delete mSocket; } + void startServer(qint16 port = 0); + void stopServer(); + + enum class ReplyCodes { + ServiceReady = 220, + ServiceClose = 221, + NeedPassword = 331, + LoginFailed = 530, + RequestDeny = 550 + }; + + void sendResponse(ReplyCodes code); + + inline QString getRawUser() { return rawUser; } + inline QString getRawPassword() { return rawPass; } + +signals: + void onStarted(); + void onStopped(); + +public slots: + void socketReadyRead(); + void socketDisconnected(); + +protected: + virtual void incomingConnection(qintptr handle); + +private: + QTcpSocket *mSocket = nullptr; + QString rawUser; + QString rawPass; +}; + +void FtpLocalServer::startServer(qint16 port) +{ + if (listen(QHostAddress::Any, port)) + emit onStarted(); // Notify connected objects + else + qDebug("Could not start FTP server"); +} + +void FtpLocalServer::stopServer() +{ + close(); + emit onStopped(); // Notify connected objects +} + +void FtpLocalServer::sendResponse(ReplyCodes code) +{ + if (mSocket) + { + QString response; + switch (code) { + case ReplyCodes::ServiceReady: + response = QString("220 Service ready for new user.\r\n"); + break; + case ReplyCodes::ServiceClose: + response = QString("221 Service closing control connection.\r\n"); + break; + case ReplyCodes::NeedPassword: + response = QString("331 User name okay, need password.\r\n"); + break; + case ReplyCodes::LoginFailed: + response = QString("530 Not logged in.\r\n"); + break; + case ReplyCodes::RequestDeny: + response = QString("550 Requested action not taken.\r\n"); + break; + default: + qDebug("Unimplemented response code: %u", static_cast(code)); + break; + } + + if (!response.isEmpty()) + mSocket->write(response.toLatin1()); + } +} + +void FtpLocalServer::incomingConnection(qintptr handle) +{ + mSocket = new QTcpSocket(this); + if (mSocket == nullptr || !mSocket->setSocketDescriptor(handle)) + { + delete mSocket; + mSocket = nullptr; + qDebug() << handle << " Error binding socket"; + return; + } + + connect(mSocket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); + connect(mSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + + // Accept client connection + sendResponse(ReplyCodes::ServiceReady); +} + +void FtpLocalServer::socketReadyRead() +{ + QString data; + if (mSocket) + data.append(mSocket->readAll()); + + // RFC959 Upper and lower case alphabetic characters are to be treated identically. + if (data.startsWith("USER", Qt::CaseInsensitive)) { + rawUser = data; + sendResponse(ReplyCodes::NeedPassword); + } else if (data.startsWith("PASS", Qt::CaseInsensitive)) { + rawPass = data; + sendResponse(ReplyCodes::LoginFailed); + } else { + sendResponse(ReplyCodes::RequestDeny); + } +} + +void FtpLocalServer::socketDisconnected() +{ + // Cleanup + if (mSocket) + mSocket->deleteLater(); + deleteLater(); +} + +void tst_QFtp::loginURL_data() +{ + QTest::addColumn("user"); + QTest::addColumn("password"); + QTest::addColumn("rawUser"); + QTest::addColumn("rawPass"); + + QTest::newRow("no username, no password") + << QString() << QString() + << QString("USER anonymous\r\n") << QString("PASS anonymous@\r\n"); + + QTest::newRow("username, no password") + << QString("someone") << QString() + << QString("USER someone\r\n") << QString(); + + QTest::newRow("username, empty password") + << QString("someone") << QString("") + << QString("USER someone\r\n") << QString("PASS \r\n"); + + QTest::newRow("username, password") + << QString("someone") << QString("nonsense") + << QString("USER someone\r\n") << QString("PASS nonsense\r\n"); + + QTest::newRow("anonymous, no password") + << QString("anonymous") << QString() + << QString("USER anonymous\r\n") << QString("PASS anonymous@\r\n"); + + QTest::newRow("Anonymous, no password") + << QString("Anonymous") << QString() + << QString("USER Anonymous\r\n") << QString("PASS anonymous@\r\n"); + + QTest::newRow("anonymous, empty password") + << QString("anonymous") << QString("") + << QString("USER anonymous\r\n") << QString("PASS \r\n"); + + QTest::newRow("ANONYMOUS, password") + << QString("ANONYMOUS") << QString("nonsense") + << QString("USER ANONYMOUS\r\n") << QString("PASS nonsense\r\n"); +} + +void tst_QFtp::loginURL() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("This test should be verified on the local machine without proxies"); + + QFETCH(QString, user); + QFETCH(QString, password); + QFETCH(QString, rawUser); + QFETCH(QString, rawPass); + + FtpLocalServer server; + server.startServer(); + uint port = server.serverPort(); + + ftp = newFtp(); + addCommand(QFtp::ConnectToHost, + ftp->connectToHost(QHostInfo::localHostName(), port)); + addCommand(QFtp::Login, ftp->login(user, password)); + + QTestEventLoop::instance().enterLoop(5); + delete ftp; + ftp = nullptr; + server.stopServer(); + if (QTestEventLoop::instance().timeout()) + QFAIL(msgTimedOut(QHostInfo::localHostName(), port)); + + QCOMPARE(server.getRawUser(), rawUser); + QCOMPARE(server.getRawPassword(), rawPass); +} + QTEST_MAIN(tst_QFtp) #include "tst_qftp.moc"