From fbd0e4489c936394a55f8084af2aab4ef2456360 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 28 Aug 2015 10:34:43 +0200 Subject: [PATCH 01/28] Copy the state to the picture paintengine when updating the state When the state of the paint changes then the one used for the picture paintengine needs to be kept in sync. Otherwise the rendering will be incorrect. Task-number: QTBUG-43145 Change-Id: Ia55a4e940d109bedb7c2eff4d985d3b212da75a4 Reviewed-by: Lars Knoll --- src/printsupport/kernel/qpaintengine_alpha.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/printsupport/kernel/qpaintengine_alpha.cpp b/src/printsupport/kernel/qpaintengine_alpha.cpp index 010a628e4f..a81f3eae43 100644 --- a/src/printsupport/kernel/qpaintengine_alpha.cpp +++ b/src/printsupport/kernel/qpaintengine_alpha.cpp @@ -146,8 +146,16 @@ void QAlphaPaintEngine::updateState(const QPaintEngineState &state) d->m_hasalpha = d->m_alphaOpacity || d->m_alphaBrush || d->m_alphaPen; - if (d->m_picengine) + if (d->m_picengine) { + const QPainter *p = painter(); + d->m_picpainter->setPen(p->pen()); + d->m_picpainter->setBrush(p->brush()); + d->m_picpainter->setBrushOrigin(p->brushOrigin()); + d->m_picpainter->setFont(p->font()); + d->m_picpainter->setOpacity(p->opacity()); + d->m_picpainter->setTransform(p->combinedTransform()); d->m_picengine->updateState(state); + } } void QAlphaPaintEngine::drawPath(const QPainterPath &path) From 97de58db672fe0489faba856fcfd59a125609796 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Fri, 21 Aug 2015 12:43:36 +0200 Subject: [PATCH 02/28] Doc: Added link to Declarative State Machine Framework Added link in C++ documentation Task-number: QTBUG-46285 Change-Id: I0f330829f7df713d4f5292b2a300c5c9d3732bda Reviewed-by: Venugopal Shivashankar --- src/corelib/doc/qtcore.qdocconf | 3 +-- src/corelib/doc/src/statemachine.qdoc | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/corelib/doc/qtcore.qdocconf b/src/corelib/doc/qtcore.qdocconf index 502689e4c2..3d64708def 100644 --- a/src/corelib/doc/qtcore.qdocconf +++ b/src/corelib/doc/qtcore.qdocconf @@ -25,8 +25,7 @@ qhp.QtCore.subprojects.classes.sortPages = true tagfile = ../../../doc/qtcore/qtcore.tags -depends += activeqt qtdbus qtgui qtwidgets qtnetwork qtdoc qtmacextras qtquick qtlinguist qtdesigner qtconcurrent qtxml qmake qtwinextras -# depends += qtqml # Qt namespace collides with QtQml::Qt, see QTBUG-38630 +depends += activeqt qtdbus qtgui qtwidgets qtnetwork qtdoc qtmacextras qtquick qtlinguist qtdesigner qtconcurrent qtxml qmake qtwinextras qtqml headerdirs += .. diff --git a/src/corelib/doc/src/statemachine.qdoc b/src/corelib/doc/src/statemachine.qdoc index e44a603959..d50851d816 100644 --- a/src/corelib/doc/src/statemachine.qdoc +++ b/src/corelib/doc/src/statemachine.qdoc @@ -71,6 +71,8 @@ which are currently active. All the states in a valid configuration of the state machine will have a common ancestor. + \sa {The Declarative State Machine Framework} + \section1 Classes in the State Machine Framework These classes are provided by qt for creating event-driven state machines. From dfb55da5d67c21179ccef107351a90be2815e1e2 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Thu, 3 Sep 2015 14:40:21 +0200 Subject: [PATCH 03/28] Doc: Added enums in qnamespace.qdoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-43810 Change-Id: Ib756382833fabecaae2526a413d046646f3e443e Reviewed-by: Topi Reiniö Reviewed-by: Martin Smith --- src/corelib/global/qnamespace.qdoc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index c6098007d1..2323ee2cb0 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -1695,6 +1695,17 @@ \value Key_Sleep \value Key_Zoom \value Key_Cancel + \value Key_MicVolumeUp + \value Key_Find + \value Key_Open + \value Key_MicVolumeDown + \value Key_New + \value Key_Settings + \value Key_Redo + \value Key_Exit + \value Key_Info + \value Key_Undo + \value Key_Guide \sa QKeyEvent::key() */ From 9588e1bba348acf9aa0d023ebb5195aa1bf69909 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Tue, 7 Jul 2015 15:29:05 +0200 Subject: [PATCH 04/28] Doc: Corrected link issues in qtbase Task-number: QTBUG-43810 Change-Id: I0a019becc53b222cb6a7df1fafdccd57aca5b598 Reviewed-by: Martin Smith --- qmake/doc/src/qmake-manual.qdoc | 3 +-- src/corelib/io/qtextstream.cpp | 2 +- src/corelib/kernel/qmetaobject.cpp | 5 +++-- src/gui/doc/qtgui.qdocconf | 3 ++- src/gui/image/qimage.cpp | 2 +- src/gui/image/qimagereader.cpp | 2 +- src/gui/math3d/qmatrix4x4.cpp | 2 -- src/network/doc/qtnetwork.qdocconf | 2 +- src/network/ssl/qsslellipticcurve.cpp | 5 ++--- src/network/ssl/qsslpresharedkeyauthenticator.cpp | 2 -- src/opengl/doc/qtopengl.qdocconf | 2 +- src/sql/doc/qtsql.qdocconf | 2 +- src/tools/qdoc/doc/examples/examples.qdoc | 5 ++--- src/widgets/doc/qtwidgets.qdocconf | 2 +- src/xml/doc/qtxml.qdocconf | 2 +- 15 files changed, 18 insertions(+), 23 deletions(-) diff --git a/qmake/doc/src/qmake-manual.qdoc b/qmake/doc/src/qmake-manual.qdoc index a2afeaf765..ccb8d95973 100644 --- a/qmake/doc/src/qmake-manual.qdoc +++ b/qmake/doc/src/qmake-manual.qdoc @@ -728,8 +728,7 @@ \section2 Creating and Moving Xcode Projects Developers on OS X can take advantage of the qmake support for Xcode - project files, as described in - \l{Qt is OS X Native#Development Tools}{Qt is OS X Native}, + project files, as described in \l{Additional Command-Line Options}, by running qmake to generate an Xcode project from an existing qmake project file. For example: diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 8ad1c2852c..aa14f545ec 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -2858,7 +2858,7 @@ QTextStream &endl(QTextStream &stream) /*! \relates QTextStream - Calls \l{QTextStream::flush()}{flush()} on \a stream and returns \a stream. + Calls QTextStream::flush() on \a stream and returns \a stream. \sa endl(), reset(), {QTextStream manipulators} */ diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 6858209b12..1ef5ee0547 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1548,12 +1548,13 @@ bool QMetaObject::invokeMethod(QObject *obj, /*! \fn QMetaObject::Connection &QMetaObject::Connection::operator=(Connection &&other) - Move-assigns \a other to this object. + Move-assigns \a other to this object, and returns a reference. */ /*! \fn QMetaObject::Connection::Connection(Connection &&o) - Move-constructs a Connection instance, making it point to the same object that \a o was pointing to. + Move-constructs a Connection instance, making it point to the same object + that \a o was pointing to. */ /*! diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf index e2194839d2..436e2e0b34 100644 --- a/src/gui/doc/qtgui.qdocconf +++ b/src/gui/doc/qtgui.qdocconf @@ -37,7 +37,8 @@ depends += \ qtqml \ qtquick \ qtwidgets \ - qtdoc + qtdoc \ + qmake headerdirs += .. diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 176cdfe09f..c4691b5f5e 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -313,7 +313,7 @@ bool QImageData::checkForAlphaPixels() const sharing}. QImage objects can also be streamed and compared. \note If you would like to load QImage objects in a static build of Qt, - refer to the \l{How To Create Qt Plugins}{Plugin HowTo}. + refer to the \l{How to Create Qt Plugins}{Plugin HowTo}. \warning Painting on a QImage with the format QImage::Format_Indexed8 is not supported. diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index ba79bf40e5..a35442308f 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1170,7 +1170,7 @@ QImageIOHandler::Transformations QImageReader::transformation() const Sets if images returned by read() should have transformation metadata automatically applied. - \sa autoTransform(), transform(), read() + \sa autoTransform(), transformation(), read() */ void QImageReader::setAutoTransform(bool enabled) { diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index eb7c7f4b7a..9d363dc895 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -148,8 +148,6 @@ QMatrix4x4::QMatrix4x4(const float *values) top-most 4 rows of \a matrix. If \a matrix has less than 4 columns or rows, the remaining elements are filled with elements from the identity matrix. - - \sa QMatrix4x4(const QGenericMatrix &) */ /*! diff --git a/src/network/doc/qtnetwork.qdocconf b/src/network/doc/qtnetwork.qdocconf index 522d71fd27..2a8e577dda 100644 --- a/src/network/doc/qtnetwork.qdocconf +++ b/src/network/doc/qtnetwork.qdocconf @@ -26,7 +26,7 @@ qhp.QtNetwork.subprojects.classes.sortPages = true tagfile = ../../../doc/qtnetwork/qtnetwork.tags -depends += qtcore qtgui qtdoc +depends += qtcore qtgui qtdoc qmake headerdirs += .. diff --git a/src/network/ssl/qsslellipticcurve.cpp b/src/network/ssl/qsslellipticcurve.cpp index a16f726429..b4396d567b 100644 --- a/src/network/ssl/qsslellipticcurve.cpp +++ b/src/network/ssl/qsslellipticcurve.cpp @@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE elliptic-curve cipher algorithms. Elliptic curves can be constructed from a "short name" (SN) (fromShortName()), - and by a call to QSslSocket::supportedEllipticCurves(). + and by a call to QSslConfiguration::supportedEllipticCurves(). QSslEllipticCurve instances can be compared for equality and can be used as keys in QHash and QSet. They cannot be used as key in a QMap. @@ -65,7 +65,7 @@ QT_BEGIN_NAMESPACE Constructs an invalid elliptic curve. - \sa isValid(), QSslSocket::supportedEllipticCurves() + \sa isValid(), QSslConfiguration::supportedEllipticCurves() */ /*! @@ -136,7 +136,6 @@ QT_BEGIN_NAMESPACE \relates QSslEllipticCurve Returns true if the curve \a lhs represents the same curve of \a rhs; - false otherwise. */ /*! diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.cpp b/src/network/ssl/qsslpresharedkeyauthenticator.cpp index 4a3b1aa807..ab78aea1cd 100644 --- a/src/network/ssl/qsslpresharedkeyauthenticator.cpp +++ b/src/network/ssl/qsslpresharedkeyauthenticator.cpp @@ -257,7 +257,6 @@ int QSslPreSharedKeyAuthenticator::maximumPreSharedKeyLength() const identity hint, identity, pre shared key, maximum length for the identity and maximum length for the pre shared key. - \sa operator!=(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs) */ bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs) { @@ -277,7 +276,6 @@ bool operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKey Returns true if the authenticator object \a lhs is different than \a rhs; false otherwise. - \sa operator==(const QSslPreSharedKeyAuthenticator &lhs, const QSslPreSharedKeyAuthenticator &rhs) */ QT_END_NAMESPACE diff --git a/src/opengl/doc/qtopengl.qdocconf b/src/opengl/doc/qtopengl.qdocconf index 5b6d09dfcd..6ff6cae2cb 100644 --- a/src/opengl/doc/qtopengl.qdocconf +++ b/src/opengl/doc/qtopengl.qdocconf @@ -19,7 +19,7 @@ exampledirs += ../../../examples/opengl \ imagedirs += images \ ../../../examples/opengl/doc/images -depends += qtdoc qtcore qtgui qtwidgets +depends += qtdoc qtcore qtgui qtwidgets qmake examplesinstallpath = opengl diff --git a/src/sql/doc/qtsql.qdocconf b/src/sql/doc/qtsql.qdocconf index b8632c5260..5a224adeb9 100644 --- a/src/sql/doc/qtsql.qdocconf +++ b/src/sql/doc/qtsql.qdocconf @@ -25,7 +25,7 @@ qhp.QtSql.subprojects.classes.selectors = class fake:headerfile qhp.QtSql.subprojects.classes.sortPages = true tagfile = ../../../doc/qtsql/qtsql.tags -depends += qtcore qtwidgets qtdoc +depends += qtcore qtwidgets qtdoc qmake headerdirs += .. diff --git a/src/tools/qdoc/doc/examples/examples.qdoc b/src/tools/qdoc/doc/examples/examples.qdoc index 777c869c65..28810e30da 100644 --- a/src/tools/qdoc/doc/examples/examples.qdoc +++ b/src/tools/qdoc/doc/examples/examples.qdoc @@ -90,9 +90,8 @@ \brief Basic set of UI components This is a listing of a list of UI components implemented by QML types. These - - files are available for general import and they are based off the \l{Qt - Quick Code Samples}. + files are available for general import and they are based on the + \l{Qt Quick Examples and Tutorials}{Qt Quick Code Samples}. This module is part of the \l{componentset}{UIComponents} example. */ diff --git a/src/widgets/doc/qtwidgets.qdocconf b/src/widgets/doc/qtwidgets.qdocconf index 1f79d144bf..f307e9d3e4 100644 --- a/src/widgets/doc/qtwidgets.qdocconf +++ b/src/widgets/doc/qtwidgets.qdocconf @@ -26,7 +26,7 @@ qhp.QtWidgets.subprojects.classes.sortPages = true tagfile = ../../../doc/qtwidgets/qtwidgets.tags -depends += qtcore qtgui qtdoc qtsql qtdesigner qtquick +depends += qtcore qtgui qtdoc qtsql qtdesigner qtquick qmake qtsvg headerdirs += .. diff --git a/src/xml/doc/qtxml.qdocconf b/src/xml/doc/qtxml.qdocconf index 419859ac8b..a23915487f 100644 --- a/src/xml/doc/qtxml.qdocconf +++ b/src/xml/doc/qtxml.qdocconf @@ -26,7 +26,7 @@ qhp.QtXml.subprojects.classes.sortPages = true tagfile = ../../../doc/qtxml/qtxml.tags -depends += qtcore qtnetwork qtdoc qtwidgets +depends += qtcore qtnetwork qtdoc qtwidgets qmake headerdirs += .. From 5919edc5231f58d2d7c04ea875064a368f0df254 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Thu, 3 Sep 2015 15:52:55 +0300 Subject: [PATCH 05/28] forkfd: fix _POSIX_SPAWN feature check Change-Id: Ia44edbac3a1bd2da92ee8c92956abfe49d8763b8 Reviewed-by: Thiago Macieira --- src/3rdparty/forkfd/forkfd.c | 2 +- src/3rdparty/forkfd/forkfd.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c index a61198a954..55dc92fe45 100644 --- a/src/3rdparty/forkfd/forkfd.c +++ b/src/3rdparty/forkfd/forkfd.c @@ -648,7 +648,7 @@ err_free: } #endif // FORKFD_NO_FORKFD -#if defined(_POSIX_SPAWN) && !defined(FORKFD_NO_SPAWNFD) +#if _POSIX_SPAWN > 0 && !defined(FORKFD_NO_SPAWNFD) int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) { diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h index dcb36f9f33..38858c00fe 100644 --- a/src/3rdparty/forkfd/forkfd.h +++ b/src/3rdparty/forkfd/forkfd.h @@ -29,7 +29,7 @@ #include #include // to get the POSIX flags -#ifdef _POSIX_SPAWN +#if _POSIX_SPAWN > 0 # include #endif @@ -51,7 +51,7 @@ int forkfd(int flags, pid_t *ppid); int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage); int forkfd_close(int ffd); -#ifdef _POSIX_SPAWN +#if _POSIX_SPAWN > 0 /* only for spawnfd: */ # define FFD_SPAWN_SEARCH_PATH O_RDWR From ee8c09f7c219ae9b55194408ba854f81b8086cb0 Mon Sep 17 00:00:00 2001 From: David Faure Date: Fri, 31 Jul 2015 14:16:53 +0200 Subject: [PATCH 06/28] qprintdialog_unix: small optimization, no need to extract a substring Change-Id: Ie5fdfd6aa3866c14b6fbfabf4533327f5c73c5a3 Reviewed-by: Friedemann Kleint Reviewed-by: Frederik Gladhorn Reviewed-by: Marc Mutz --- src/printsupport/dialogs/qprintdialog_unix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/printsupport/dialogs/qprintdialog_unix.cpp b/src/printsupport/dialogs/qprintdialog_unix.cpp index 502ff7d98a..880b233aef 100644 --- a/src/printsupport/dialogs/qprintdialog_unix.cpp +++ b/src/printsupport/dialogs/qprintdialog_unix.cpp @@ -805,7 +805,7 @@ void QUnixPrintWidgetPrivate::applyPrinterProperties() home += QLatin1Char('/'); if (!cur.isEmpty() && cur.at(cur.length()-1) != QLatin1Char('/')) cur += QLatin1Char('/'); - if (cur.left(home.length()) != home) + if (!cur.startsWith(home)) cur = home; if (QGuiApplication::platformName() == QLatin1String("xcb")) { if (printer->docName().isEmpty()) { From 69e1b1de935511e5513fb002e2df96ac08e433f6 Mon Sep 17 00:00:00 2001 From: Nico Vertriest Date: Mon, 17 Aug 2015 13:01:09 +0200 Subject: [PATCH 07/28] Doc: System Tray Icon example Moved from qtsvg to qtbase Task-number: QTBUG-47201 Change-Id: Iab185ea2e270893c0937d1ff87fdb544d226d603 Reviewed-by: Martin Smith --- .../systray/doc/images/systemtray-editor.png | Bin 0 -> 18147 bytes .../systray/doc/images/systemtray-example.png | Bin 0 -> 47588 bytes .../desktop/systray/doc/src/systray.qdoc | 183 ++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 examples/widgets/desktop/systray/doc/images/systemtray-editor.png create mode 100644 examples/widgets/desktop/systray/doc/images/systemtray-example.png create mode 100644 examples/widgets/desktop/systray/doc/src/systray.qdoc diff --git a/examples/widgets/desktop/systray/doc/images/systemtray-editor.png b/examples/widgets/desktop/systray/doc/images/systemtray-editor.png new file mode 100644 index 0000000000000000000000000000000000000000..fb15dea8cb56f6e2d3f8be214e8de05df3735b7e GIT binary patch literal 18147 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�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 Date: Fri, 28 Aug 2015 15:46:07 +0200 Subject: [PATCH 08/28] iOS: compose key events from QKeySequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of hard-coding the key and modifier that should trigger a shortcut, we read it out from the QKeySequence that corresponds to a StandardKey instead. Change-Id: I6325534d3ff91c788d7e660d9009954e437b8534 Reviewed-by: Tor Arne Vestbø --- .../platforms/ios/qiostextresponder.mm | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 685ff8ff47..4e109f6921 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -333,65 +333,73 @@ #ifndef QT_NO_SHORTCUT +- (void)sendShortcut:(QKeySequence::StandardKey)standardKey +{ + const int keys = QKeySequence(standardKey)[0]; + Qt::Key key = Qt::Key(keys & 0x0000FFFF); + Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(keys & 0xFFFF0000); + [self sendKeyPressRelease:key modifiers:modifiers]; +} + - (void)cut:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_X modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Cut]; } - (void)copy:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_C modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Copy]; } - (void)paste:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_V modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Paste]; } - (void)selectAll:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_A modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::SelectAll]; } - (void)delete:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_Delete modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Delete]; } - (void)toggleBoldface:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_B modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Bold]; } - (void)toggleItalics:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_I modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Italic]; } - (void)toggleUnderline:(id)sender { Q_UNUSED(sender); - [self sendKeyPressRelease:Qt::Key_U modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Underline]; } // ------------------------------------------------------------------------- - (void)undo { - [self sendKeyPressRelease:Qt::Key_Z modifiers:Qt::ControlModifier]; + [self sendShortcut:QKeySequence::Undo]; [self rebuildUndoStack]; } - (void)redo { - [self sendKeyPressRelease:Qt::Key_Z modifiers:Qt::ControlModifier|Qt::ShiftModifier]; + [self sendShortcut:QKeySequence::Redo]; [self rebuildUndoStack]; } From feff56827b86ff00930a903944afe913a926a9a1 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 1 Sep 2015 13:06:07 +0200 Subject: [PATCH 09/28] iOS: filter responder actions from menu sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow up from b494d85. We need to filter the responder actions after a sync as well, otherwise they will be added back if a menu item e.g changes text. Change-Id: I2ecbcc292400ada97a8e29d4b97f087349d8a061 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/ios/qiosmenu.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/platforms/ios/qiosmenu.mm b/src/plugins/platforms/ios/qiosmenu.mm index de77097bf8..09395805bf 100644 --- a/src/plugins/platforms/ios/qiosmenu.mm +++ b/src/plugins/platforms/ios/qiosmenu.mm @@ -369,7 +369,7 @@ void QIOSMenu::syncMenuItem(QPlatformMenuItem *) switch (m_effectiveMenuType) { case EditMenu: - [m_menuController setVisibleMenuItems:visibleMenuItems()]; + [m_menuController setVisibleMenuItems:filterFirstResponderActions(visibleMenuItems())]; break; default: [m_pickerView setVisibleMenuItems:visibleMenuItems() selectItem:m_targetItem]; From 1a0384cb15f7a2194341ec4b102948fa9337f5cf Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sun, 16 Aug 2015 15:09:51 +0100 Subject: [PATCH 10/28] Support device pixel ratio in QWidget graphic effects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie20bd30328ae353ace82fbeee5808546f0568703 Reviewed-by: Christoph Cullmann Reviewed-by: Morten Johan Sørvig --- src/widgets/kernel/qwidget.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index ba0fcf75d0..49da4d1faf 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -5828,7 +5828,10 @@ QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint * pixmapOffset -= effectRect.topLeft(); - QPixmap pixmap(effectRect.size()); + const qreal dpr = context->painter->device()->devicePixelRatio(); + QPixmap pixmap(effectRect.size() * dpr); + pixmap.setDevicePixelRatio(dpr); + pixmap.fill(Qt::transparent); m_widget->render(&pixmap, pixmapOffset, QRegion(), QWidget::DrawChildren); return pixmap; From 880a8aa7e99bb91e7a815cadde72bb5230c815ea Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 26 Aug 2015 13:27:49 +0200 Subject: [PATCH 11/28] QMimeType: add KDAB copyright to the code I contributed a lot to. Change-Id: I794259f28c7adbaad3cfb40f92a0ad2dc512e5b4 Reviewed-by: Thiago Macieira --- src/corelib/mimetypes/qmimedatabase.cpp | 1 + src/corelib/mimetypes/qmimedatabase.h | 1 + src/corelib/mimetypes/qmimedatabase_p.h | 1 + src/corelib/mimetypes/qmimeprovider.cpp | 1 + src/corelib/mimetypes/qmimeprovider_p.h | 1 + src/corelib/mimetypes/qmimetype.cpp | 1 + src/corelib/mimetypes/qmimetype.h | 1 + 7 files changed, 7 insertions(+) diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp index bec7a79e8f..1ef890c46a 100644 --- a/src/corelib/mimetypes/qmimedatabase.cpp +++ b/src/corelib/mimetypes/qmimedatabase.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimedatabase.h b/src/corelib/mimetypes/qmimedatabase.h index 912d9b8443..fd19636e27 100644 --- a/src/corelib/mimetypes/qmimedatabase.h +++ b/src/corelib/mimetypes/qmimedatabase.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimedatabase_p.h b/src/corelib/mimetypes/qmimedatabase_p.h index e3cfe3443f..aa86f607c6 100644 --- a/src/corelib/mimetypes/qmimedatabase_p.h +++ b/src/corelib/mimetypes/qmimedatabase_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index 887db51e5c..d26bfd0848 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimeprovider_p.h b/src/corelib/mimetypes/qmimeprovider_p.h index eaf95942f7..c0517d69a4 100644 --- a/src/corelib/mimetypes/qmimeprovider_p.h +++ b/src/corelib/mimetypes/qmimeprovider_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp index a5f9cb70d5..5398f9358e 100644 --- a/src/corelib/mimetypes/qmimetype.cpp +++ b/src/corelib/mimetypes/qmimetype.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. diff --git a/src/corelib/mimetypes/qmimetype.h b/src/corelib/mimetypes/qmimetype.h index 4ba3c53470..13184905b8 100644 --- a/src/corelib/mimetypes/qmimetype.h +++ b/src/corelib/mimetypes/qmimetype.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. From 5e41f4137dd42a9a639be8743ae95c8e159bd4e0 Mon Sep 17 00:00:00 2001 From: David Faure Date: Tue, 25 Aug 2015 14:30:07 +0200 Subject: [PATCH 12/28] QMimeDatabase: warn instead of asserting on bad magic. An invalid mime magic definition could lead to an assert. Replaced with a qWarning. Move all checking to the QMimeMagicRule constructor, and do keep invalid rules since they are need to parse child rules. Unit test added, with QTest::ignoreMessage when using the XML backend (there's no warning from update-mime-database when using the cache). Also make it easier to add more shared mime info files for tests. Task-number: QTBUG-44319 Done-with: Eike Ziller Change-Id: Ie39a160a106b650cdcee88778fa7eff9e932a988 Reviewed-by: Thiago Macieira --- src/corelib/mimetypes/qmimemagicrule.cpp | 68 +++++++++++--- src/corelib/mimetypes/qmimemagicrule_p.h | 3 +- src/corelib/mimetypes/qmimetypeparser.cpp | 40 ++------ src/corelib/mimetypes/qmimetypeparser_p.h | 2 + .../qmimedatabase/invalid-magic1.xml | 9 ++ .../qmimedatabase/invalid-magic2.xml | 9 ++ .../qmimedatabase/invalid-magic3.xml | 9 ++ .../mimetypes/qmimedatabase/testdata.qrc | 3 + .../qmimedatabase/tst_qmimedatabase.cpp | 92 ++++++++++++------- .../qmimedatabase/tst_qmimedatabase.h | 6 +- 10 files changed, 159 insertions(+), 82 deletions(-) create mode 100644 tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml create mode 100644 tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml create mode 100644 tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index c8508ac0d2..6a3a429179 100644 --- a/src/corelib/mimetypes/qmimemagicrule.cpp +++ b/src/corelib/mimetypes/qmimemagicrule.cpp @@ -38,6 +38,7 @@ #ifndef QT_NO_MIMETYPE +#include "qmimetypeparser_p.h" #include #include #include @@ -231,26 +232,53 @@ static inline QByteArray makePattern(const QByteArray &value) return pattern; } -QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType, +// Evaluate a magic match rule like +// +// + +QMimeMagicRule::QMimeMagicRule(const QString &typeStr, const QByteArray &theValue, - int theStartPos, - int theEndPos, - const QByteArray &theMask) : + const QString &offsets, + const QByteArray &theMask, + QString *errorString) : d(new QMimeMagicRulePrivate) { - Q_ASSERT(!theValue.isEmpty()); - - d->type = theType; d->value = theValue; - d->startPos = theStartPos; - d->endPos = theEndPos; d->mask = theMask; d->matchFunction = 0; + d->type = QMimeMagicRule::type(typeStr.toLatin1()); + if (d->type == Invalid) { + *errorString = QStringLiteral("Type %s is not supported").arg(typeStr); + } + + // Parse for offset as "1" or "1:10" + const int colonIndex = offsets.indexOf(QLatin1Char(':')); + const QString startPosStr = colonIndex == -1 ? offsets : offsets.mid(0, colonIndex); + const QString endPosStr = colonIndex == -1 ? offsets : offsets.mid(colonIndex + 1); + if (!QMimeTypeParserBase::parseNumber(startPosStr, &d->startPos, errorString) || + !QMimeTypeParserBase::parseNumber(endPosStr, &d->endPos, errorString)) { + d->type = Invalid; + return; + } + + if (d->value.isEmpty()) { + d->type = Invalid; + if (errorString) + *errorString = QLatin1String("Invalid empty magic rule value"); + return; + } + if (d->type >= Host16 && d->type <= Byte) { bool ok; d->number = d->value.toUInt(&ok, 0); // autodetect - Q_ASSERT(ok); + if (!ok) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule value \"%1\"").arg( + QString::fromLatin1(d->value)); + return; + } d->numberMask = !d->mask.isEmpty() ? d->mask.toUInt(&ok, 0) : 0; // autodetect } @@ -259,9 +287,23 @@ QMimeMagicRule::QMimeMagicRule(QMimeMagicRule::Type theType, d->pattern = makePattern(d->value); d->pattern.squeeze(); if (!d->mask.isEmpty()) { - Q_ASSERT(d->mask.size() >= 4 && d->mask.startsWith("0x")); - d->mask = QByteArray::fromHex(QByteArray::fromRawData(d->mask.constData() + 2, d->mask.size() - 2)); - Q_ASSERT(d->mask.size() == d->pattern.size()); + if (d->mask.size() < 4 || !d->mask.startsWith("0x")) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule mask \"%1\"").arg( + QString::fromLatin1(d->mask)); + return; + } + const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData( + d->mask.constData() + 2, d->mask.size() - 2)); + if (tempMask.size() != d->pattern.size()) { + d->type = Invalid; + if (errorString) + *errorString = QString::fromLatin1("Invalid magic rule mask size \"%1\"").arg( + QString::fromLatin1(d->mask)); + return; + } + d->mask = tempMask; } else { d->mask.fill(char(-1), d->pattern.size()); } diff --git a/src/corelib/mimetypes/qmimemagicrule_p.h b/src/corelib/mimetypes/qmimemagicrule_p.h index 03ac1d1de9..6b64bfcc10 100644 --- a/src/corelib/mimetypes/qmimemagicrule_p.h +++ b/src/corelib/mimetypes/qmimemagicrule_p.h @@ -61,7 +61,8 @@ class QMimeMagicRule public: enum Type { Invalid = 0, String, Host16, Host32, Big16, Big32, Little16, Little32, Byte }; - QMimeMagicRule(Type type, const QByteArray &value, int startPos, int endPos, const QByteArray &mask = QByteArray()); + QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, + const QByteArray &mask, QString *errorString); QMimeMagicRule(const QMimeMagicRule &other); ~QMimeMagicRule(); diff --git a/src/corelib/mimetypes/qmimetypeparser.cpp b/src/corelib/mimetypes/qmimetypeparser.cpp index 9610162c4f..8a8b97655a 100644 --- a/src/corelib/mimetypes/qmimetypeparser.cpp +++ b/src/corelib/mimetypes/qmimetypeparser.cpp @@ -153,8 +153,8 @@ QMimeTypeParserBase::ParseState QMimeTypeParserBase::nextState(ParseState curren return ParseError; } -// Parse int number from an (attribute) string) -static bool parseNumber(const QString &n, int *target, QString *errorMessage) +// Parse int number from an (attribute) string +bool QMimeTypeParserBase::parseNumber(const QString &n, int *target, QString *errorMessage) { bool ok; *target = n.toInt(&ok); @@ -165,37 +165,14 @@ static bool parseNumber(const QString &n, int *target, QString *errorMessage) return true; } -// Evaluate a magic match rule like -// -// #ifndef QT_NO_XMLSTREAMREADER -static bool createMagicMatchRule(const QXmlStreamAttributes &atts, - QString *errorMessage, QMimeMagicRule *&rule) +static QMimeMagicRule *createMagicMatchRule(const QXmlStreamAttributes &atts, QString *errorMessage) { const QString type = atts.value(QLatin1String(matchTypeAttributeC)).toString(); - QMimeMagicRule::Type magicType = QMimeMagicRule::type(type.toLatin1()); - if (magicType == QMimeMagicRule::Invalid) { - qWarning("%s: match type %s is not supported.", Q_FUNC_INFO, type.toUtf8().constData()); - return true; - } const QString value = atts.value(QLatin1String(matchValueAttributeC)).toString(); - if (value.isEmpty()) { - *errorMessage = QString::fromLatin1("Empty match value detected."); - return false; - } - // Parse for offset as "1" or "1:10" - int startPos, endPos; - const QString offsetS = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); - const int colonIndex = offsetS.indexOf(QLatin1Char(':')); - const QString startPosS = colonIndex == -1 ? offsetS : offsetS.mid(0, colonIndex); - const QString endPosS = colonIndex == -1 ? offsetS : offsetS.mid(colonIndex + 1); - if (!parseNumber(startPosS, &startPos, errorMessage) || !parseNumber(endPosS, &endPos, errorMessage)) - return false; + const QString offsets = atts.value(QLatin1String(matchOffsetAttributeC)).toString(); const QString mask = atts.value(QLatin1String(matchMaskAttributeC)).toString(); - - rule = new QMimeMagicRule(magicType, value.toUtf8(), startPos, endPos, mask.toLatin1()); - - return true; + return new QMimeMagicRule(type, value.toUtf8(), offsets, mask.toLatin1(), errorMessage); } #endif @@ -283,9 +260,10 @@ bool QMimeTypeParserBase::parse(QIODevice *dev, const QString &fileName, QString } break; case ParseMagicMatchRule: { - QMimeMagicRule *rule = 0; - if (!createMagicMatchRule(atts, errorMessage, rule)) - return false; + QString magicErrorMessage; + QMimeMagicRule *rule = createMagicMatchRule(atts, &magicErrorMessage); + if (!rule->isValid()) + qWarning("QMimeDatabase: Error parsing %s\n%s", qPrintable(fileName), qPrintable(magicErrorMessage)); QList *ruleList; if (currentRules.isEmpty()) ruleList = &rules; diff --git a/src/corelib/mimetypes/qmimetypeparser_p.h b/src/corelib/mimetypes/qmimetypeparser_p.h index 2be4380cee..3a2e6b8a14 100644 --- a/src/corelib/mimetypes/qmimetypeparser_p.h +++ b/src/corelib/mimetypes/qmimetypeparser_p.h @@ -66,6 +66,8 @@ public: bool parse(QIODevice *dev, const QString &fileName, QString *errorMessage); + static bool parseNumber(const QString &n, int *target, QString *errorMessage); + protected: virtual bool process(const QMimeType &t, QString *errorMessage) = 0; virtual bool process(const QMimeGlobPattern &t, QString *errorMessage) = 0; diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml new file mode 100644 index 0000000000..04204d8763 --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic1.xml @@ -0,0 +1,9 @@ + + + + wrong value for byte type + + + + + diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml new file mode 100644 index 0000000000..f18075482e --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic2.xml @@ -0,0 +1,9 @@ + + + + mask doesn't start with 0x + + + + + diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml new file mode 100644 index 0000000000..0e2508cc0f --- /dev/null +++ b/tests/auto/corelib/mimetypes/qmimedatabase/invalid-magic3.xml @@ -0,0 +1,9 @@ + + + + mask has wrong size + + + + + diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc index 4654a61660..29666627a1 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc +++ b/tests/auto/corelib/mimetypes/qmimedatabase/testdata.qrc @@ -4,5 +4,8 @@ qml-again.xml text-x-objcsrc.xml test.qml + invalid-magic1.xml + invalid-magic2.xml + invalid-magic3.xml diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp index 8bc90d917e..763bb58602 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.cpp @@ -45,9 +45,16 @@ #include -static const char yastFileName[] ="yast2-metapackage-handler-mimetypes.xml"; -static const char qmlAgainFileName[] ="qml-again.xml"; -static const char textXObjCSrcFileName[] ="text-x-objcsrc.xml"; +static const char *const additionalMimeFiles[] = { + "yast2-metapackage-handler-mimetypes.xml", + "qml-again.xml", + "text-x-objcsrc.xml", + "invalid-magic1.xml", + "invalid-magic2.xml", + "invalid-magic3.xml", + 0 +}; + #define RESOURCE_PREFIX ":/qt-project.org/qmime/" void initializeLang() @@ -149,12 +156,12 @@ void tst_QMimeDatabase::initTestCase() qWarning("%s", qPrintable(testSuiteWarning())); errorMessage = QString::fromLatin1("Cannot find '%1'"); - m_yastMimeTypes = QLatin1String(RESOURCE_PREFIX) + yastFileName; - QVERIFY2(QFile::exists(m_yastMimeTypes), qPrintable(errorMessage.arg(yastFileName))); - m_qmlAgainFileName = QLatin1String(RESOURCE_PREFIX) + qmlAgainFileName; - QVERIFY2(QFile::exists(m_qmlAgainFileName), qPrintable(errorMessage.arg(qmlAgainFileName))); - m_textXObjCSrcFileName = QLatin1String(RESOURCE_PREFIX) + textXObjCSrcFileName; - QVERIFY2(QFile::exists(m_textXObjCSrcFileName), qPrintable(errorMessage.arg(textXObjCSrcFileName))); + for (uint i = 0; i < sizeof additionalMimeFiles / sizeof additionalMimeFiles[0] - 1; i++) { + const QString resourceFilePath = QString::fromLatin1(RESOURCE_PREFIX) + QLatin1String(additionalMimeFiles[i]); + QVERIFY2(QFile::exists(resourceFilePath), qPrintable(errorMessage.arg(resourceFilePath))); + m_additionalMimeFileNames.append(QLatin1String(additionalMimeFiles[i])); + m_additionalMimeFilePaths.append(resourceFilePath); + } initTestCaseInternal(); m_isUsingCacheProvider = !qEnvironmentVariableIsSet("QT_NO_MIME_CACHE"); @@ -859,6 +866,14 @@ static void checkHasMimeType(const QString &mimeType) QVERIFY(found); } +static void ignoreInvalidMimetypeWarnings(const QString &mimeDir) +{ + const QByteArray basePath = QFile::encodeName(mimeDir) + "/packages/"; + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic1.xml\nInvalid magic rule value \"foo\"").constData()); + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic2.xml\nInvalid magic rule mask \"ffff\"").constData()); + QTest::ignoreMessage(QtWarningMsg, ("QMimeDatabase: Error parsing " + basePath + "invalid-magic3.xml\nInvalid magic rule mask size \"0xffff\"").constData()); +} + QT_BEGIN_NAMESPACE extern Q_CORE_EXPORT int qmime_secondsBetweenChecks; // see qmimeprovider.cpp QT_END_NAMESPACE @@ -879,23 +894,21 @@ void tst_QMimeDatabase::installNewGlobalMimeType() const QString mimeDir = m_globalXdgDir + QLatin1String("/mime"); const QString destDir = mimeDir + QLatin1String("/packages/"); - const QString destFile = destDir + QLatin1String(yastFileName); - QFile::remove(destFile); - const QString destQmlFile = destDir + QLatin1String(qmlAgainFileName); - QFile::remove(destQmlFile); - const QString destTextXObjCSrcFile = destDir + QLatin1String(textXObjCSrcFileName); - QFile::remove(destTextXObjCSrcFile); - //qDebug() << destFile; - if (!QFileInfo(destDir).isDir()) QVERIFY(QDir(m_globalXdgDir).mkpath(destDir)); + QString errorMessage; - QVERIFY2(copyResourceFile(m_yastMimeTypes, destFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_qmlAgainFileName, destQmlFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_textXObjCSrcFileName, destTextXObjCSrcFile, &errorMessage), qPrintable(errorMessage)); + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) { + const QString destFile = destDir + m_additionalMimeFileNames.at(i); + QFile::remove(destFile); + QVERIFY2(copyResourceFile(m_additionalMimeFilePaths.at(i), destFile, &errorMessage), qPrintable(errorMessage)); + } if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(mimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); + if (!m_isUsingCacheProvider) + ignoreInvalidMimetypeWarnings(mimeDir); + QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("text/x-SuSE-ymu")); QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); @@ -916,10 +929,9 @@ void tst_QMimeDatabase::installNewGlobalMimeType() qDebug() << objcsrc.globPatterns(); } - // Now test removing it again - QVERIFY(QFile::remove(destFile)); - QVERIFY(QFile::remove(destQmlFile)); - QVERIFY(QFile::remove(destTextXObjCSrcFile)); + // Now test removing the mimetype definitions again + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) + QFile::remove(destDir + m_additionalMimeFileNames.at(i)); if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(mimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), @@ -936,23 +948,35 @@ void tst_QMimeDatabase::installNewLocalMimeType() qmime_secondsBetweenChecks = 0; QMimeDatabase db; + + // Check that we're starting clean QVERIFY(!db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); + QVERIFY(!db.mimeTypeForName(QLatin1String("text/invalid-magic1")).isValid()); const QString destDir = m_localMimeDir + QLatin1String("/packages/"); - QDir().mkpath(destDir); - const QString destFile = destDir + QLatin1String(yastFileName); - QFile::remove(destFile); - const QString destQmlFile = destDir + QLatin1String(qmlAgainFileName); - QFile::remove(destQmlFile); + QVERIFY(QDir().mkpath(destDir)); QString errorMessage; - QVERIFY2(copyResourceFile(m_yastMimeTypes, destFile, &errorMessage), qPrintable(errorMessage)); - QVERIFY2(copyResourceFile(m_qmlAgainFileName, destQmlFile, &errorMessage), qPrintable(errorMessage)); - if (m_isUsingCacheProvider && !runUpdateMimeDatabase(m_localMimeDir)) { + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) { + const QString destFile = destDir + m_additionalMimeFileNames.at(i); + QFile::remove(destFile); + QVERIFY2(copyResourceFile(m_additionalMimeFilePaths.at(i), destFile, &errorMessage), qPrintable(errorMessage)); + } + if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(m_localMimeDir)) { const QString skipWarning = QStringLiteral("shared-mime-info not found, skipping mime.cache test (") + QDir::toNativeSeparators(m_localMimeDir) + QLatin1Char(')'); QSKIP(qPrintable(skipWarning)); } + if (!m_isUsingCacheProvider) + ignoreInvalidMimetypeWarnings(m_localMimeDir); + + QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); + + // These mimetypes have invalid magic, but still do exist. + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic1")).isValid()); + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic2")).isValid()); + QVERIFY(db.mimeTypeForName(QLatin1String("text/invalid-magic3")).isValid()); + QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), QString::fromLatin1("text/x-SuSE-ymu")); QVERIFY(db.mimeTypeForName(QLatin1String("text/x-suse-ymp")).isValid()); @@ -968,8 +992,8 @@ void tst_QMimeDatabase::installNewLocalMimeType() QString::fromLatin1("text/x-qml")); // Now test removing the local mimetypes again (note, this leaves a mostly-empty mime.cache file) - QVERIFY(QFile::remove(destFile)); - QVERIFY(QFile::remove(destQmlFile)); + for (int i = 0; i < m_additionalMimeFileNames.size(); ++i) + QFile::remove(destDir + m_additionalMimeFileNames.at(i)); if (m_isUsingCacheProvider && !waitAndRunUpdateMimeDatabase(m_localMimeDir)) QSKIP("shared-mime-info not found, skipping mime.cache test"); QCOMPARE(db.mimeTypeForFile(QLatin1String("foo.ymu"), QMimeDatabase::MatchExtension).name(), diff --git a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h index f12beafe86..2827bd2dc4 100644 --- a/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h +++ b/tests/auto/corelib/mimetypes/qmimedatabase/tst_qmimedatabase.h @@ -36,6 +36,7 @@ #include #include +#include class tst_QMimeDatabase : public QObject { @@ -92,9 +93,8 @@ private: QString m_globalXdgDir; QString m_localMimeDir; - QString m_yastMimeTypes; - QString m_qmlAgainFileName; - QString m_textXObjCSrcFileName; + QStringList m_additionalMimeFileNames; + QStringList m_additionalMimeFilePaths; QTemporaryDir m_temporaryDir; QString m_testSuite; bool m_isUsingCacheProvider; From 750509c3d11324a8371aeefc389f8b6ba7ed8a2e Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 12 Aug 2015 15:25:17 +0200 Subject: [PATCH 13/28] Fix compilation with QNETWORKACCESSHTTPBACKEND_DEBUG enabled Change-Id: I868e8ecdff6a503ee891a257121bf14a7da77fec Reviewed-by: Markus Goetz (Woboq GmbH) Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/network/access/qnetworkreplyhttpimpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index c445677792..abc6603be2 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1152,7 +1152,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData if (statusCode == 304) { #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - qDebug() << "Received a 304 from" << url(); + qDebug() << "Received a 304 from" << request.url(); #endif QAbstractNetworkCache *nc = managerPrivate->networkCache; if (nc) { @@ -1457,7 +1457,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe } #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG) - QByteArray n = rawHeader(header); + QByteArray n = q->rawHeader(header); QByteArray o; if (it != cacheHeaders.rawHeaders.constEnd()) o = (*it).second; From cb12da0387458fb63a80a4f59d5ddb24d33ad59b Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 2 Sep 2015 18:30:29 +0300 Subject: [PATCH 14/28] Android: Don't use predictive text when selecting file names Using predictive text will not refresh the selected files until a word is completed or the keyboard is hidden. Task-number: QTBUG-44337 Change-Id: I9e9f1e760fe5d5a69abd6e112af55b217ae6a16d Reviewed-by: Shawn Rutledge --- src/widgets/dialogs/qfiledialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 64e3f5069e..c9e6a199c4 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -2851,6 +2851,9 @@ void QFileDialogPrivate::createWidgets() completer = new QFSCompleter(model, q); qFileDialogUi->fileNameEdit->setCompleter(completer); #endif // QT_NO_FSCOMPLETER + + qFileDialogUi->fileNameEdit->setInputMethodHints(Qt::ImhNoPredictiveText); + QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), q, SLOT(_q_autoCompleteFileName(QString))); QObject::connect(qFileDialogUi->fileNameEdit, SIGNAL(textChanged(QString)), From 6413ceeccff9078bc8ec2dcb63b445c031c420dd Mon Sep 17 00:00:00 2001 From: Kari Pihkala Date: Sat, 2 Aug 2014 09:59:18 +0300 Subject: [PATCH 15/28] Fix default hotspot of a hidpi QCursor The hotspot is defined in device independent coordinates, so the default coordinates need to be divided by device pixel ratio. Also, modify the scaling of cursor's pixmap to use SmoothTransformation to generate cleaner looking lodpi cursors from hidpi cursors. Change-Id: Ia938fd1e476e19e796f30712e23b06a5efed9964 Task-number: QTBUG-34116 Reviewed-by: Jake Petroules --- src/gui/kernel/qcursor.cpp | 10 +++++----- src/gui/kernel/qcursor_p.h | 3 ++- src/plugins/platforms/cocoa/qcocoacursor.mm | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/gui/kernel/qcursor.cpp b/src/gui/kernel/qcursor.cpp index 6ed750eda1..6b01952647 100644 --- a/src/gui/kernel/qcursor.cpp +++ b/src/gui/kernel/qcursor.cpp @@ -387,7 +387,7 @@ QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY) bmm.fill(Qt::color1); } - d = QCursorData::setBitmap(bm, bmm, hotX, hotY); + d = QCursorData::setBitmap(bm, bmm, hotX, hotY, pixmap.devicePixelRatio()); d->pixmap = pixmap; } @@ -430,7 +430,7 @@ QCursor::QCursor(const QPixmap &pixmap, int hotX, int hotY) QCursor::QCursor(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) : d(0) { - d = QCursorData::setBitmap(bitmap, mask, hotX, hotY); + d = QCursorData::setBitmap(bitmap, mask, hotX, hotY, 1.0); } /*! @@ -650,7 +650,7 @@ void QCursorData::initialize() QCursorData::initialized = true; } -QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY) +QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY, qreal devicePixelRatio) { if (!QCursorData::initialized) QCursorData::initialize(); @@ -664,8 +664,8 @@ QCursorData *QCursorData::setBitmap(const QBitmap &bitmap, const QBitmap &mask, d->bm = new QBitmap(bitmap); d->bmm = new QBitmap(mask); d->cshape = Qt::BitmapCursor; - d->hx = hotX >= 0 ? hotX : bitmap.width() / 2; - d->hy = hotY >= 0 ? hotY : bitmap.height() / 2; + d->hx = hotX >= 0 ? hotX : bitmap.width() / 2 / devicePixelRatio; + d->hy = hotY >= 0 ? hotY : bitmap.height() / 2 / devicePixelRatio; return d; } diff --git a/src/gui/kernel/qcursor_p.h b/src/gui/kernel/qcursor_p.h index 0aaa62b891..188ea387b3 100644 --- a/src/gui/kernel/qcursor_p.h +++ b/src/gui/kernel/qcursor_p.h @@ -70,7 +70,8 @@ public: short hx, hy; static bool initialized; void update(); - static QCursorData *setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY); + static QCursorData *setBitmap(const QBitmap &bitmap, const QBitmap &mask, int hotX, int hotY, + qreal devicePixelRatio); }; extern QCursorData *qt_cursorTable[Qt::LastCursor + 1]; // qcursor.cpp diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm index 922809f90d..f08386d18e 100644 --- a/src/plugins/platforms/cocoa/qcocoacursor.mm +++ b/src/plugins/platforms/cocoa/qcocoacursor.mm @@ -304,7 +304,7 @@ NSCursor *QCocoaCursor::createCursorFromPixmap(const QPixmap pixmap, const QPoin NSImage *nsimage; if (pixmap.devicePixelRatio() > 1.0) { QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio(); - QPixmap scaledPixmap = pixmap.scaled(layoutSize); + QPixmap scaledPixmap = pixmap.scaled(layoutSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); nsimage = static_cast(qt_mac_create_nsimage(scaledPixmap)); CGImageRef cgImage = qt_mac_toCGImage(pixmap.toImage()); NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; From 1a5cc2a868969f1b1e9278c4145b7af2fc4b8f69 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Mon, 24 Aug 2015 12:22:52 +0200 Subject: [PATCH 16/28] Cocoa: correct QDesktopWidget::availableGeometry() Since 10.9, System Preferences->Mission Control->Displays have separate Spaces settings needs to be followed. Task-number: QTBUG-47030 Change-Id: I1c1cf326246bd5609090ce5ac3212d963d562593 Reviewed-by: Jake Petroules --- src/plugins/platforms/cocoa/qcocoaintegration.mm | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index c8f6dd05db..8f84539e04 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -86,9 +86,17 @@ void QCocoaScreen::updateGeometry() NSRect frameRect = [nsScreen frame]; - if (m_screenIndex == 0) { + // Since Mavericks, there is a setting, System Preferences->Mission Control-> + // Displays have separate Spaces. + BOOL spansDisplays = NO; +#if QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_9) + spansDisplays = [NSScreen screensHaveSeparateSpaces]; +#endif + if (spansDisplays || m_screenIndex == 0) { m_geometry = QRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height); - // This is the primary screen, the one that contains the menubar. Its origin should be + // Displays have separate Spaces setting is on or this is the primary screen, + // the one that contains the menubar. Its origin should be // (0, 0), and it's the only one whose available geometry differs from its full geometry. NSRect visibleRect = [nsScreen visibleFrame]; m_availableGeometry = QRect(visibleRect.origin.x, From 0e895c47b049fffd58abff3f61681e94ba5515ec Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 25 Aug 2015 13:55:12 +0200 Subject: [PATCH 17/28] Doc: Update links to Qt Creator Manual MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're now at 3.5 level: - Fixed changed filenames and topic titles - Removed links to obsolete pages - Added links to new pages Task-number: QTBUG-47901 Change-Id: I3393555c86bf3cf58770f230e7a9c7fd7e258476 Reviewed-by: Topi Reiniö --- doc/global/externalsites/qtcreator.qdoc | 44 ++++++++++++++++--------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/doc/global/externalsites/qtcreator.qdoc b/doc/global/externalsites/qtcreator.qdoc index 200ef307ca..95200622f3 100644 --- a/doc/global/externalsites/qtcreator.qdoc +++ b/doc/global/externalsites/qtcreator.qdoc @@ -27,24 +27,16 @@ /*! \externalpage http://doc.qt.io/qtcreator/creator-deployment-qnx.html - \title Qt Creator: Deploying Applications to QNX Devices + \title Qt Creator: Deploying Applications to QNX Neutrino Devices */ /*! \externalpage http://doc.qt.io/qtcreator/creator-developing-baremetal.html \title Qt Creator: Connecting Bare Metal Devices */ -/*! - \externalpage http://doc.qt.io/qtcreator/creator-developing-bb10.html - \title Qt Creator: Connecting BlackBerry 10 Devices -*/ /*! \externalpage http://doc.qt.io/qtcreator/creator-developing-qnx.html \title Qt Creator: Connecting QNX Devices */ -/*! - \externalpage http://doc.qt.io/qtcreator/creator-deployment-bb10.html - \title Qt Creator: Deploying Applications to BlackBerry 10 Devices -*/ /*! \externalpage http://doc.qt.io/qtcreator/creator-developing-generic-linux.html \title Qt Creator: Connecting Embedded Linux Devices @@ -98,7 +90,19 @@ \title Qt Creator: Creating Screens */ /*! - \externalpage http://doc.qt.io/qtcreator/creator-qml-application.html + \externalpage http://doc.qt.io/qtcreator/creator-qtquick-designer-extensions.html + \title Qt Creator: Using Qt Quick Designer Extensions +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/qmldesigner-pathview-editor.html + \title Qt Creator: Editing PathView Properties +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/qmldesigner-connections.html + \title Qt Creator: Adding Connections +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/qtcreator-transitions-example.html \title Qt Creator: Creating a Qt Quick Application */ /*! @@ -279,7 +283,7 @@ */ /*! \externalpage http://doc.qt.io/qtcreator/creator-testing.html - \title Qt Creator: Debugging and Analyzing + \title Qt Creator: Testing */ /*! \externalpage http://doc.qt.io/qtcreator/creator-deployment.html @@ -297,10 +301,6 @@ \externalpage http://doc.qt.io/qtcreator/creator-design-mode.html \title Qt Creator: Designing User Interfaces */ -/*! - \externalpage http://doc.qt.io/qtcreator/creator-publish-ovi.html - \title Qt Creator: Publishing -*/ /*! \externalpage http://doc.qt.io/qtcreator/creator-glossary.html \title Qt Creator: Glossary @@ -476,7 +476,7 @@ \title Qt Creator: Adding Debuggers */ /*! - \externalpage http://doc.qt.io/qtcreator/creator-mobile-app-tutorial.html + \externalpage http://doc.qt.io/qtcreator/qtcreator-accelbubble-example.html \title Qt Creator: Creating a Mobile Application */ /*! @@ -499,7 +499,19 @@ \externalpage http://doc.qt.io/qtcreator/creator-quick-ui-forms.html \title Qt Creator: Qt Quick UI Forms */ +/*! + \externalpage http://doc.qt.io/qtcreator/qtcreator-uiforms-example.html + \title Qt Creator: Using Qt Quick UI Forms +*/ /*! \externalpage http://doc.qt.io/qtcreator/creator-clang-static-analyzer.html \title Qt Creator: Using Clang Static Analyzer */ +/*! + \externalpage http://doc.qt.io/qtcreator/creator-cpu-usage-analyzer.html + \title Qt Creator: Analyzing CPU Usage +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/creator-autotest.html + \title Qt Creator: Running Autotests +*/ From 0552331d0e42e7b73f36e31ffd3cc511b50d2ec0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 3 Aug 2015 10:07:17 +0200 Subject: [PATCH 18/28] Do not install headers for private classes When merging the accessibility plugin into the widgets library, the headers were just moved. They should have gotten the _p at that time. Task-number: QTBUG-47569 Change-Id: I0a2290dae3a8187596e9d7541ccf69beeb603296 Reviewed-by: Dimitar Dobrev Reviewed-by: Thiago Macieira --- src/widgets/accessible/accessible.pri | 12 ++++++------ src/widgets/accessible/complexwidgets.cpp | 2 +- .../{complexwidgets.h => complexwidgets_p.h} | 11 +++++++++++ src/widgets/accessible/itemviews.cpp | 2 +- .../accessible/{itemviews.h => itemviews_p.h} | 11 +++++++++++ src/widgets/accessible/qaccessiblemenu.cpp | 2 +- .../{qaccessiblemenu.h => qaccessiblemenu_p.h} | 11 +++++++++++ src/widgets/accessible/qaccessiblewidgetfactory.cpp | 12 ++++++------ src/widgets/accessible/qaccessiblewidgetfactory_p.h | 11 +++++++++++ src/widgets/accessible/qaccessiblewidgets.cpp | 2 +- .../{qaccessiblewidgets.h => qaccessiblewidgets_p.h} | 11 +++++++++++ src/widgets/accessible/rangecontrols.cpp | 4 ++-- .../{rangecontrols.h => rangecontrols_p.h} | 11 +++++++++++ src/widgets/accessible/simplewidgets.cpp | 2 +- .../{simplewidgets.h => simplewidgets_p.h} | 11 +++++++++++ 15 files changed, 96 insertions(+), 19 deletions(-) rename src/widgets/accessible/{complexwidgets.h => complexwidgets_p.h} (94%) rename src/widgets/accessible/{itemviews.h => itemviews_p.h} (97%) rename src/widgets/accessible/{qaccessiblemenu.h => qaccessiblemenu_p.h} (94%) rename src/widgets/accessible/{qaccessiblewidgets.h => qaccessiblewidgets_p.h} (97%) rename src/widgets/accessible/{rangecontrols.h => rangecontrols_p.h} (96%) rename src/widgets/accessible/{simplewidgets.h => simplewidgets_p.h} (96%) diff --git a/src/widgets/accessible/accessible.pri b/src/widgets/accessible/accessible.pri index bcdfbd639c..ac8205b1e3 100644 --- a/src/widgets/accessible/accessible.pri +++ b/src/widgets/accessible/accessible.pri @@ -4,12 +4,12 @@ contains(QT_CONFIG, accessibility) { HEADERS += \ accessible/qaccessiblewidget.h \ accessible/qaccessiblewidgetfactory_p.h \ - accessible/complexwidgets.h \ - accessible/itemviews.h \ - accessible/qaccessiblemenu.h \ - accessible/qaccessiblewidgets.h \ - accessible/rangecontrols.h \ - accessible/simplewidgets.h + accessible/complexwidgets_p.h \ + accessible/itemviews_p.h \ + accessible/qaccessiblemenu_p.h \ + accessible/qaccessiblewidgets_p.h \ + accessible/rangecontrols_p.h \ + accessible/simplewidgets_p.h SOURCES += \ accessible/qaccessiblewidget.cpp \ diff --git a/src/widgets/accessible/complexwidgets.cpp b/src/widgets/accessible/complexwidgets.cpp index 60a1329d5e..6e985c86e8 100644 --- a/src/widgets/accessible/complexwidgets.cpp +++ b/src/widgets/accessible/complexwidgets.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "complexwidgets.h" +#include "complexwidgets_p.h" #include #include diff --git a/src/widgets/accessible/complexwidgets.h b/src/widgets/accessible/complexwidgets_p.h similarity index 94% rename from src/widgets/accessible/complexwidgets.h rename to src/widgets/accessible/complexwidgets_p.h index bd063e7517..8edf996818 100644 --- a/src/widgets/accessible/complexwidgets.h +++ b/src/widgets/accessible/complexwidgets_p.h @@ -34,6 +34,17 @@ #ifndef COMPLEXWIDGETS_H #define COMPLEXWIDGETS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include #include #include diff --git a/src/widgets/accessible/itemviews.cpp b/src/widgets/accessible/itemviews.cpp index 4f6cb56060..6534d51a45 100644 --- a/src/widgets/accessible/itemviews.cpp +++ b/src/widgets/accessible/itemviews.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "itemviews.h" +#include "itemviews_p.h" #include #include diff --git a/src/widgets/accessible/itemviews.h b/src/widgets/accessible/itemviews_p.h similarity index 97% rename from src/widgets/accessible/itemviews.h rename to src/widgets/accessible/itemviews_p.h index c1bd70a390..a1b6a6db9f 100644 --- a/src/widgets/accessible/itemviews.h +++ b/src/widgets/accessible/itemviews_p.h @@ -34,6 +34,17 @@ #ifndef ACCESSIBLE_ITEMVIEWS_H #define ACCESSIBLE_ITEMVIEWS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include "QtCore/qpointer.h" #include #include diff --git a/src/widgets/accessible/qaccessiblemenu.cpp b/src/widgets/accessible/qaccessiblemenu.cpp index 72eb82b2b7..a0a7852851 100644 --- a/src/widgets/accessible/qaccessiblemenu.cpp +++ b/src/widgets/accessible/qaccessiblemenu.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "qaccessiblemenu.h" +#include "qaccessiblemenu_p.h" #include #include diff --git a/src/widgets/accessible/qaccessiblemenu.h b/src/widgets/accessible/qaccessiblemenu_p.h similarity index 94% rename from src/widgets/accessible/qaccessiblemenu.h rename to src/widgets/accessible/qaccessiblemenu_p.h index 9c7671072d..b42c852ff1 100644 --- a/src/widgets/accessible/qaccessiblemenu.h +++ b/src/widgets/accessible/qaccessiblemenu_p.h @@ -34,6 +34,17 @@ #ifndef QACCESSIBLEMENU_H #define QACCESSIBLEMENU_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include #include diff --git a/src/widgets/accessible/qaccessiblewidgetfactory.cpp b/src/widgets/accessible/qaccessiblewidgetfactory.cpp index e8b325b93f..4fa7c89482 100644 --- a/src/widgets/accessible/qaccessiblewidgetfactory.cpp +++ b/src/widgets/accessible/qaccessiblewidgetfactory.cpp @@ -31,12 +31,12 @@ ** ****************************************************************************/ -#include "qaccessiblewidgets.h" -#include "qaccessiblemenu.h" -#include "simplewidgets.h" -#include "rangecontrols.h" -#include "complexwidgets.h" -#include "itemviews.h" +#include "qaccessiblewidgets_p.h" +#include "qaccessiblemenu_p.h" +#include "simplewidgets_p.h" +#include "rangecontrols_p.h" +#include "complexwidgets_p.h" +#include "itemviews_p.h" #include #include diff --git a/src/widgets/accessible/qaccessiblewidgetfactory_p.h b/src/widgets/accessible/qaccessiblewidgetfactory_p.h index b3f9d8a251..caa37dcf81 100644 --- a/src/widgets/accessible/qaccessiblewidgetfactory_p.h +++ b/src/widgets/accessible/qaccessiblewidgetfactory_p.h @@ -36,6 +36,17 @@ #ifndef QACCESSIBLEWIDGETFACTORY_H #define QACCESSIBLEWIDGETFACTORY_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + QT_BEGIN_NAMESPACE QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *object); diff --git a/src/widgets/accessible/qaccessiblewidgets.cpp b/src/widgets/accessible/qaccessiblewidgets.cpp index a2bf14b25b..604c754ea1 100644 --- a/src/widgets/accessible/qaccessiblewidgets.cpp +++ b/src/widgets/accessible/qaccessiblewidgets.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "qaccessiblewidgets.h" +#include "qaccessiblewidgets_p.h" #include "qabstracttextdocumentlayout.h" #include "qapplication.h" #include "qclipboard.h" diff --git a/src/widgets/accessible/qaccessiblewidgets.h b/src/widgets/accessible/qaccessiblewidgets_p.h similarity index 97% rename from src/widgets/accessible/qaccessiblewidgets.h rename to src/widgets/accessible/qaccessiblewidgets_p.h index 53f8c2c603..4bdc229578 100644 --- a/src/widgets/accessible/qaccessiblewidgets.h +++ b/src/widgets/accessible/qaccessiblewidgets_p.h @@ -34,6 +34,17 @@ #ifndef QACCESSIBLEWIDGETS_H #define QACCESSIBLEWIDGETS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include #ifndef QT_NO_ACCESSIBILITY diff --git a/src/widgets/accessible/rangecontrols.cpp b/src/widgets/accessible/rangecontrols.cpp index 002d4a9ef4..0607a35269 100644 --- a/src/widgets/accessible/rangecontrols.cpp +++ b/src/widgets/accessible/rangecontrols.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "rangecontrols.h" +#include "rangecontrols_p.h" #include #include @@ -47,7 +47,7 @@ #include #include -#include "simplewidgets.h" // let spinbox use line edit's interface +#include "simplewidgets_p.h" // let spinbox use line edit's interface QT_BEGIN_NAMESPACE diff --git a/src/widgets/accessible/rangecontrols.h b/src/widgets/accessible/rangecontrols_p.h similarity index 96% rename from src/widgets/accessible/rangecontrols.h rename to src/widgets/accessible/rangecontrols_p.h index 11d4435e9d..32c6d6985f 100644 --- a/src/widgets/accessible/rangecontrols.h +++ b/src/widgets/accessible/rangecontrols_p.h @@ -34,6 +34,17 @@ #ifndef RANGECONTROLS_H #define RANGECONTROLS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include QT_BEGIN_NAMESPACE diff --git a/src/widgets/accessible/simplewidgets.cpp b/src/widgets/accessible/simplewidgets.cpp index b08c21939a..065c618cf7 100644 --- a/src/widgets/accessible/simplewidgets.cpp +++ b/src/widgets/accessible/simplewidgets.cpp @@ -31,7 +31,7 @@ ** ****************************************************************************/ -#include "simplewidgets.h" +#include "simplewidgets_p.h" #include #include diff --git a/src/widgets/accessible/simplewidgets.h b/src/widgets/accessible/simplewidgets_p.h similarity index 96% rename from src/widgets/accessible/simplewidgets.h rename to src/widgets/accessible/simplewidgets_p.h index 0dfd9f79c8..c2e904273f 100644 --- a/src/widgets/accessible/simplewidgets.h +++ b/src/widgets/accessible/simplewidgets_p.h @@ -34,6 +34,17 @@ #ifndef SIMPLEWIDGETS_H #define SIMPLEWIDGETS_H +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + #include #include From d05bb9ffb05be57b9ef8e961cc17111b1bdaffc8 Mon Sep 17 00:00:00 2001 From: Jonathan Meier Date: Sat, 5 Sep 2015 11:38:56 +0200 Subject: [PATCH 19/28] Fix qmake messing up headers of generated Visual Studio solution files While generating Visual Studio 2015 solution files for a project using the subdirs template qmake writes out both the header for version 2015 and version 2013. The problem is a case fall-through. Task-number: QTBUG-48110 Change-Id: Ib6ddc1ceb306be9b3098d7b7c66a8ffabbd86481 Reviewed-by: J-P Nurmi Reviewed-by: Andrew Knight Reviewed-by: Joerg Bornemann --- qmake/generators/win32/msvc_vcproj.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp index 8e08ab248e..9acfa6588b 100644 --- a/qmake/generators/win32/msvc_vcproj.cpp +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -655,6 +655,7 @@ void VcprojGenerator::writeSubDirs(QTextStream &t) switch (which_dotnet_version(project->first("MSVC_VER").toLatin1())) { case NET2015: t << _slnHeader140; + break; case NET2013: t << _slnHeader120; break; From 1b29ef627ce8e69dbe2daae73bc6a9f57b631b3d Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Wed, 29 Jul 2015 13:33:51 +1000 Subject: [PATCH 20/28] Fix hang in qnam when disconnecting Generate error for network requests when connection gets disconnected. Documentation states that QNAM requests will fail if network is not accessible, so we need to track session state. Task-number: QTBUG-47482 Change-Id: I2c2d348637f72b2a908b438a66aa543a878de1e5 Reviewed-by: Timo Jyrinki Reviewed-by: Richard J. Moore --- src/network/access/qnetworkreplyhttpimpl.cpp | 15 +++++++++++++++ src/network/access/qnetworkreplyhttpimpl_p.h | 2 ++ src/network/access/qnetworkreplyimpl.cpp | 15 +++++++++++++++ src/network/access/qnetworkreplyimpl_p.h | 2 ++ 4 files changed, 34 insertions(+) diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index abc6603be2..c1956ae99f 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -1676,6 +1676,11 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation() Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "backend start error."))); QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection); return; +#endif + } else { +#ifndef QT_NO_BEARERMANAGEMENT + QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)), + q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); #endif } @@ -1844,6 +1849,16 @@ void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected() } } +void QNetworkReplyHttpImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState) +{ + if (sessionState == QNetworkSession::Disconnected + && (state != Idle || state != Reconnecting)) { + error(QNetworkReplyImpl::NetworkSessionFailedError, + QCoreApplication::translate("QNetworkReply", "Network session error.")); + finished(); + } +} + void QNetworkReplyHttpImplPrivate::_q_networkSessionFailed() { // Abort waiting and working replies. diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h index b009092196..cfc05edbd8 100644 --- a/src/network/access/qnetworkreplyhttpimpl_p.h +++ b/src/network/access/qnetworkreplyhttpimpl_p.h @@ -95,6 +95,7 @@ public: #ifndef QT_NO_BEARERMANAGEMENT Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected()) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State)) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)) #endif Q_PRIVATE_SLOT(d_func(), void _q_finished()) @@ -170,6 +171,7 @@ public: #ifndef QT_NO_BEARERMANAGEMENT void _q_networkSessionConnected(); void _q_networkSessionFailed(); + void _q_networkSessionStateChanged(QNetworkSession::State); void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies); #endif void _q_finished(); diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index f235adaee8..18f322f45d 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -126,6 +126,11 @@ void QNetworkReplyImplPrivate::_q_startOperation() finished(); #endif return; + } else { +#ifndef QT_NO_BEARERMANAGEMENT + QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)), + q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); +#endif } #ifndef QT_NO_BEARERMANAGEMENT @@ -310,6 +315,16 @@ void QNetworkReplyImplPrivate::_q_networkSessionConnected() } } +void QNetworkReplyImplPrivate::_q_networkSessionStateChanged(QNetworkSession::State sessionState) +{ + if (sessionState == QNetworkSession::Disconnected + && (state != Idle || state != Reconnecting)) { + error(QNetworkReplyImpl::NetworkSessionFailedError, + QCoreApplication::translate("QNetworkReply", "Network session error.")); + finished(); + } +} + void QNetworkReplyImplPrivate::_q_networkSessionFailed() { // Abort waiting and working replies. diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h index d2e7d02408..5883c9d1c3 100644 --- a/src/network/access/qnetworkreplyimpl_p.h +++ b/src/network/access/qnetworkreplyimpl_p.h @@ -89,6 +89,7 @@ public: #ifndef QT_NO_BEARERMANAGEMENT Q_PRIVATE_SLOT(d_func(), void _q_networkSessionConnected()) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionFailed()) + Q_PRIVATE_SLOT(d_func(), void _q_networkSessionStateChanged(QNetworkSession::State)) Q_PRIVATE_SLOT(d_func(), void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)) #endif @@ -124,6 +125,7 @@ public: #ifndef QT_NO_BEARERMANAGEMENT void _q_networkSessionConnected(); void _q_networkSessionFailed(); + void _q_networkSessionStateChanged(QNetworkSession::State); void _q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies); #endif From c258422cf9962b994505030b7cc9bb00d22b7bf8 Mon Sep 17 00:00:00 2001 From: Tuomas Heimonen Date: Tue, 8 Sep 2015 14:52:21 +0300 Subject: [PATCH 21/28] tst_QProcess_and_GuiEventLoop: Added flag QT_NO_PROCESS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I895b9c12de8734c20ec87ac30a9a9cca8f4242d7 Reviewed-by: Pasi Petäjäjärvi Reviewed-by: Thiago Macieira --- .../tst_qprocess_and_guieventloop.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/auto/other/qprocess_and_guieventloop/tst_qprocess_and_guieventloop.cpp b/tests/auto/other/qprocess_and_guieventloop/tst_qprocess_and_guieventloop.cpp index 53459b13f6..b79b3aba28 100644 --- a/tests/auto/other/qprocess_and_guieventloop/tst_qprocess_and_guieventloop.cpp +++ b/tests/auto/other/qprocess_and_guieventloop/tst_qprocess_and_guieventloop.cpp @@ -45,9 +45,11 @@ private slots: void tst_QProcess_and_GuiEventLoop::waitForAndEventLoop() { -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) +#if defined(QT_NO_PROCESS) + QSKIP("QProcess not supported"); +#elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK) QSKIP("Not supported on Android"); -#endif +#else // based on testcase provided in QTBUG-39488 QByteArray msg = "Hello World"; @@ -78,6 +80,7 @@ void tst_QProcess_and_GuiEventLoop::waitForAndEventLoop() QCOMPARE(process.exitCode(), 0); QCOMPARE(spy.count(), 1); QCOMPARE(process.readAll().trimmed(), msg); +#endif } QTEST_MAIN(tst_QProcess_and_GuiEventLoop) From 4b2db07b42dc0be28beafbd20d005cc0b7b11fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pasi=20Pet=C3=A4j=C3=A4j=C3=A4rvi?= Date: Wed, 9 Sep 2015 15:45:18 +0300 Subject: [PATCH 22/28] Fix tst_QGuiApplication for embedded platforms using eglfs QPA Disable input and cursor for QGuiApplication instances used in autotest to initialize it properly. Change-Id: I78dc9b776269c082c20f244a51f858289129275d Reviewed-by: Laszlo Agocs --- .../qcoreapplication/tst_qcoreapplication.cpp | 5 ----- .../qguiapplication/tst_qguiapplication.cpp | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp index efecf31d66..60e358232e 100644 --- a/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/corelib/kernel/qcoreapplication/tst_qcoreapplication.cpp @@ -41,12 +41,7 @@ #include #include -#ifdef QT_GUI_LIB -#include -typedef QGuiApplication TestApplication; -#else typedef QCoreApplication TestApplication; -#endif class EventSpy : public QObject { diff --git a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp index 19365bffdd..d573d97495 100644 --- a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp +++ b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp @@ -60,6 +60,7 @@ class tst_QGuiApplication: public tst_QCoreApplication Q_OBJECT private slots: + void initTestCase(); void cleanup(); void displayName(); void firstWindowTitle(); @@ -84,6 +85,21 @@ private slots: void settableStyleHints(); // Needs to run last as it changes style hints. }; +void tst_QGuiApplication::initTestCase() +{ +#ifdef QT_QPA_DEFAULT_PLATFORM_NAME + if ((QString::compare(QStringLiteral(QT_QPA_DEFAULT_PLATFORM_NAME), + QStringLiteral("eglfs"), Qt::CaseInsensitive) == 0) || + (QString::compare(QString::fromLatin1(qgetenv("QT_QPA_PLATFORM")), + QStringLiteral("eglfs"), Qt::CaseInsensitive) == 0)) { + // Set env variables to disable input and cursor because eglfs is single fullscreen window + // and trying to initialize input and cursor will crash test. + qputenv("QT_QPA_EGLFS_DISABLE_INPUT", "1"); + qputenv("QT_QPA_EGLFS_HIDECURSOR", "1"); + } +#endif +} + void tst_QGuiApplication::cleanup() { QVERIFY(QGuiApplication::allWindows().isEmpty()); From d75505faccc9a86d9e76932ae022b660e01ad1a5 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Tue, 8 Sep 2015 13:38:41 +0200 Subject: [PATCH 23/28] iOS: silence compiler warning about unimplemented 'selectionRectsForRange' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a dummy method to silence the compiler. After testing, this method seems to never be called. Which is good, since the current IM API in Qt have little to offer to resolve what is being asked. Until a need arise, we just return an empty array. Change-Id: I573eb8205a7e635a46d487ae175fb46e3a602001 Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/ios/qiostextresponder.mm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/platforms/ios/qiostextresponder.mm b/src/plugins/platforms/ios/qiostextresponder.mm index 4e109f6921..c9120e848c 100644 --- a/src/plugins/platforms/ios/qiostextresponder.mm +++ b/src/plugins/platforms/ios/qiostextresponder.mm @@ -738,6 +738,15 @@ return toCGRect(startRect.united(endRect)); } +- (NSArray *)selectionRectsForRange:(UITextRange *)range +{ + Q_UNUSED(range); + // This method is supposed to return a rectangle for each line with selection. Since we don't + // expose an API in Qt/IM for getting this information, and since we never seems to be getting + // a call from UIKit for this, we return an empty array until a need arise. + return [[NSArray new] autorelease]; +} + - (CGRect)caretRectForPosition:(UITextPosition *)position { Q_UNUSED(position); From 378e26dd14df808a55471330400984841ef414d4 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Wed, 11 Feb 2015 20:02:07 +0200 Subject: [PATCH 24/28] QUdpSocket: avoid infinite read notifier blocking There was a small amount of time between the last readDatagram() call and disabling a read notifier in case the socket had a pending datagram. If a new datagram arrived in this period, this qualified as absence of a datagram reader. Do not change the read notifier state because it is disabled on canReadNotification() entry and always enabled by the datagram reader. Thanks to Peter Seiderer, who investigated the same: "Querying hasPendingDatagrams() for enabling/disabling setReadNotificationEnabled() is racy (a new datagram could arrive after readDatagam() is called and before hasPendingDatagrams() is checked). But for unbuffered sockets the ReadNotification is already disabled before the readReady signal is emitted and should be re-enabled when calling read() or readDatagram() from the user." However, this patch does not completely solve the problem under Windows, as the socket notifier may emit spurious notifications. Task-number: QTBUG-46552 Change-Id: If7295d53ae2c788c39e86303502f38135c4d6180 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/network/socket/qabstractsocket.cpp | 12 ++--- .../socket/qudpsocket/tst_qudpsocket.cpp | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index c42b4f520c..d93150799c 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -739,15 +739,9 @@ bool QAbstractSocketPrivate::canReadNotification() return true; } - if (socketEngine) { - // turn the socket engine off if we've either: - // - got pending datagrams - // - reached the buffer size limit - if (isBuffered) - socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable()); - else if (socketType != QAbstractSocket::TcpSocket) - socketEngine->setReadNotificationEnabled(!socketEngine->hasPendingDatagrams()); - } + // turn the socket engine off if we've reached the buffer size limit + if (socketEngine && isBuffered) + socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable()); // reset the read socket notifier state if we reentered inside the // readyRead() connected slot. diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp index b6129bec08..a4695955af 100644 --- a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp @@ -116,10 +116,12 @@ private slots: void linkLocalIPv4(); void readyRead(); void readyReadForEmptyDatagram(); + void asyncReadDatagram(); protected slots: void empty_readyReadSlot(); void empty_connectedSlot(); + void async_readDatagramSlot(); private: #ifndef QT_NO_BEARERMANAGEMENT @@ -127,6 +129,8 @@ private: QNetworkConfiguration networkConfiguration; QSharedPointer networkSession; #endif + QUdpSocket *m_asyncSender; + QUdpSocket *m_asyncReceiver; }; static QHostAddress makeNonAny(const QHostAddress &address, QHostAddress::SpecialAddress preferForAny = QHostAddress::LocalHost) @@ -1671,5 +1675,55 @@ void tst_QUdpSocket::readyReadForEmptyDatagram() QCOMPARE(receiver.readDatagram(buf, sizeof buf), qint64(0)); } +void tst_QUdpSocket::async_readDatagramSlot() +{ + char buf[1]; + QVERIFY(m_asyncReceiver->hasPendingDatagrams()); + QCOMPARE(m_asyncReceiver->pendingDatagramSize(), qint64(1)); + QCOMPARE(m_asyncReceiver->bytesAvailable(), qint64(1)); + QCOMPARE(m_asyncReceiver->readDatagram(buf, sizeof(buf)), qint64(1)); + + if (buf[0] == '2') { + QTestEventLoop::instance().exitLoop(); + return; + } + + m_asyncSender->writeDatagram("2", makeNonAny(m_asyncReceiver->localAddress()), m_asyncReceiver->localPort()); + // wait a little to ensure that the datagram we've just sent + // will be delivered on receiver side. + QTest::qSleep(100); +} + +void tst_QUdpSocket::asyncReadDatagram() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + m_asyncSender = new QUdpSocket; + m_asyncReceiver = new QUdpSocket; +#ifdef FORCE_SESSION + m_asyncSender->setProperty("_q_networksession", QVariant::fromValue(networkSession)); + m_asyncReceiver->setProperty("_q_networksession", QVariant::fromValue(networkSession)); +#endif + + QVERIFY(m_asyncReceiver->bind(QHostAddress(QHostAddress::AnyIPv4), 0)); + quint16 port = m_asyncReceiver->localPort(); + QVERIFY(port != 0); + + QSignalSpy spy(m_asyncReceiver, SIGNAL(readyRead())); + connect(m_asyncReceiver, SIGNAL(readyRead()), SLOT(async_readDatagramSlot())); + + m_asyncSender->writeDatagram("1", makeNonAny(m_asyncReceiver->localAddress()), port); + + QTestEventLoop::instance().enterLoop(1); + + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(spy.count(), 2); + + delete m_asyncSender; + delete m_asyncReceiver; +} + QTEST_MAIN(tst_QUdpSocket) #include "tst_qudpsocket.moc" From a6ec869211d67fed94e3513dc453a96717155121 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Tue, 11 Aug 2015 13:23:32 +0300 Subject: [PATCH 25/28] Fix the spurious socket notifications under Windows To handle network events, QEventDispatcherWin32 uses I/O model based on notifications through the window message queue. Having successfully posted notification of a particular event to an application window, no further messages for that network event will be posted to the application window until the application makes the function call that implicitly re-enables notification of that network event. With these semantics, an application need not read all available data in response to an FD_READ message: a single recv in response to each FD_READ message is appropriate. If an application issues multiple recv calls in response to a single FD_READ, it can receive multiple FD_READ messages (including spurious). To solve this issue, this patch always disables the notifier after getting a notification, and re-enables it only when the message queue is empty. Task-number: QTBUG-46552 Change-Id: I05df67032911cd1f5927fa7912f7864bfbf8711e Reviewed-by: Joerg Bornemann --- src/corelib/kernel/qeventdispatcher_win.cpp | 112 +++++++++++------- src/corelib/kernel/qeventdispatcher_win_p.h | 11 +- src/corelib/kernel/qsocketnotifier.cpp | 36 ------ .../qsocketnotifier/tst_qsocketnotifier.cpp | 73 ++++++++++++ 4 files changed, 153 insertions(+), 79 deletions(-) diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 6da70cf0bd..695eb3d5d0 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -391,6 +391,8 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QSockNot *sn = dict ? dict->value(wp) : 0; if (sn) { + d->doWsaAsyncSelect(sn->fd, 0); + d->active_fd[sn->fd].selected = false; if (type < 3) { QEvent event(QEvent::SockAct); QCoreApplication::sendEvent(sn->obj, &event); @@ -633,19 +635,12 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId) } } -void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket) +void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket, long event) { Q_ASSERT(internalHwnd); - int sn_event = 0; - if (sn_read.contains(socket)) - sn_event |= FD_READ | FD_CLOSE | FD_ACCEPT; - if (sn_write.contains(socket)) - sn_event |= FD_WRITE | FD_CONNECT; - if (sn_except.contains(socket)) - sn_event |= FD_OOB; - // BoundsChecker may emit a warning for WSAAsyncSelect when sn_event == 0 + // BoundsChecker may emit a warning for WSAAsyncSelect when event == 0 // This is a BoundsChecker bug and not a Qt bug - WSAAsyncSelect(socket, internalHwnd, sn_event ? int(WM_QT_SOCKETNOTIFIER) : 0, sn_event); + WSAAsyncSelect(socket, internalHwnd, event ? int(WM_QT_SOCKETNOTIFIER) : 0, event); } void QEventDispatcherWin32::createInternalHwnd() @@ -658,13 +653,6 @@ void QEventDispatcherWin32::createInternalHwnd() installMessageHook(); - // register all socket notifiers - QList sockets = (d->sn_read.keys().toSet() - + d->sn_write.keys().toSet() - + d->sn_except.keys().toSet()).toList(); - for (int i = 0; i < sockets.count(); ++i) - d->doWsaAsyncSelect(sockets.at(i)); - // start all normal timers for (int i = 0; i < d->timerVec.count(); ++i) d->registerTimer(d->timerVec.at(i)); @@ -749,28 +737,40 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) msg = d->queuedSocketEvents.takeFirst(); } else { haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); - if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) - && ((msg.message >= WM_KEYFIRST - && msg.message <= WM_KEYLAST) - || (msg.message >= WM_MOUSEFIRST - && msg.message <= WM_MOUSELAST) - || msg.message == WM_MOUSEWHEEL - || msg.message == WM_MOUSEHWHEEL - || msg.message == WM_TOUCH + if (haveMessage) { + if ((flags & QEventLoop::ExcludeUserInputEvents) + && ((msg.message >= WM_KEYFIRST + && msg.message <= WM_KEYLAST) + || (msg.message >= WM_MOUSEFIRST + && msg.message <= WM_MOUSELAST) + || msg.message == WM_MOUSEWHEEL + || msg.message == WM_MOUSEHWHEEL + || msg.message == WM_TOUCH #ifndef QT_NO_GESTURES - || msg.message == WM_GESTURE - || msg.message == WM_GESTURENOTIFY + || msg.message == WM_GESTURE + || msg.message == WM_GESTURENOTIFY #endif - || msg.message == WM_CLOSE)) { - // queue user input events for later processing - haveMessage = false; - d->queuedUserInputEvents.append(msg); - } - if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) - && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { - // queue socket events for later processing - haveMessage = false; - d->queuedSocketEvents.append(msg); + || msg.message == WM_CLOSE)) { + // queue user input events for later processing + d->queuedUserInputEvents.append(msg); + continue; + } + if ((flags & QEventLoop::ExcludeSocketNotifiers) + && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { + // queue socket events for later processing + d->queuedSocketEvents.append(msg); + continue; + } + } else if (!(flags & QEventLoop::ExcludeSocketNotifiers)) { + // register all socket notifiers + for (QSFDict::iterator it = d->active_fd.begin(), end = d->active_fd.end(); + it != end; ++it) { + QSockFd &sd = it.value(); + if (!sd.selected) { + d->doWsaAsyncSelect(it.key(), sd.event); + sd.selected = true; + } + } } } if (!haveMessage) { @@ -896,8 +896,25 @@ void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) sn->fd = sockfd; dict->insert(sn->fd, sn); - if (d->internalHwnd) - d->doWsaAsyncSelect(sockfd); + long event = 0; + if (d->sn_read.contains(sockfd)) + event |= FD_READ | FD_CLOSE | FD_ACCEPT; + if (d->sn_write.contains(sockfd)) + event |= FD_WRITE | FD_CONNECT; + if (d->sn_except.contains(sockfd)) + event |= FD_OOB; + + QSFDict::iterator it = d->active_fd.find(sockfd); + if (it != d->active_fd.end()) { + QSockFd &sd = it.value(); + if (sd.selected) { + d->doWsaAsyncSelect(sockfd, 0); + sd.selected = false; + } + sd.event |= event; + } else { + d->active_fd.insert(sockfd, QSockFd(event)); + } } void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) @@ -916,6 +933,19 @@ void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) #endif Q_D(QEventDispatcherWin32); + QSFDict::iterator it = d->active_fd.find(sockfd); + if (it != d->active_fd.end()) { + QSockFd &sd = it.value(); + if (sd.selected) + d->doWsaAsyncSelect(sockfd, 0); + const long event[3] = { FD_READ | FD_CLOSE | FD_ACCEPT, FD_WRITE | FD_CONNECT, FD_OOB }; + sd.event ^= event[type]; + if (sd.event == 0) + d->active_fd.erase(it); + else + sd.selected = false; + } + QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except }; QSNDict *dict = sn_vec[type]; QSockNot *sn = dict->value(sockfd); @@ -924,9 +954,6 @@ void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) dict->remove(sockfd); delete sn; - - if (d->internalHwnd) - d->doWsaAsyncSelect(sockfd); } void QEventDispatcherWin32::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) @@ -1165,6 +1192,7 @@ void QEventDispatcherWin32::closingDown() unregisterSocketNotifier((*(d->sn_write.begin()))->obj); while (!d->sn_except.isEmpty()) unregisterSocketNotifier((*(d->sn_except.begin()))->obj); + Q_ASSERT(d->active_fd.isEmpty()); // clean up any timers for (int i = 0; i < d->timerVec.count(); ++i) diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index e59e29f1ff..8578110ee4 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -115,6 +115,14 @@ struct QSockNot { }; typedef QHash QSNDict; +struct QSockFd { + long event; + bool selected; + + explicit inline QSockFd(long ev = 0) : event(ev), selected(false) { } +}; +typedef QHash QSFDict; + struct WinTimerInfo { // internal timer info QObject *dispatcher; int timerId; @@ -169,7 +177,8 @@ public: QSNDict sn_read; QSNDict sn_write; QSNDict sn_except; - void doWsaAsyncSelect(int socket); + QSFDict active_fd; + void doWsaAsyncSelect(int socket, long event); QList winEventNotifierList; void activateEventNotifier(QWinEventNotifier * wen); diff --git a/src/corelib/kernel/qsocketnotifier.cpp b/src/corelib/kernel/qsocketnotifier.cpp index d789af2fd9..3a5eff0c19 100644 --- a/src/corelib/kernel/qsocketnotifier.cpp +++ b/src/corelib/kernel/qsocketnotifier.cpp @@ -98,42 +98,6 @@ public: QTcpSocket and QUdpSocket provide notification through signals, so there is normally no need to use a QSocketNotifier on them. - \section1 Notes for Windows Users - - The socket passed to QSocketNotifier will become non-blocking, even if - it was created as a blocking socket. - The activated() signal is sometimes triggered by high general activity - on the host, even if there is nothing to read. A subsequent read from - the socket can then fail, the error indicating that there is no data - available (e.g., \c{WSAEWOULDBLOCK}). This is an operating system - limitation, and not a bug in QSocketNotifier. - - To ensure that the socket notifier handles read notifications correctly, - follow these steps when you receive a notification: - - \list 1 - \li Disable the notifier. - \li Read data from the socket. - \li Re-enable the notifier if you are interested in more data (such as after - having written a new command to a remote server). - \endlist - - To ensure that the socket notifier handles write notifications correctly, - follow these steps when you receive a notification: - - \list 1 - \li Disable the notifier. - \li Write as much data as you can (before \c EWOULDBLOCK is returned). - \li Re-enable notifier if you have more data to write. - \endlist - - \b{Further information:} - On Windows, Qt always disables the notifier after getting a notification, - and only re-enables it if more data is expected. For example, if data is - read from the socket and it can be used to read more, or if reading or - writing is not possible because the socket would block, in which case - it is necessary to wait before attempting to read or write again. - \sa QFile, QProcess, QTcpSocket, QUdpSocket */ diff --git a/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp index 930a17c3a9..f49da1f5a8 100644 --- a/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp +++ b/tests/auto/corelib/kernel/qsocketnotifier/tst_qsocketnotifier.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #ifndef Q_OS_WINRT #include #else @@ -66,8 +67,29 @@ private slots: #ifdef Q_OS_UNIX void posixSockets(); #endif + void asyncMultipleDatagram(); + +protected slots: + void async_readDatagramSlot(); + void async_writeDatagramSlot(); + +private: + QUdpSocket *m_asyncSender; + QUdpSocket *m_asyncReceiver; }; +static QHostAddress makeNonAny(const QHostAddress &address, + QHostAddress::SpecialAddress preferForAny = QHostAddress::LocalHost) +{ + if (address == QHostAddress::Any) + return preferForAny; + if (address == QHostAddress::AnyIPv4) + return QHostAddress::LocalHost; + if (address == QHostAddress::AnyIPv6) + return QHostAddress::LocalHostIPv6; + return address; +} + class UnexpectedDisconnectTester : public QObject { Q_OBJECT @@ -299,5 +321,56 @@ void tst_QSocketNotifier::posixSockets() } #endif +void tst_QSocketNotifier::async_readDatagramSlot() +{ + char buf[1]; + QVERIFY(m_asyncReceiver->hasPendingDatagrams()); + do { + QCOMPARE(m_asyncReceiver->pendingDatagramSize(), qint64(1)); + QCOMPARE(m_asyncReceiver->readDatagram(buf, sizeof(buf)), qint64(1)); + if (buf[0] == '1') { + // wait for the second datagram message. + QTest::qSleep(100); + } + } while (m_asyncReceiver->hasPendingDatagrams()); + + if (buf[0] == '3') + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocketNotifier::async_writeDatagramSlot() +{ + m_asyncSender->writeDatagram("3", makeNonAny(m_asyncReceiver->localAddress()), + m_asyncReceiver->localPort()); +} + +void tst_QSocketNotifier::asyncMultipleDatagram() +{ + m_asyncSender = new QUdpSocket; + m_asyncReceiver = new QUdpSocket; + + QVERIFY(m_asyncReceiver->bind(QHostAddress(QHostAddress::AnyIPv4), 0)); + quint16 port = m_asyncReceiver->localPort(); + QVERIFY(port != 0); + + QSignalSpy spy(m_asyncReceiver, &QIODevice::readyRead); + connect(m_asyncReceiver, &QIODevice::readyRead, this, + &tst_QSocketNotifier::async_readDatagramSlot); + m_asyncSender->writeDatagram("1", makeNonAny(m_asyncReceiver->localAddress()), port); + m_asyncSender->writeDatagram("2", makeNonAny(m_asyncReceiver->localAddress()), port); + // wait a little to ensure that the datagrams we've just sent + // will be delivered on receiver side. + QTest::qSleep(100); + + QTimer::singleShot(500, this, &tst_QSocketNotifier::async_writeDatagramSlot); + + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(spy.count(), 2); + + delete m_asyncSender; + delete m_asyncReceiver; +} + QTEST_MAIN(tst_QSocketNotifier) #include From c86ca601edfde6e7b6a0769903d86bd48e26d70d Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Fri, 21 Aug 2015 13:00:55 +0300 Subject: [PATCH 26/28] Windows socket engine: do not discard datagram on critical failure On some network conditions, WSARecvFrom() may return WSAECONNRESET or WSAENETRESET error code to indicate that the connection has been broken and should be reset. According to MSDN documentation, WSAECONNRESET mean that the virtual circuit was reset by the remote side executing a hard or abortive close. Also, it would indicate that a previous send operation resulted in an ICMP "Port Unreachable" message. For a datagram socket, WSAENETRESET indicates that the time to live has expired. Previously, hasPendingDatagram() discarded datagrams with these errors and reported no data available. This behavior is incorrect and can lead to infinite "freezing" of the socket. This patch allows to handle these notifications as a result of the readDatagram() call. Task-number: QTBUG-46552 Change-Id: I7d84babe22d36736b928b4dd4841e30be53d16bd Reviewed-by: Kai Koehne Reviewed-by: Joerg Bornemann --- .../socket/qnativesocketengine_win.cpp | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 72f85c831f..708be2dea7 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -1090,8 +1090,11 @@ qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const buf.buf = &c; buf.len = sizeof(c); DWORD flags = MSG_PEEK; - if (::WSARecvFrom(socketDescriptor, &buf, 1, 0, &flags, 0,0,0,0) == SOCKET_ERROR) - return 0; + if (::WSARecvFrom(socketDescriptor, &buf, 1, 0, &flags, 0,0,0,0) == SOCKET_ERROR) { + int err = WSAGetLastError(); + if (err != WSAECONNRESET && err != WSAENETRESET) + return 0; + } } return nbytes; } @@ -1119,14 +1122,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const int err = WSAGetLastError(); if (ret == SOCKET_ERROR && err != WSAEMSGSIZE) { WS_ERROR_DEBUG(err); - if (err == WSAECONNRESET || err == WSAENETRESET) { - // Discard error message to prevent QAbstractSocket from - // getting this message repeatedly after reenabling the - // notifiers. - flags = 0; - ::WSARecvFrom(socketDescriptor, &buf, 1, &available, &flags, - &storage.a, &storageSize, 0, 0); - } + result = (err == WSAECONNRESET || err == WSAENETRESET); } else { // If there's no error, or if our buffer was too small, there must be // a pending datagram. @@ -1179,12 +1175,21 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const if (recvResult != SOCKET_ERROR) { ret = qint64(bytesRead); break; - } else if (recvResult == SOCKET_ERROR && err == WSAEMSGSIZE) { - bufferCount += 5; - delete[] buf; - } else if (recvResult == SOCKET_ERROR) { - WS_ERROR_DEBUG(err); - ret = -1; + } else { + switch (err) { + case WSAEMSGSIZE: + bufferCount += 5; + delete[] buf; + continue; + case WSAECONNRESET: + case WSAENETRESET: + ret = 0; + break; + default: + WS_ERROR_DEBUG(err); + ret = -1; + break; + } break; } } From 3f531512e859f78e22a775193f0832f4709c84ec Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Mon, 29 Jun 2015 14:26:07 +0200 Subject: [PATCH 27/28] Force GLES 2.0 on Android 4.2 devices reporting 3.0 Android does not support GLES 3.0 before 4.3 (API level 18). However some 4.2.2 devices are reported to return 3.0 in the version string and therefore choose the GLES 3+ code paths in Qt. This blows up sooner or later because the 3.0 specific functions are apparently not present at all. Task-number: QTBUG-46831 Change-Id: Ic3eeb7c55829cf36c6d142c01ff8a1e18e9ecc1a Reviewed-by: Kati Kankaanpaa Reviewed-by: Christian Stromme --- .../eglconvenience/qeglplatformcontext.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp index 5bee29afef..bffb0eb4e8 100644 --- a/src/platformsupport/eglconvenience/qeglplatformcontext.cpp +++ b/src/platformsupport/eglconvenience/qeglplatformcontext.cpp @@ -39,6 +39,10 @@ #include #include +#ifdef Q_OS_ANDROID +#include +#endif + QT_BEGIN_NAMESPACE /*! @@ -294,6 +298,14 @@ void QEGLPlatformContext::updateFormatFromGL() QByteArray version = QByteArray(reinterpret_cast(s)); int major, minor; if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) { +#ifdef Q_OS_ANDROID + // Some Android 4.2.2 devices report OpenGL ES 3.0 without the functions being available. + static int apiLevel = QtAndroidPrivate::androidSdkVersion(); + if (apiLevel <= 17 && major >= 3) { + major = 2; + minor = 0; + } +#endif m_format.setMajorVersion(major); m_format.setMinorVersion(minor); } From c12b5e07274acde364be98cd0de620d3a44c97b7 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Wed, 2 Sep 2015 17:35:53 +0200 Subject: [PATCH 28/28] QCocoaMenu: Manually reposition popups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the popup will show too close to the screen bottom, we need to help Cocoa a bit. The horizontal positioning hasn't shown any problems. Change-Id: I5f298529fbf4a902e39f686f368046a8d1c11760 Task-number: QTBUG-45063 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/qcocoamenu.mm | 41 +++++++++++++++++------ 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 6668080725..a6157bdc3a 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -464,6 +464,13 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, NSView *view = cocoaWindow ? cocoaWindow->contentView() : nil; NSMenuItem *nsItem = item ? ((QCocoaMenuItem *)item)->nsItem() : nil; + QScreen *screen = 0; + if (parentWindow) + screen = parentWindow->screen(); + if (!screen && !QGuiApplication::screens().isEmpty()) + screen = QGuiApplication::screens().at(0); + Q_ASSERT(screen); + // Ideally, we would call -popUpMenuPositioningItem:atLocation:inView:. // However, this showed not to work with modal windows where the menu items // would appear disabled. So, we resort to a more artisanal solution. Note @@ -480,6 +487,21 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, [popupCell setTransparent:YES]; [popupCell setMenu:m_nativeMenu]; [popupCell selectItem:nsItem]; + + int availableHeight = screen->availableSize().height(); + const QPoint &globalPos = parentWindow->mapToGlobal(pos); + int menuHeight = m_nativeMenu.size.height; + if (globalPos.y() + menuHeight > availableHeight) { + // Maybe we need to fix the vertical popup position but we don't know the + // exact popup height at the moment (and Cocoa is just guessing) nor its + // position. So, instead of translating by the popup's full height, we need + // to estimate where the menu will show up and translate by the remaining height. + float idx = ([m_nativeMenu indexOfItem:nsItem] + 1.0f) / m_nativeMenu.numberOfItems; + float heightBelowPos = (1.0 - idx) * menuHeight; + if (globalPos.y() + heightBelowPos > availableHeight) + pos.setY(pos.y() - globalPos.y() + availableHeight - heightBelowPos); + } + NSRect cellFrame = NSMakeRect(pos.x(), pos.y(), m_nativeMenu.minimumWidth, 10); [popupCell performClickWithFrame:cellFrame inView:view]; } else { @@ -488,22 +510,21 @@ void QCocoaMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, if (view) { // convert coordinates from view to the view's window nsPos = [view convertPoint:nsPos toView:nil]; - } else if (!QGuiApplication::screens().isEmpty()) { - QScreen *screen = QGuiApplication::screens().at(0); + } else { nsPos.y = screen->availableVirtualSize().height() - nsPos.y; } if (view) { // Finally, we need to synthesize an event. NSEvent *menuEvent = [NSEvent mouseEventWithType:NSRightMouseDown - location:nsPos - modifierFlags:0 - timestamp:0 - windowNumber:view ? view.window.windowNumber : 0 - context:nil - eventNumber:0 - clickCount:1 - pressure:1.0]; + location:nsPos + modifierFlags:0 + timestamp:0 + windowNumber:view ? view.window.windowNumber : 0 + context:nil + eventNumber:0 + clickCount:1 + pressure:1.0]; [NSMenu popUpContextMenu:m_nativeMenu withEvent:menuEvent forView:view]; } else { [m_nativeMenu popUpMenuPositioningItem:nsItem atLocation:nsPos inView:0];