diff --git a/config.tests/.qmake.conf b/config.tests/.qmake.conf new file mode 100644 index 0000000000..71e6817656 --- /dev/null +++ b/config.tests/.qmake.conf @@ -0,0 +1,6 @@ +mingw { + TMPPATH = $$(INCLUDE) + QMAKE_INCDIR_POST += $$split(TMPPATH, $$QMAKE_DIRLIST_SEP) + TMPPATH = $$(LIB) + QMAKE_LIBDIR_POST += $$split(TMPPATH, $$QMAKE_DIRLIST_SEP) +} diff --git a/config.tests/unix/pps/pps.cpp b/config.tests/unix/pps/pps.cpp new file mode 100644 index 0000000000..3f00509973 --- /dev/null +++ b/config.tests/unix/pps/pps.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 BlackBerry Limited. All rights reserved. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main(int, char **) +{ + pps_decoder_t decoder; + + pps_decoder_initialize(&decoder, NULL); + return 0; +} diff --git a/config.tests/unix/pps/pps.pro b/config.tests/unix/pps/pps.pro new file mode 100644 index 0000000000..21bdeedbfb --- /dev/null +++ b/config.tests/unix/pps/pps.pro @@ -0,0 +1,3 @@ +SOURCES = pps.cpp +CONFIG -= qt +LIBS += -lpps diff --git a/configure b/configure index f7edfdf093..120150ca87 100755 --- a/configure +++ b/configure @@ -629,6 +629,7 @@ CFG_JAVASCRIPTCORE_JIT=auto CFG_PKGCONFIG=auto CFG_STACK_PROTECTOR_STRONG=auto CFG_SLOG2=auto +CFG_PPS=auto CFG_SYSTEM_PROXIES=no # Target architecture @@ -1769,6 +1770,13 @@ while [ "$#" -gt 0 ]; do UNKNOWN_OPT=yes fi ;; + pps) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_PPS="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; gtkstyle) if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then CFG_QGTKSTYLE="$VAL" @@ -1918,7 +1926,11 @@ while [ "$#" -gt 0 ]; do break fi done - [ "$found" = yes ] || ERROR=yes + if [ "$found" != "yes" ]; then + echo "$CURRENT_OPT: unknown argument" + ERROR=yes + continue + fi if [ "$VAR" = "sql" ]; then # set the CFG_SQL_driver @@ -1942,9 +1954,6 @@ while [ "$#" -gt 0 ]; do QMakeVar del "${IN_VAR}s" "$VAL" QMakeVar del "${PLUG_VAR}s" "$VAL" fi - if [ "$ERROR" = "yes" ]; then - echo "$CURRENT_OPT: unknown argument" - fi ;; v|verbose) if [ "$VAL" = "yes" ]; then @@ -2455,6 +2464,9 @@ QNX/Blackberry options: -no-slog2 .......... Do not compile with slog2 support. -slog2 ............. Compile with slog2 support. + -no-pps ............ Do not compile with pps support. + -pps ............... Compile with pps support. + MacOS/iOS options: -Fstring ........... Add an explicit framework path. @@ -3318,7 +3330,7 @@ if [ "$XPLATFORM_IOS" = "yes" ]; then CFG_NOBUILD_PARTS="$CFG_NOBUILD_PARTS examples tests" CFG_SHARED="no" # iOS builds should be static to be able to submit to the App Store CFG_CXX11="no" # C++11 support disabled for now - CFG_SKIP_MODULES="$CFG_SKIP_MODULES qtconnectivity qtdoc qtgraphicaleffects qtlocation qtmacextras qtmultimedia qtquickcontrols qtserialport qttools qtwebkit qtwebkit-examples" + CFG_SKIP_MODULES="$CFG_SKIP_MODULES qtconnectivity qtdoc qtlocation qtmacextras qtserialport qttools qtwebkit qtwebkit-examples" # If the user passes -sdk on the command line we build a SDK-specific Qt build. # Otherwise we build a joined simulator and device build, which is the default. @@ -4344,6 +4356,15 @@ if [ "$XPLATFORM_QNX" = "yes" ]; then CFG_SLOG2=no fi fi + + if [ "$CFG_PPS" != "no" ]; then + if compileTest unix/pps "pps"; then + CFG_PPS=yes + QMAKE_CONFIG="$QMAKE_CONFIG qqnx_pps" + else + CFG_PPS=no + fi + fi fi if [ "$CFG_ZLIB" = "auto" ]; then @@ -6684,8 +6705,10 @@ if [ "$CFG_XCB" != "no" ]; then report_support " XVideo ............." "$CFG_XVIDEO" fi report_support " Session management ....." "$CFG_SM" -[ "$XPLATFORM_QNX" = "yes" ] && \ +if [ "$XPLATFORM_QNX" = "yes" ]; then report_support " SLOG2 .................." "$CFG_SLOG2" + report_support " PPS ...................." "$CFG_PPS" +fi report_support " SQL drivers:" report_support " DB2 .................." "$CFG_SQL_db2" plugin "plugin" yes "built into QtSql" report_support " InterBase ............" "$CFG_SQL_ibase" plugin "plugin" yes "built into QtSql" diff --git a/dist/changes-5.2.0 b/dist/changes-5.2.0 index 898441a675..f513d64626 100644 --- a/dist/changes-5.2.0 +++ b/dist/changes-5.2.0 @@ -16,56 +16,69 @@ Each of these identifiers can be entered in the bug tracker to obtain more information about a particular change. **************************************************************************** -* Architecture Specific Changes * +* Important Behavior Changes * **************************************************************************** -Qt is now compiled with qreal typedef'ed to double on all platforms. qreal -was a float on ARM chipsets before. This guarantees more consistent behavior -between all platforms Qt supports, but is binary incompatible to Qt 5.1 -on ARM. The old behavior can be restored by passing -qreal float to -configure. + - Qt is now compiled with qreal typedef'ed to double on all + platforms. qreal was a float on ARM chipsets before. This guarantees more + consistent behavior between all platforms Qt supports, but is binary + incompatible to Qt 5.1 on ARM. The old behavior can be restored by + passing -qreal float to configure. + - The supported date range in QDateTime has been reduced to about +/- 292 + million years, the range supported by the number of msecs since the Unix + epoch of 1 Jan 1970 as stored in a qint64, and as able to be used in the + setMSecsSinceEpoch() and toMSecsSinceEpoch() methods. + + - QUrl and QUrlQuery: + * [QTBUG-31660] QUrl no longer considers all delimiter characters + equivalent to their percent-encoded forms. Now, both classes always + keep all delimiters exactly as they were in the original URL text. + * [QTBUG-31660] QUrl no longer decodes %7B and %7D to "{" and "}" in the + output of toString() + * QUrl no longer supports QUrl::FullyDecoded mode in authority() and + userInfo(), nor QUrl::DecodedMode in setAuthority() and setUserInfo(). + * [QTBUG-31945] QUrl no longer decodes %23 found in the fragment to "#" + in the output of toString(QUrl::FullyEncoded) or toEncoded() + * QUrl now defaults to decoded mode in the getters and setters for + userName, password, host, topLevelDomain, path and fileName. This + means a '%' in one of those fields is now returned (or set) as '%' + rather than "%25". In the unlikely case where the former behavior was + expected, pass PrettyDecoded to the getter and TolerantMode to the + setter. + * QUrl now normalizes the path given in setPath, removing ./ and ../ and + duplicate slashes. **************************************************************************** * Library * **************************************************************************** - -QtWidgets ---------- - -- QAbstractScrollArea now has a SizeAdjustPolicy. If it is set to AdjustToContents - it will make use of the protected viewportSizeHint() virtual function. This - function returns a suggested size based on contents. Note that although the - viewportSizeHint() virtual function was reserved in 5.0, user code was not - supposed to be overriding it (as the function was private and undocumented). - Code that was overriding viewportSizeHint() needs to be recompiled against 5.2 - for this feature to work correctly. - -- QTreeView now has setTreePosition to allow the treestructure to show data from other - columns than logicalindex zero. - -- [QTBUG-4206] QTableView resizeToContents will now adjust to actual contents - and not just visible area. QHeaderView::setAutoResizePrecision() has been - introduced to control how precise the autoResize should be. - -- QFileDialog::setDefaultSuffix() removes leading dot characters. - -- [QTBUG-34132] QFileDialog does not instantiate widgets if a native dialog - will be used instead. Therefore some accessors which previously returned - unused objects will now return null. As before, you can set the - DontUseNativeDialog option to ensure that widgets will be created and used instead. - -- QSizePolicy got a retainSizeWhenHidden attribute. - -- [QTBUG-31602] QSpinBox size calculation will now be fixed when stylesheets are - used. - QtCore ------ -- [QTBUG-30250] QTime, QDateTime: - When calling QTime::toString(Qt::TextDate) and QTime::toString(Qt::ISODate), - milliseconds are now included in the returned string. This also applies to - QDateTime::toString(Qt::TextDate) and QDateTime::toString(ISODate). + + - Added QCollator, a class to collate Unicode strings. + - Added QCommandLineParser, a class to parse command lines. + - Added QFileSelector, a class to select platform-specific file assets. + - Added QLoggingCategory and related functions for logging + - [QTBUG-23946] Fixed a bug that prevented Qt from being built in a + namespace on Mac OS X. + - Updated the locale database to CLDR 23.1 + - Added support for ARMv8 64-bit mode. + + - Metatype system (QMetaType & QVariant): + * Qt now handles metatypes automatically for std::vector, std::list, + std::pair and std::map + * The metatype system now supports registering explicit conversion + functions via QMetaType::registerConverter + * The metatype system now supports iteration over a container type via + QSequentialIterable and QAssociativeIterable + * Registering the same type name twice will now print only a warning (as + opposed to aborting the execution of the program) + + - Qt Containers: + * In debug mode, the Qt containers will now verify whether the iterators + passed to most mutating functions belong to the iterator in question. + * Fixed a number of bugs that would modify shared containers when calling + erase(), under corner-case conditions. - QtAlgorithms With STL no longer being optional for building and using Qt, a number of parts @@ -96,85 +109,458 @@ QtCore - qLess (std::less) - qGreater (std::greater) + - QByteArray: + * Added QByteArray::Base64Url and QByteArray::OmitTrailingEquals flags + for QByteArray::toBase64 and fromBase64. + * [QTBUG-34694] Fixed a bug that would cause QByteArray to overflow some + size calculations. + + - QCoreApplication: + * [QTBUG-15379][QTBUG-30628] Fixed a bug that caused Qt to mis-parse a + command-line argument like -DKEY=\"VALUE\" on Windows. + + - QDateTime: + * [QTBUG-26161][QTBUG-29666] Fully implement support for Qt::TimeSpec of + Qt::OffsetFromUTC, added new methods for offsetFromUTC(), + toTimeSpec(), and toOffsetFromUTC(). + * Added convenience methods for fromMSecsSinceEpoch() and fromTime_t() + to take time spec to be used in returned datetime. + * Add method timeZoneAbbreviation() to return effective time zone + abbreviation. + * The debug datastream is now an ISO-like format instead of Qt::TextDate + * The Standard Time to Daylight Time transition for Qt::LocalTime is now + handled correctly. Any date set in the "missing" hour is now + considered invalid. All date math results that fall into the missing + hour will be automatically adjusted to a valid time in the following + hour. + * Added new method isDaylightTime() to return if the datetime is in + Daylight Time or not. + * Added support for a new Qt::TimeZone spec to be used with QTimeZone to + define times in a specific time zone. + * Added Qt::RFC2822Date format that can be passed to QDateTime and + QLocale formatters and parsers. + * Fixed a bug that caused QDate::toString() to return empty for dates + with years beyond 9999. + + - QFileSystemWatcher: + * [QTBUG-33211] Fixed a bug that caused QFileSystemWatcher to emit change + notifications with the wrong path on Linux. + + - QJson: + * [QTBUG-33229] The Writer and the Parser now fully accept non-character + unicode points. + * The Writer will no longer write inf, -inf and nan for infinites and NaN + values, which resulted in parsing back as strings. Instead, it will now + output null. + * The Writer now emits numeric values with full numeric precision. This + also allows QJson to support integer values with no loss of precision + up to 2^53. + + - QJsonValue: + * Added QJsonValue::toInt(). QJsonValue can store 32-bit signed integers + with no loss of precision. + + - QObject: + * New-style signal connections to functors, lambdas, and static functions + now works with a receiver object. The connection will be removed when + the either the sender or receiver objects are destroyed. + + - QPluginLoader: + * QPluginLoader will no longer load the plugins on Mac OS X and iOS + systems when scanning for valid plugins. + * Added QPluginLoader::staticPlugins(), which returns the list of + built-in plugins (linked to the executable and any loaded libraries). + + - QProcess: + * [QTBUG-32958] Fixed a bug that would cause QProcess to crash if + waitForStarted() were called after an unsuccessful start() + * Added QProcess::nullDevice(), which returns the platform's "blackhole" + device (/dev/null on Unix, NUL on Windows). This can be used with + QProcess::setStandardOutputFile. + * Added enum values QProcess::ForwardedOutputChannel and + QProcess::ForwardedErrorChannel, which allow for more fine-grained + control over which channel is forwarded and which one is captured. + * Added QProcess::ForwardedInputChannel, which allows for stdin to be + forwarded to this process's input. + * [QTBUG-32979] On Unix, QProcess now correctly forwards the siginfo_t + and context parameters of the signal handler to the previous handler. + + - QScopedPointer: + * Added QScopedPointerDeleteLater, a new class that can be used as the + second template to QScopedPointer and will call deleteLater() on the + pointer when it goes out of scope. + + - QSocketNotifier: + * Fixed a bug that caused socket notifiers not to be re-enabled after + they had been activated on BlackBerry. + + - QStandardPaths: + * QStandardPaths::enableTestMode is deprecated and is replaced by + QStandardPaths::setTestModeEnabled. + * Added QStandardPaths::GenericConfigLocation, which refers to a location + where applications can store config files to be shared with other + applications. + + - QThread: + * Added an advisory interrupt mechanism (QThread::requestInterruption and + QThread::isInterruptionRequested). + + - QThreadPool: + * Added method clear() to remove any queued QRunnables. + * Fixed a number of race conditions. + + - QTime: + * [QTBUG-30250] When calling QTime::toString(Qt::TextDate) and + QTime::toString(Qt::ISODate), milliseconds are now included in + the returned string. This also applies to + QDateTime::toString(Qt::TextDate) and + QDateTime::toString(ISODate). + * Added new methods fromMSecsSinceStartOfDay() to create a new QTime + from an msecs value, and msecsSinceStartOfDay() to return the QTime as + the number of msecs since the start of the day. + + - QTimeZone: + * Added new QTimeZone class to support time tone calculations using the + host platform time zone database and the Olsen time zone ID's. + + - QUrl: + * [QTBUG-33229] QUrl now fully accepts non-character unicode points; + they are encoded as percent characters; they can also be pretty + decoded + * Added QUrl::RemoveFilename flag which can be passed to path(), + authority() and toString() and will cause the filename part of the path + (the contents after the last '/') to be removed. + * Added QUrl::fileName(), which returns just the filename part of the + path. + * Added QUrl::NormalizePathSegments flag, which will cause QUrl to + remove "/./" or "/../" sequences as much as possible. It will not + remove "/../" from the beginning of the path. + * Added QUrl::adjusted(), which returns a new QUrl with certain parts of + the original URL removed or normalized. + * Added QUrl::matches(), which can be used to compare parts of two URLs + or to compare two URLs after normalization. + + - QUtf8: + * [QTBUG-33229] UTF-8 now accepts non-character unicode points; these + are not replaced by the replacement character anymore + + - QVariant: + * Fixed QVariant::canConvert with longlong + * Variant containing enum types can now be converted to integer + * [QTBUG-33981] Fixed a bug that would cause QPolygonF to be saved or + loaded incorrectly in QDataStream. + +QtDBus +------ + + - Improved error handling so as to give more feedback to the developer when + certain marshalling or demarshalling actions fail. + - [QTBUG-27809] Fixed some race conditions related to delivering method + reply deliveries. + - Fixed a bug that made non-slot invokables not get listed in the + auto-generated introspections. + +QtDeclarative +------------- + + - ColorDialog: + * Added currentColor property. + + - [QTBUG-32928] ShortcutOverride events now work for QQuickItem + subclasses + QtGui ----- -- [QTBUG-28228] Session Management: - The new QPlatformSessionManager class brings back the session management - functionality. It allows the implementation of platform specific behavior - related to session management. For platform that don't support this - feature the default behavior has not changed. - Both X11 and Windows session management are supported. + + - Accessibility classes are now public allowing accessibility + information for custom widgets/QQuickItems + + - Session Management: + * [QTBUG-28228] The new QPlatformSessionManager class brings back + the session management functionality. It allows the + implementation of platform specific behavior related to session + management. For platform that don't support this feature the + default behavior has not changed. Both X11 and Windows session + management are supported. + + - QPolygonF: + * When a QVariant holds a QPolygonF() then it will be correctly seen as + a null QVariant. + + - [QTBUG-27349] Reintroduced command line argument for positioning + windows (-geometry on X11, -qwindowgeometry on other platforms) QtNetwork --------- -- API was added to store and resume TLS session tickets. + - API was added to store and resume TLS session tickets. + - The minimum support openssl version has been increased to openssl + 1.0. The code to support older versions has not been removed, but is no + longer supported. + - An off-by-one error in NTLM proxy authentication has been fixed. + - Various improvements to reduce the memory used by qtnetwork have been + made. + - Improved support for HTTP proxy authentication. + - Support for preconnecting to servers before making HTTP and HTTPS + connections. This allows for much reduced latency when the hosts to be + connected to are known. -- The minimum support openssl version has been increased to openssl 1.0. The - code to support older versions has not been removed, but is no longer - supported. +QtPrintSupport +-------------- -- An off-by-one error in NTLM proxy authentication has been fixed. - -- Various improvements to reduce the memory used by qtnetwork have been made. - -- Improved support for HTTP proxy authentication. - -- Support for preconnecting to servers before making HTTP and HTTPS - connections. This allows for much reduced latency when the hosts to be - connected to are known. + - QPrintDialog: + * Added support for setting CUPS job options in the print dialog. + * Added support for setting CUPS Banner pages in the print dialog. + * Added support for setting CUPS Page Set (even/odd pages only) in the + print dialog. + * Added support for setting CUPS Pages Per Sheet and Pages Per Sheet + Layout options + * Added CUPS server-side print range support for apps that can't support + print range option themselves QtSql ----- + Changes in Qt 5.1.0 (missing from released changelog dist/changes-5.1.0) -- [QTBUG-28088] Remove dependency of DB driver headers on qsqlcachedresult_p.h. -- Deprecate QSqlError setters. - The constructor is sufficient, since it has a parameter for each member variable. -- Rename the SQL driver header files to _p.h (make private) - The drivers were never public API. They were exposed by mistake in - public headers. What's more, they have #include'd a private header - (qsqlcachedresult_p.h) since at least Qt 4.5.1. That means no one used - those headers in Qt 4 (private headers weren't installed then) and - it's unlikely anyone did in 5.0. -- ODBC: Implemented lastInsertId() for some ODBC compatible databases. -- PSQL: Make lastInsertID work for tables without OIDs. -- [QTBUG-14904] SQLITE: Fix for Sql query aliases with dots -- [QTBUG-2192] ODBC: fix loss of milliseconds when binding QDateTime -- [QTBUG-30076] PSQL: escape backslashes only when server requires it - IMPORTANT: Applications that implemented their own workarounds must be - updated to avoid misbehavior and SQL injection vulnerabilities. -- [QTBUG-10569] ODBC: fixed QODBCResult::exec() return value for DELETE - operations when no rows are affected. -- ODBC: Fixed loss of column metadata on SQL_NO_DATA. -- QSqlTableModel: expose methods for getting primary values. -- ODBC: Fixed possible cause of spurious errors (commit af35ee291a1bbbc8) + - [QTBUG-28088] Remove dependency of DB driver headers on qsqlcachedresult_p.h. + - Deprecate QSqlError setters. The constructor is sufficient, since it has + a parameter for each member variable. + - Rename the SQL driver header files to _p.h (make private) + The drivers were never public API. They were exposed by mistake in + public headers. What's more, they have #include'd a private header + (qsqlcachedresult_p.h) since at least Qt 4.5.1. That means no one used + those headers in Qt 4 (private headers weren't installed then) and + it's unlikely anyone did in 5.0. + - ODBC: Implemented lastInsertId() for some ODBC compatible databases. + - PSQL: Make lastInsertID work for tables without OIDs. + - [QTBUG-14904] SQLITE: Fix for Sql query aliases with dots + - [QTBUG-2192] ODBC: fix loss of milliseconds when binding QDateTime + - [QTBUG-30076] PSQL: escape backslashes only when server requires it + IMPORTANT: Applications that implemented their own workarounds must be + updated to avoid misbehavior and SQL injection vulnerabilities. + - [QTBUG-10569] ODBC: fixed QODBCResult::exec() return value for DELETE + operations when no rows are affected. + - ODBC: Fixed loss of column metadata on SQL_NO_DATA. + - QSqlTableModel: expose methods for getting primary values. + - ODBC: Fixed possible cause of spurious errors (commit af35ee291a1bbbc8) Changes in Qt 5.2.0 -- [QTBUG-29261] IBASE: Construct a valid QTime when creating timestamps for iBase SQL driver. -- [QTBUG-33389] PSQL: Format QDateTime following ISO8601. -- Add QSQLITE_OPEN_URI option to QSQLITE driver + - [QTBUG-29261] IBASE: Construct a valid QTime when creating timestamps for + iBase SQL driver. + - [QTBUG-33389] PSQL: Format QDateTime following ISO8601. + - Add QSQLITE_OPEN_URI option to QSQLITE driver + +QtWidgets +--------- + + - [QTBUG-30255] Fixed a bug where spans across empty cells in a grid + layout got broken. + - [QTBUG-32788] Properly handles Qt::WidgetWithChildrenShortcut + shortcuts in MDI subwindows now. + - [QTBUG-33078] QWidget::setWindowOpacity() now works when called + before QWidget::show(). + - [QTBUG-33247] Changed accessible trees and tables to always expose + hidden headers, instead of only exposing the visible headers. + - [QTBUG-34007] Fixed a crash in tablet support. + - Fixed a bug where the maximum size hint of a layout with spans was + wrong. + - Item delegates now cycle through all three states of tri-state + checkboxes, the same way QCheckBox itself does. + + - QAbstractItemView: + * [QTBUG-7232] In ItemViews scrollbars will now by default only + scroll 1 pixel when scrollMode is set to scrollPerPixel. That is + it will (when scrollMode is scrollPerPixel) do what is stated in + the documentation, and no longer automatically adjust the + scrollbar's singleStep. The user can now control that value. + + - QAbstractScrollArea: + * QAbstractScrollArea now has a SizeAdjustPolicy. If it is set to + AdjustToContents it will make use of the protected viewportSizeHint() + virtual function. This function returns a suggested size based on + contents. Note that although the viewportSizeHint() virtual function + was reserved in 5.0, user code was not supposed to be overriding it + (as the function was private and undocumented). Code that was + overriding viewportSizeHint() needs to be recompiled against 5.2 for + this feature to work correctly. + + - QColorDialog: + * Added a web color QLineEdit. + * [QTBUG-14332] Added a screen color picker button. + * [QTBUG-31998] Does no longer create widgets when using the + platform dialog. + + - QComboBox: + * Added currentData() convenience function which allows to retrieve + the user data set for the current item. + + - QCompleter: + * [QTBUG-3414] Added filterMode property. + + - QDesktopWidget: + * [QTBUG-32567] Fixed emission of workAreaResized() signal. + + - QDialogButtonBox: + * Added a (StandardButtons,QWidget*) constructor. + + - QDockWidget: + * [QTBUG-31044] The position of a dock widget is now kept when + undocking. + * [QTBUG-32260] Fixed a bug where visibilityChanged was signaled + wrongly in certain multi-screen setups. + + - QFileDialog: + * setDefaultSuffix() now removes leading dot characters. + * Introduced DontUseCustomDirectoryIcons. This improves the file + dialog performance under Windows for the case where there are + lots of folders. Went from taking 60 seconds to 2 seconds, on a + SDCard with 10k folders. + * Added setMimeTypeFilters() for mimetype-based filtering, as an + alternative to pattern matching. + * Fixed removing of directories containing hidden or system files. + * Added QUrl-based API for remote files. + * [QTBUG-13182] Improved performance on Windows by + + not resolving NTFS symlinks (15x speedup on pessimistic + workloads) and + + using extensions over calling GetFileAttributesEx() in certain + cases. + * [QTBUG-29403] Fixed potential crash in destructor of + ~QFileInfoGather in threaded applications. + * [QTBUG-34132] QFileDialog does no longer instantiate widgets if a + native dialog will be used instead. Therefore some accessors + which previously returned unused objects will now return null. + As before, you can set the DontUseNativeDialog option to ensure + that widgets will be created and used instead. + * [QTBUG-33039] Does no longer create widgets when using the + platform dialog. + + - QFontDialog: + * Now has finer-grained control over the types of fonts listed, + similar to what QFontComboBox already had. + + - QGraphicsView etc + * Fixed a crash in QGraphicsProxyWidget. + * [QTBUG-8061] Allow handling of mouseDoubleClickEvent in + QGraphicsItems. + * [QTBUG-19036] Make QGraphicsScene::items(QPointF) work using + Qt::{Contains,Intersets}ItemBoundingRect with items that contain + the point in the bounding rectangle, but not their (custom) + shape. + + - QHeaderView: + * [QTBUG-4346] A maximumSize for sections has been introduced. The + maximum section size is by default the largest possible section + size which in Qt 5.2 has been limited to 1048575 pixels. + + - QInputDialog: + * Added getMultiLineText static method. + + - QLineEdit: + * Keep placeholderText visible when focused, until text is added. + * Context-menu actions now have icons. + * Made it possible to add side widgets. + * Made it possible to add a clear button commonly used for item + view filtering as a side widget + + - QListView: + * [QTBUG-1180] Dragging an item outside the QListView in icon mode + no longer loses the icon. Also fixed a bug where under certain + conditions code overriding QAbstractItemView::viewOptions() would + not be called. + + - QMenuBar: + * [QTBUG-32807] Menus now close again on second click. + + - QMessageBox: + * May use native message boxes on some platforms now. + * [QTBUG-6731] It is now possible to select some or all text from a + QMessageBox and copy it to the clipboard. + + - QSizePolicy: + * Added a retainSizeWhenHidden attribute. + + - QSpinBox: + * Values can now be displayed in different bases + (cf. displayIntegerBase property) + * [QTBUG-31602] Size calculation will now be fixed when stylesheets + are used. + + - QSplitter: + * Now gets the default value of opaqueResize property from (new) + QStyle::SH_Splitter_OpaqueResize styleHint. + + - QSystemTrayIcon: + * [QTBUG-31762] Fixed position of system tray icon on Linux/X11 + (except Unity). + * [QTBUG-33461] Increased the maximum length of a system tray + tooltip on Windows to what it was in Qt 4. + + - QTableView: + * [QTBUG-4206] resizeToContents will now adjust to actual contents + and not just visible area. QHeaderView::setAutoResizePrecision() + has been introduced to control how precise the autoResize should + be. + + - QTextEdit: + * Added placeholderText akin to QLineEdit. + * Context-menu actions now have icons. + + - QTreeView: + * QTreeView now has setTreePosition to allow the treestructure to + show data from other columns than logicalindex zero. + + - QWindowContainer: + * [QTBUG-32177] Sets active window correctly now. + + - QWizard: + * [QTBUG-29924] Gave all buttons an objectName(). **************************************************************************** * Platform Specific Changes * **************************************************************************** -Qt for Windows --------------- - - QCoreApplication::arguments() changed the handling of single quotes, double quotes - and the backslash character, in order to match what argv[] contains. - In particular, single quotes are no longer removed from the argument string. - (QTBUG-15379, QTBUG-30628) +Android +------- -Qt for Android --------------- - Project structure and deployment has changed with the introduction of androiddeployqt. Source and build files are no longer mixed, and the build files for the APK package should not be touched. Customizing the build files is possible. Read the documentation for androiddeployqt for more information. - - Status bar visibility is now controlled by Qt, so the window state will override settings in the AndroidManifest.xml. The default is still to show the status bar (QWindow::showMaximized()). Use QWindow::showFullScreen() to hide it. + - Implemented support for accessibility on Android. + +OS X +---- + + - Qt for OS X is now identified by the macro Q_OS_OSX. This complements the + existing Q_OS_DARWIN macro (which identifies the open source kernel and + could identify non-Apple products) and Q_OS_MAC, which is defined for + both OS X and iOS. The old macro Q_OS_MACX is now deprecated. + - Qt no longer complains about new versions of OS X that haven't been + released yet. This will allow Qt 5.2 and future versions to build on + future versions yet to be released. Note that those versions are not + supported until official announcement by the Qt Project. + - Added a number of functions to QtCore to convert to and from + CFString/NSString and QString, CFURL/NSURL and QUrl. + +Windows +------- + + - [QTBUG-33409][QTBUG-8764][QTBUG-10032] Fixed virtual key mapping on + Windows. + - QCoreApplication::arguments() changed the handling of single quotes, double quotes + and the backslash character, in order to match what argv[] contains. + In particular, single quotes are no longer removed from the argument string. + (QTBUG-15379, QTBUG-30628) + +X11 +--- + + - [QTBUG-31762] Fix position of system tray icon (except Unity) + - [QTBUG-27349] Reintroduced command line argument for positioning + windows (-geometry) diff --git a/doc/global/manifest-meta.qdocconf b/doc/global/manifest-meta.qdocconf index e14f5018ea..fb0f5c200b 100644 --- a/doc/global/manifest-meta.qdocconf +++ b/doc/global/manifest-meta.qdocconf @@ -125,8 +125,11 @@ manifestmeta.android.names = "QtQuick/Qt Quick Demo - Maroon*" \ "QtLinguist/Arrow Pad Example" \ "QtGui/Raster Window Example" \ "QtGui/Analog Clock Window Example" \ - "QtMultimediaWidgets/Video Widget Example" \ - "QtMultimediaWidgets/Media Player Example" \ + "QtAndroidExtras/Qt Notifier" \ + "QtMultimedia/QML Video Shader Effects Example" \ + "QtMultimedia/QML Video Example" \ + "QtMultimedia/QML Camera Example" \ + "QtMultimedia/Audio Output Example" \ "QtSVG/Text Object Example" \ "QtQML/Qt Quick Examples - XMLHttpRequest" \ "QtQuick/Qt Quick Particles Examples - *" \ @@ -138,6 +141,9 @@ manifestmeta.android.names = "QtQuick/Qt Quick Demo - Maroon*" \ "QtQuick/Qt Quick Examples - Animation" \ "QtQuick/Qt Quick Examples - Shader Effects" \ "QtQuick/Qt Quick Examples - Canvas" \ + "QtQuick/Qt Quick Examples - MouseArea" \ + "QtQuick/Qt Quick Examples - Positioners" \ + "QtQuick/Qt Quick Examples - Right to Left" \ "QtWidgets/Interview" \ "QtWidgets/Spreadsheet" \ "QtWidgets/Pixelator Example" \ @@ -164,7 +170,6 @@ manifestmeta.android.names = "QtQuick/Qt Quick Demo - Maroon*" \ "QtQML/Extending QML - Methods Example" \ "QtQML/Extending QML - Signal Support Example" \ "QtQML/Extending QML - Attached Properties Example" \ - "QtQuick/Qt Quick Examples - Window and Screen" \ "QtWidgets/Address Book Example" manifestmeta.android.tags = android diff --git a/mkspecs/android-g++/qmake.conf b/mkspecs/android-g++/qmake.conf index 11d62a1efb..e3fa924e23 100644 --- a/mkspecs/android-g++/qmake.conf +++ b/mkspecs/android-g++/qmake.conf @@ -4,7 +4,6 @@ QMAKE_PLATFORM = android QMAKE_COMPILER = gcc CONFIG += android_install unversioned_soname android_deployment_settings -DEFINES += QT_NO_PRINTER QT_NO_PRINTDIALOG include(../common/linux.conf) include(../common/gcc-base-unix.conf) diff --git a/qmake/generators/makefile.cpp b/qmake/generators/makefile.cpp index 3093c834cc..d88c6e447a 100644 --- a/qmake/generators/makefile.cpp +++ b/qmake/generators/makefile.cpp @@ -43,6 +43,9 @@ #include "option.h" #include "cachekeys.h" #include "meta.h" + +#include + #include #include #include @@ -52,6 +55,7 @@ #include #include #include + #if defined(Q_OS_UNIX) #include #else @@ -92,7 +96,7 @@ bool MakefileGenerator::canExecute(const QStringList &cmdline, int *a) const QString MakefileGenerator::mkdir_p_asstring(const QString &dir, bool escape) const { - QString edir = escape ? escapeFilePath(dir) : dir; + QString edir = escape ? escapeFilePath(Option::fixPathToTargetOS(dir, false, false)) : dir; return "@" + makedir.arg(edir); } @@ -3345,11 +3349,17 @@ QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QSt const ProString match = project->first(ProKey(replace_rules.at(r) + ".match")), replace = project->first(ProKey(replace_rules.at(r) + ".replace")); if (!match.isEmpty() /*&& match != replace*/) - ret += " -e \"s," + match + "," + replace + ",g\""; + ret += " -e " + shellQuote("s," + match + "," + replace + ",g"); } ret += " \"" + src + "\" >\"" + dst + "\""; } return ret; } +QString MakefileGenerator::shellQuote(const QString &str) +{ + return isWindowsShell() ? QMakeInternal::IoUtils::shellQuoteWin(str) + : QMakeInternal::IoUtils::shellQuoteUnix(str); +} + QT_END_NAMESPACE diff --git a/qmake/generators/makefile.h b/qmake/generators/makefile.h index 09327c599c..4b8a96c15f 100644 --- a/qmake/generators/makefile.h +++ b/qmake/generators/makefile.h @@ -271,6 +271,7 @@ public: virtual bool mergeBuildProject(MakefileGenerator * /*other*/) { return false; } virtual bool openOutput(QFile &, const QString &build) const; bool isWindowsShell() const { return Option::dir_sep == QLatin1String("\\"); } + QString shellQuote(const QString &str); }; inline void MakefileGenerator::setNoIO(bool o) diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index bea01aaf80..35639a33bf 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -502,7 +502,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) incr_lflags += var("QMAKE_LFLAGS_RELEASE"); t << incr_target_dir << ": $(INCREMENTAL_OBJECTS)\n\t"; if(!destdir.isEmpty()) - t << mkdir_p_asstring(destdir) << "\n\t"; + t << mkdir_p_asstring(destdir, false) << "\n\t"; t << "$(LINK) " << incr_lflags << " " << var("QMAKE_LINK_O_FLAG") << incr_target_dir << " $(INCREMENTAL_OBJECTS)\n"; //communicated below @@ -528,7 +528,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) << " " << var("POST_TARGETDEPS"); } if(!destdir.isEmpty()) - t << "\n\t" << mkdir_p_asstring(destdir); + t << "\n\t" << mkdir_p_asstring(destdir, false); if(!project->isEmpty("QMAKE_PRE_LINK")) t << "\n\t" << var("QMAKE_PRE_LINK"); @@ -685,7 +685,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) if(!project->first("QMAKE_PKGINFO").isEmpty()) { ProString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO")); - QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents"); + QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents"; t << pkginfo << ": \n\t"; if(!destdir.isEmpty()) t << mkdir_p_asstring(destdir) << "\n\t"; @@ -697,7 +697,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) if(!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) { ProString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE")); bundledFiles << resources; - QString destdir = escapeFilePath(project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources"); + QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources"; t << resources << ": \n\t"; t << mkdir_p_asstring(destdir) << "\n\t"; t << "@touch " << resources << "\n\t\n"; @@ -710,7 +710,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) QString destdir = info_plist_out.section(Option::dir_sep, 0, -2); t << info_plist_out << ": \n\t"; if(!destdir.isEmpty()) - t << mkdir_p_asstring(destdir) << "\n\t"; + t << mkdir_p_asstring(destdir, false) << "\n\t"; ProStringList commonSedArgs; if (!project->values("VERSION").isEmpty()) commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",g\" "; diff --git a/qmake/generators/win32/msvc_nmake.cpp b/qmake/generators/win32/msvc_nmake.cpp index 37ba3a66ef..723f2a03e9 100644 --- a/qmake/generators/win32/msvc_nmake.cpp +++ b/qmake/generators/win32/msvc_nmake.cpp @@ -235,8 +235,10 @@ QString NmakeMakefileGenerator::defaultInstall(const QString &t) if(targetdir.right(1) != Option::dir_sep) targetdir += Option::dir_sep; - if(t == "target" && project->first("TEMPLATE") == "lib") { - if(project->isActiveConfig("shared") && project->isActiveConfig("debug")) { + if (project->isActiveConfig("debug")) { + if (t == "dlltarget" + || (project->first("TEMPLATE") == "lib" + && project->isActiveConfig("shared"))) { QString pdb_target = getPdbTarget(); pdb_target.remove('"'); QString src_targ = (project->isEmpty("DESTDIR") ? QString("$(DESTDIR)") : project->first("DESTDIR")) + pdb_target; diff --git a/qmake/main.cpp b/qmake/main.cpp index b1929e0d21..79e3739f56 100644 --- a/qmake/main.cpp +++ b/qmake/main.cpp @@ -106,7 +106,7 @@ static int doSed(int argc, char **argv) } } if (phase == 1 - && (c == QLatin1Char('+') || c == QLatin1Char('?') + && (c == QLatin1Char('+') || c == QLatin1Char('?') || c == QLatin1Char('|') || c == QLatin1Char('{') || c == QLatin1Char('}') || c == QLatin1Char('(') || c == QLatin1Char(')'))) { // translate sed rx to QRegExp @@ -133,6 +133,7 @@ static int doSed(int argc, char **argv) SedSubst subst; subst.from = QRegExp(phases.at(0)); subst.to = phases.at(1); + subst.to.replace("\\\\", "\\"); // QString::replace(rx, sub) groks \1, but not \\. substs << subst; } } else if (argv[i][0] == '-' && argv[i][1] != 0) { diff --git a/src/3rdparty/xcb/include/xcb/xkb.h b/src/3rdparty/xcb/include/xcb/xkb.h index 44b0a8d1ae..0180ec8b58 100644 --- a/src/3rdparty/xcb/include/xcb/xkb.h +++ b/src/3rdparty/xcb/include/xcb/xkb.h @@ -114,8 +114,8 @@ typedef enum xcb_xkb_control_t { XCB_XKB_CONTROL_GROUPS_WRAP = 134217728, XCB_XKB_CONTROL_INTERNAL_MODS = 268435456, XCB_XKB_CONTROL_IGNORE_LOCK_MODS = 536870912, - XCB_XKB_CONTROL_PER_KEY_REPEAT = 1073741824, - XCB_XKB_CONTROL_CONTROLS_ENABLED = 2147483648 + XCB_XKB_CONTROL_PER_KEY_REPEAT = 1073741824u, + XCB_XKB_CONTROL_CONTROLS_ENABLED = 2147483648u } xcb_xkb_control_t; typedef enum xcb_xkb_axfb_opt_t { diff --git a/src/3rdparty/xcb/libxcb/fix_compiler_warning_on_32bit_systems.patch b/src/3rdparty/xcb/libxcb/fix_compiler_warning_on_32bit_systems.patch new file mode 100644 index 0000000000..240c20d2ac --- /dev/null +++ b/src/3rdparty/xcb/libxcb/fix_compiler_warning_on_32bit_systems.patch @@ -0,0 +1,15 @@ +diff --git a/src/3rdparty/xcb/include/xcb/xkb.h b/src/3rdparty/xcb/include/xcb/xkb.h +index 44b0a8d..0180ec8 100644 +--- a/src/3rdparty/xcb/include/xcb/xkb.h ++++ b/src/3rdparty/xcb/include/xcb/xkb.h +@@ -114,8 +114,8 @@ typedef enum xcb_xkb_control_t { + XCB_XKB_CONTROL_GROUPS_WRAP = 134217728, + XCB_XKB_CONTROL_INTERNAL_MODS = 268435456, + XCB_XKB_CONTROL_IGNORE_LOCK_MODS = 536870912, +- XCB_XKB_CONTROL_PER_KEY_REPEAT = 1073741824, +- XCB_XKB_CONTROL_CONTROLS_ENABLED = 2147483648 ++ XCB_XKB_CONTROL_PER_KEY_REPEAT = 1073741824u, ++ XCB_XKB_CONTROL_CONTROLS_ENABLED = 2147483648u + } xcb_xkb_control_t; + + typedef enum xcb_xkb_axfb_opt_t { diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index f23478d75c..44077132d4 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -735,6 +735,20 @@ # define Q_COMPILER_CLASS_ENUM # define Q_COMPILER_ATOMICS # endif /* VC 11 */ +# if _MSC_VER >= 1800 + /* C++11 features in VC12 = VC2013 */ +# define Q_COMPILER_DEFAULT_MEMBERS +# define Q_COMPILER_DELETE_MEMBERS +# define Q_COMPILER_DELEGATING_CONSTRUCTORS +# define Q_COMPILER_EXPLICIT_CONVERSIONS +# define Q_COMPILER_NONSTATIC_MEMBER_INIT +# define Q_COMPILER_INITIALIZER_LISTS +// implemented in principle, but has a bug that makes it unusable: http://connect.microsoft.com/VisualStudio/feedback/details/802058/c-11-unified-initialization-fails-with-c-style-arrays +// #define Q_COMPILER_UNIFORM_INIT +# define Q_COMPILER_RAW_STRINGS +# define Q_COMPILER_TEMPLATE_ALIAS +# define Q_COMPILER_VARIADIC_TEMPLATES +# endif /* VC 12 */ #endif /* Q_CC_MSVC */ #ifdef __cplusplus diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index b27d0bf53f..0c72bd7022 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -985,8 +985,7 @@ bool qSharedBuild() Q_DECL_NOTHROW /*! \fn QSysInfo::MacVersion QSysInfo::macVersion() - Returns the version of Mac OS X on which the application is run (Mac OS X - Only). + Returns the version of Darwin (OS X or iOS) on which the application is run. */ /*! @@ -1056,7 +1055,7 @@ bool qSharedBuild() Q_DECL_NOTHROW \enum QSysInfo::MacVersion This enum provides symbolic names for the various versions of the - OS X operating system. On OS X, the + Darwin operating system, covering both OS X and iOS. The QSysInfo::MacintoshVersion variable gives the version of the system on which the application is run. @@ -1084,6 +1083,15 @@ bool qSharedBuild() Q_DECL_NOTHROW \value MV_MOUNTAINLION Apple codename for MV_10_8 \value MV_MAVERICKS Apple codename for MV_10_9 + \value MV_IOS iOS (any) + \value MV_IOS_4_3 iOS 4.3 + \value MV_IOS_5_0 iOS 5.0 + \value MV_IOS_5_1 iOS 5.1 + \value MV_IOS_6_0 iOS 6.0 + \value MV_IOS_6_1 iOS 6.1 + \value MV_IOS_7_0 iOS 7.0 + \value MV_IOS_7_1 iOS 7.1 + \sa WinVersion */ @@ -1706,13 +1714,15 @@ static const unsigned int qt_one = 1; const int QSysInfo::ByteOrder = ((*((unsigned char *) &qt_one) == 0) ? BigEndian : LittleEndian); #endif -#if defined(Q_OS_MACX) +#if defined(Q_OS_MAC) QT_BEGIN_INCLUDE_NAMESPACE #include "private/qcore_mac_p.h" #include "qnamespace.h" QT_END_INCLUDE_NAMESPACE +#if defined(Q_OS_OSX) + Q_CORE_EXPORT OSErr qt_mac_create_fsref(const QString &file, FSRef *fsref) { return FSPathMakeRef(reinterpret_cast(file.toUtf8().constData()), fsref, 0); @@ -1728,17 +1738,17 @@ Q_CORE_EXPORT void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding e Q_CORE_EXPORT QString qt_mac_from_pascal_string(const Str255 pstr) { return QCFString(CFStringCreateWithPascalString(0, pstr, CFStringGetSystemEncoding())); } -#endif // defined(Q_OS_MACX) - -#if defined(Q_OS_MAC) +#endif // defined(Q_OS_OSX) QSysInfo::MacVersion QSysInfo::macVersion() { -#ifdef Q_OS_MACX +#if defined(Q_OS_OSX) SInt32 gestalt_version; if (Gestalt(gestaltSystemVersion, &gestalt_version) == noErr) { return QSysInfo::MacVersion(((gestalt_version & 0x00F0) >> 4) + 2); } +#elif defined(Q_OS_IOS) + return qt_ios_version(); // qtcore_mac_objc.mm #endif return QSysInfo::MV_Unknown; } diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index c2bb7173ba..3c3d35e373 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -520,6 +520,16 @@ Q_DECL_CONSTEXPR inline const T &qBound(const T &min, const T &val, const T &max # define QT_MAC_DEPLOYMENT_TARGET_BELOW(osx, ios) \ (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && osx != __MAC_NA && __MAC_OS_X_VERSION_MIN_REQUIRED < osx) || \ (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && ios != __IPHONE_NA && __IPHONE_OS_VERSION_MIN_REQUIRED < ios) + +# define QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(ios) \ + QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_NA, ios) +# define QT_OSX_PLATFORM_SDK_EQUAL_OR_ABOVE(osx) \ + QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(osx, __IPHONE_NA) + +# define QT_IOS_DEPLOYMENT_TARGET_BELOW(ios) \ + QT_MAC_DEPLOYMENT_TARGET_BELOW(__MAC_NA, ios) +# define QT_OSX_DEPLOYMENT_TARGET_BELOW(osx) \ + QT_MAC_DEPLOYMENT_TARGET_BELOW(osx, __IPHONE_NA) #endif /* diff --git a/src/corelib/global/qsysinfo.h b/src/corelib/global/qsysinfo.h index bd76f908e7..38735c12de 100644 --- a/src/corelib/global/qsysinfo.h +++ b/src/corelib/global/qsysinfo.h @@ -120,6 +120,7 @@ public: #endif #ifdef Q_OS_MAC +# define Q_MV_IOS(major, minor) (QSysInfo::MV_IOS | major << 4 | minor) enum MacVersion { MV_Unknown = 0x0000, @@ -146,7 +147,17 @@ public: MV_SNOWLEOPARD = MV_10_6, MV_LION = MV_10_7, MV_MOUNTAINLION = MV_10_8, - MV_MAVERICKS = MV_10_9 + MV_MAVERICKS = MV_10_9, + + /* iOS */ + MV_IOS = 1 << 8, + MV_IOS_4_3 = Q_MV_IOS(4, 3), + MV_IOS_5_0 = Q_MV_IOS(5, 0), + MV_IOS_5_1 = Q_MV_IOS(5, 1), + MV_IOS_6_0 = Q_MV_IOS(6, 0), + MV_IOS_6_1 = Q_MV_IOS(6, 1), + MV_IOS_7_0 = Q_MV_IOS(7, 0), + MV_IOS_7_1 = Q_MV_IOS(7, 1) }; static const MacVersion MacintoshVersion; static MacVersion macVersion(); diff --git a/src/corelib/io/qfileselector.cpp b/src/corelib/io/qfileselector.cpp index eed9e8870a..72e4198fb0 100644 --- a/src/corelib/io/qfileselector.cpp +++ b/src/corelib/io/qfileselector.cpp @@ -359,6 +359,8 @@ QStringList QFileSelectorPrivate::platformSelectors() ret << QStringLiteral("android"); # elif defined(Q_OS_BLACKBERRY) ret << QStringLiteral("blackberry"); +# elif defined(Q_OS_QNX) + ret << QStringLiteral("qnx"); # elif defined(Q_OS_IOS) ret << QStringLiteral("ios"); # elif defined(Q_OS_LINUX) diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 26ad9f488c..7625a74381 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -98,6 +98,9 @@ mac:!nacl { kernel/qcore_mac.cpp OBJECTIVE_SOURCES += \ kernel/qcore_mac_objc.mm + + # We need UIKit for UIDevice + ios: LIBS_PRIVATE += -framework UIKit } nacl { diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm index 7b9eb67969..73f8296021 100644 --- a/src/corelib/kernel/qcore_mac_objc.mm +++ b/src/corelib/kernel/qcore_mac_objc.mm @@ -41,6 +41,10 @@ #include +#ifdef Q_OS_IOS +#import +#endif + QT_BEGIN_NAMESPACE NSString *QCFString::toNSString(const QString &string) @@ -54,5 +58,31 @@ QString QCFString::toQString(const NSString *nsstr) return toQString(reinterpret_cast(nsstr)); } +#ifdef Q_OS_IOS +QSysInfo::MacVersion qt_ios_version() +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + int major = 0, minor = 0; + NSArray *components = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."]; + switch ([components count]) { + case 3: + // We don't care about the patch version + case 2: + minor = [[components objectAtIndex:1] intValue]; + // fall through + case 1: + major = [[components objectAtIndex:0] intValue]; + break; + default: + Q_UNREACHABLE(); + } + + [pool release]; + + return QSysInfo::MacVersion(Q_MV_IOS(major, minor)); +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index fa911fb967..f491be9768 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -143,6 +143,10 @@ private: QString string; }; +#ifdef Q_OS_IOS +QSysInfo::MacVersion qt_ios_version(); +#endif + QT_END_NAMESPACE #endif // QCORE_MAC_P_H diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 6fb22054cc..e02026ca4c 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Olivier Goffart ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -177,6 +178,7 @@ private: void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = 0; +void (*QAbstractDeclarativeData::destroyed_qml1)(QAbstractDeclarativeData *, QObject *) = 0; void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = 0; void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = 0; int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = 0; @@ -805,8 +807,12 @@ QObject::~QObject() } } - if (d->declarativeData) - QAbstractDeclarativeData::destroyed(d->declarativeData, this); + if (d->declarativeData) { + if (QAbstractDeclarativeData::destroyed) + QAbstractDeclarativeData::destroyed(d->declarativeData, this); + if (QAbstractDeclarativeData::destroyed_qml1) + QAbstractDeclarativeData::destroyed_qml1(d->declarativeData, this); + } // set ref to zero to indicate that this object has been deleted if (d->currentSender != 0) @@ -847,9 +853,9 @@ QObject::~QObject() // The destroy operation must happen outside the lock if (c->isSlotObject) { + c->isSlotObject = false; locker.unlock(); c->slotObj->destroyIfLastRef(); - c->isSlotObject = false; locker.relock(); } c->deref(); @@ -864,15 +870,29 @@ QObject::~QObject() d->connectionLists = 0; } - // disconnect all senders + /* Disconnect all senders: + * This loop basically just does + * for (node = d->senders; node; node = node->next) { ... } + * + * We need to temporarily unlock the receiver mutex to destroy the functors or to lock the + * sender's mutex. And when the mutex is released, node->next might be destroyed by another + * thread. That's why we set node->prev to &node, that way, if node is destroyed, node will + * be updated. + */ QObjectPrivate::Connection *node = d->senders; while (node) { QObject *sender = node->sender; + // Send disconnectNotify before removing the connection from sender's connection list. + // This ensures any eventual destructor of sender will block on getting receiver's lock + // and not finish until we release it. + sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index)); QMutex *m = signalSlotLock(sender); node->prev = &node; bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); //the node has maybe been removed while the mutex was unlocked in relock? if (!node || node->sender != sender) { + // We hold the wrong mutex + Q_ASSERT(needToUnlock); m->unlock(); continue; } @@ -881,8 +901,6 @@ QObject::~QObject() if (senderLists) senderLists->dirty = true; - int signal_index = node->signal_index; - QtPrivate::QSlotObjectBase *slotObj = Q_NULLPTR; if (node->isSlotObject) { slotObj = node->slotObj; @@ -894,12 +912,12 @@ QObject::~QObject() m->unlock(); if (slotObj) { + if (node) + node->prev = &node; locker.unlock(); slotObj->destroyIfLastRef(); locker.relock(); } - - sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), signal_index)); } } @@ -1858,7 +1876,7 @@ void QObjectPrivate::setParent_helper(QObject *o) } } } - if (!isDeletingChildren && declarativeData) + if (!isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged) QAbstractDeclarativeData::parentChanged(declarativeData, q, o); } @@ -3163,7 +3181,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, && (slot == 0 || (c->isSlotObject && c->slotObj->compare(slot)))))) { bool needToUnlock = false; QMutex *receiverMutex = 0; - if (!receiver) { + if (c->receiver) { receiverMutex = signalSlotLock(c->receiver); // need to relock this receiver and sender in the correct order needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex); @@ -3180,9 +3198,9 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, c->receiver = 0; if (c->isSlotObject) { + c->isSlotObject = false; senderMutex->unlock(); c->slotObj->destroyIfLastRef(); - c->isSlotObject = false; senderMutex->lock(); } @@ -3211,8 +3229,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, QObject *s = const_cast(sender); QMutex *senderMutex = signalSlotLock(sender); - QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0; - QOrderedMutexLocker locker(senderMutex, receiverMutex); + QMutexLocker locker(senderMutex); QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; if (!connectionLists) diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h index 1bbd548be6..d2996e6e4d 100644 --- a/src/corelib/kernel/qobject_impl.h +++ b/src/corelib/kernel/qobject_impl.h @@ -99,6 +99,8 @@ namespace QtPrivate { template ::Value > struct ConnectionTypes { static const int *types() { return 0; } }; + template <> struct ConnectionTypes, true> + { static const int *types() { return 0; } }; template struct ConnectionTypes, true> { static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper::qt_metatype_id())..., 0 }; return t; } }; #endif diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index f5745515c9..cd2d592cec 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -90,6 +90,7 @@ class Q_CORE_EXPORT QAbstractDeclarativeData { public: static void (*destroyed)(QAbstractDeclarativeData *, QObject *); + static void (*destroyed_qml1)(QAbstractDeclarativeData *, QObject *); static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *); static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **); static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int); diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp index 9288dc7756..5fec06b30a 100644 --- a/src/corelib/tools/qtimezone.cpp +++ b/src/corelib/tools/qtimezone.cpp @@ -105,7 +105,7 @@ public: QTimeZoneSingleton() : backend(newBackendTimeZone()) {} // The backend_tz is the tz to use in static methods such as availableTimeZoneIds() and - // isTimeZoneIdAvailable() and to create named Olsen time zones. This is usually the host + // isTimeZoneIdAvailable() and to create named IANA time zones. This is usually the host // system, but may be different if the host resources are insufficient or if // QT_NO_SYSTEMLOCALE is set. A simple UTC backend is used if no alternative is available. QSharedDataPointer backend; @@ -135,18 +135,21 @@ Q_GLOBAL_STATIC(QTimeZoneSingleton, global_tz); \section1 - \section2 Olsen Time Zone IDs + \section2 IANA Time Zone IDs - QTimeZone uses the Olsen time zone IDs as defined in the IANA Time Zone + QTimeZone uses the IANA time zone IDs as defined in the IANA Time Zone Database (http://www.iana.org/time-zones). This is to ensure a standard ID - across all supported platforms. Most platforms support the Olsen IDs + across all supported platforms. Most platforms support the IANA IDs and the IANA Database natively, but for Windows a mapping is required to the native IDs. See below for more details. - The Olsen IDs can and do change on a regular basis, and can vary depending + The IANA IDs can and do change on a regular basis, and can vary depending on how recently the host system data was updated. As such you cannot rely on any given ID existing on any host system. You must use - availableTimeZoneIds() to determine what Olsen IDs are available. + availableTimeZoneIds() to determine what IANA IDs are available. + + The IANA IDs and database are also know as the Olson IDs and database, + named after their creator. \section2 UTC Offset Time Zones @@ -165,7 +168,7 @@ Q_GLOBAL_STATIC(QTimeZoneSingleton, global_tz); the current year. QTimeZone uses a conversion table derived form the Unicode CLDR data to map - between Olsen IDs and Windows IDs. Depending on your version of Windows + between IANA IDs and Windows IDs. Depending on your version of Windows and Qt, this table may not be able to provide a valid conversion, in which "UTC" will be returned. @@ -180,7 +183,7 @@ Q_GLOBAL_STATIC(QTimeZoneSingleton, global_tz); If you require a QDateTime that uses the current system time zone at any given moment then you should use a Qt::TimeSpec of Qt::LocalTime. - The method systemTimeZoneId() returns the current system Olsen time zone + The method systemTimeZoneId() returns the current system IANA time zone ID which on OSX and Linux will always be correct. On Windows this ID is translated from the the Windows system ID using an internal translation table and the user's selected country. As a consequence there is a small @@ -323,7 +326,7 @@ QTimeZone::QTimeZone() } /*! - Creates an instance of the requested time zone \a olsenId. + Creates an instance of the requested time zone \a ianaId. The ID must be one of the available system IDs otherwise an invalid time zone will be returned. @@ -331,14 +334,14 @@ QTimeZone::QTimeZone() \sa availableTimeZoneIds() */ -QTimeZone::QTimeZone(const QByteArray &olsenId) +QTimeZone::QTimeZone(const QByteArray &ianaId) { // Try and see if it's a valid UTC offset ID, just as quick to try create as look-up - d = new QUtcTimeZonePrivate(olsenId); + d = new QUtcTimeZonePrivate(ianaId); // If not a valid UTC offset ID then try create it with the system backend // Relies on backend not creating valid tz with invalid name if (!d->isValid()) - d = newBackendTimeZone(olsenId); + d = newBackendTimeZone(ianaId); } /*! @@ -359,14 +362,14 @@ QTimeZone::QTimeZone(int offsetSeconds) } /*! - Creates a custom time zone with an ID of \a olsenId and an offset from UTC + Creates a custom time zone with an ID of \a ianaId and an offset from UTC of \a offsetSeconds. The \a name will be the name used by displayName() for the LongName, the \a abbreviation will be used by displayName() for the ShortName and by abbreviation(), and the optional \a country will be used by country(). The \a comment is an optional note that may be displayed in a GUI to assist users in selecting a time zone. - The \a olsenId must not be one of the available system IDs returned by + The \a ianaId must not be one of the available system IDs returned by availableTimeZoneIds(). The \a offsetSeconds from UTC must be in the range -14 hours to +14 hours. @@ -374,12 +377,12 @@ QTimeZone::QTimeZone(int offsetSeconds) default value of QLocale::AnyCountry. */ -QTimeZone::QTimeZone(const QByteArray &olsenId, int offsetSeconds, const QString &name, +QTimeZone::QTimeZone(const QByteArray &ianaId, int offsetSeconds, const QString &name, const QString &abbreviation, QLocale::Country country, const QString &comment) { - // olsenId must be a valid ID and must not clash with the standard system names - if (QTimeZonePrivate::isValidId(olsenId) && !availableTimeZoneIds().contains(olsenId)) - d = new QUtcTimeZonePrivate(olsenId, offsetSeconds, name, abbreviation, country, comment); + // ianaId must be a valid ID and must not clash with the standard system names + if (QTimeZonePrivate::isValidId(ianaId) && !availableTimeZoneIds().contains(ianaId)) + d = new QUtcTimeZonePrivate(ianaId, offsetSeconds, name, abbreviation, country, comment); else d = 0; } @@ -473,10 +476,10 @@ bool QTimeZone::isValid() const } /*! - Returns the Olsen ID for the time zone. + Returns the IANA ID for the time zone. - Olsen IDs are used on all platforms. On Windows these are translated - from the Windows ID into the closest Olsen ID for the time zone and country. + IANA IDs are used on all platforms. On Windows these are translated + from the Windows ID into the closest IANA ID for the time zone and country. */ QByteArray QTimeZone::id() const @@ -705,6 +708,9 @@ bool QTimeZone::hasTransitions() const This is most useful when you have a Transition time and wish to find the Transition after it. + If there is no transition after the given \a afterDateTime then an invalid + OffsetData will be returned with an invalid QDateTime. + The given \a afterDateTime is exclusive. \sa hasTransitions(), previousTransition(), transitions() @@ -723,6 +729,9 @@ QTimeZone::OffsetData QTimeZone::nextTransition(const QDateTime &afterDateTime) This is most useful when you have a Transition time and wish to find the Transition before it. + If there is no transition before the given \a beforeDateTime then an invalid + OffsetData will be returned with an invalid QDateTime. + The given \a beforeDateTime is exclusive. \sa hasTransitions(), nextTransition(), transitions() @@ -760,7 +769,7 @@ QTimeZone::OffsetDataList QTimeZone::transitions(const QDateTime &fromDateTime, // Static methods /*! - Returns the current system time zone Olsen ID. + Returns the current system time zone IANA ID. On Windows this ID is translated from the the Windows ID using an internal translation table and the user's selected country. As a consequence there @@ -774,20 +783,20 @@ QByteArray QTimeZone::systemTimeZoneId() } /*! - Returns \c true if a given time zone \a olsenId is available on this system. + Returns \c true if a given time zone \a ianaId is available on this system. \sa availableTimeZoneIds() */ -bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &olsenId) +bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId) { // isValidId is not strictly required, but faster to weed out invalid // IDs as availableTimeZoneIds() may be slow - return (QTimeZonePrivate::isValidId(olsenId) && (availableTimeZoneIds().contains(olsenId))); + return (QTimeZonePrivate::isValidId(ianaId) && (availableTimeZoneIds().contains(ianaId))); } /*! - Returns a list of all available Olsen time zone IDs on this system. + Returns a list of all available IANA time zone IDs on this system. \sa isTimeZoneIdAvailable() */ @@ -802,7 +811,7 @@ QList QTimeZone::availableTimeZoneIds() } /*! - Returns a list of all available Olsen time zone IDs for a given \a country. + Returns a list of all available IANA time zone IDs for a given \a country. As a special case, a \a country of Qt::AnyCountry returns those time zones that do not have any country related to them, such as UTC. If you require @@ -822,7 +831,7 @@ QList QTimeZone::availableTimeZoneIds(QLocale::Country country) } /*! - Returns a list of all available Olsen time zone IDs with a given standard + Returns a list of all available IANA time zone IDs with a given standard time offset of \a offsetSeconds. \sa isTimeZoneIdAvailable() @@ -838,79 +847,79 @@ QList QTimeZone::availableTimeZoneIds(int offsetSeconds) } /*! - Returns the Windows ID equivalent to the given \a olsenId. + Returns the Windows ID equivalent to the given \a ianaId. - \sa windowsIdToDefaultOlsenId(), windowsIdToOlsenIds() + \sa windowsIdToDefaultIanaId(), windowsIdToIanaIds() */ -QByteArray QTimeZone::olsenIdToWindowsId(const QByteArray &olsenId) +QByteArray QTimeZone::ianaIdToWindowsId(const QByteArray &ianaId) { - return QTimeZonePrivate::olsenIdToWindowsId(olsenId); + return QTimeZonePrivate::ianaIdToWindowsId(ianaId); } /*! - Returns the default Olsen ID for a given \a windowsId. + Returns the default IANA ID for a given \a windowsId. - Because a Windows ID can cover several Olsen IDs in several different - countries, this function returns the most frequently used Olsen ID with no + Because a Windows ID can cover several IANA IDs in several different + countries, this function returns the most frequently used IANA ID with no regard for the country and should thus be used with care. It is usually best to request the default for a specific country. - \sa olsenIdToWindowsId(), windowsIdToOlsenIds() + \sa ianaIdToWindowsId(), windowsIdToIanaIds() */ -QByteArray QTimeZone::windowsIdToDefaultOlsenId(const QByteArray &windowsId) +QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId) { - return QTimeZonePrivate::windowsIdToDefaultOlsenId(windowsId); + return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId); } /*! - Returns the default Olsen ID for a given \a windowsId and \a country. + Returns the default IANA ID for a given \a windowsId and \a country. - Because a Windows ID can cover several Olsen IDs within a given country, - the most frequently used Olsen ID in that country is returned. + Because a Windows ID can cover several IANA IDs within a given country, + the most frequently used IANA ID in that country is returned. - As a special case, QLocale::AnyCountry returns the default of those Olsen IDs + As a special case, QLocale::AnyCountry returns the default of those IANA IDs that do not have any specific country. - \sa olsenIdToWindowsId(), windowsIdToOlsenIds() + \sa ianaIdToWindowsId(), windowsIdToIanaIds() */ -QByteArray QTimeZone::windowsIdToDefaultOlsenId(const QByteArray &windowsId, +QByteArray QTimeZone::windowsIdToDefaultIanaId(const QByteArray &windowsId, QLocale::Country country) { - return QTimeZonePrivate::windowsIdToDefaultOlsenId(windowsId, country); + return QTimeZonePrivate::windowsIdToDefaultIanaId(windowsId, country); } /*! - Returns all the Olsen IDs for a given \a windowsId. + Returns all the IANA IDs for a given \a windowsId. The returned list is sorted alphabetically. - \sa olsenIdToWindowsId(), windowsIdToDefaultOlsenId() + \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId() */ -QList QTimeZone::windowsIdToOlsenIds(const QByteArray &windowsId) +QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId) { - return QTimeZonePrivate::windowsIdToOlsenIds(windowsId); + return QTimeZonePrivate::windowsIdToIanaIds(windowsId); } /*! - Returns all the Olsen IDs for a given \a windowsId and \a country. + Returns all the IANA IDs for a given \a windowsId and \a country. - As a special case QLocale::AnyCountry returns those Olsen IDs that do + As a special case QLocale::AnyCountry returns those IANA IDs that do not have any specific country. The returned list is in order of frequency of usage, i.e. larger zones within a country are listed first. - \sa olsenIdToWindowsId(), windowsIdToDefaultOlsenId() + \sa ianaIdToWindowsId(), windowsIdToDefaultIanaId() */ -QList QTimeZone::windowsIdToOlsenIds(const QByteArray &windowsId, +QList QTimeZone::windowsIdToIanaIds(const QByteArray &windowsId, QLocale::Country country) { - return QTimeZonePrivate::windowsIdToOlsenIds(windowsId, country); + return QTimeZonePrivate::windowsIdToIanaIds(windowsId, country); } #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/tools/qtimezone.h b/src/corelib/tools/qtimezone.h index cbc4f3e4ad..b038d66a65 100644 --- a/src/corelib/tools/qtimezone.h +++ b/src/corelib/tools/qtimezone.h @@ -77,7 +77,7 @@ public: typedef QVector OffsetDataList; QTimeZone(); - explicit QTimeZone(const QByteArray &olsenId); + explicit QTimeZone(const QByteArray &ianaId); explicit QTimeZone(int offsetSeconds); /*implicit*/ QTimeZone(const QByteArray &zoneId, int offsetSeconds, const QString &name, const QString &abbreviation, QLocale::Country country = QLocale::AnyCountry, @@ -126,18 +126,18 @@ public: static QByteArray systemTimeZoneId(); - static bool isTimeZoneIdAvailable(const QByteArray &olsenId); + static bool isTimeZoneIdAvailable(const QByteArray &ianaId); static QList availableTimeZoneIds(); static QList availableTimeZoneIds(QLocale::Country country); static QList availableTimeZoneIds(int offsetSeconds); - static QByteArray olsenIdToWindowsId(const QByteArray &olsenId); - static QByteArray windowsIdToDefaultOlsenId(const QByteArray &windowsId); - static QByteArray windowsIdToDefaultOlsenId(const QByteArray &windowsId, + static QByteArray ianaIdToWindowsId(const QByteArray &ianaId); + static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId); + static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId, QLocale::Country country); - static QList windowsIdToOlsenIds(const QByteArray &windowsId); - static QList windowsIdToOlsenIds(const QByteArray &windowsId, + static QList windowsIdToIanaIds(const QByteArray &windowsId); + static QList windowsIdToIanaIds(const QByteArray &windowsId, QLocale::Country country); private: diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp index 8d5d60bf37..08a5ce0861 100644 --- a/src/corelib/tools/qtimezoneprivate.cpp +++ b/src/corelib/tools/qtimezoneprivate.cpp @@ -264,7 +264,8 @@ QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) // If the local msecs is less than the real local time of the transition // then get the previous transition to use instead if (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { - while (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { + while (tran.atMSecsSinceEpoch != invalidMSecs() + && forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { nextTran = tran; tran = previousTransition(tran.atMSecsSinceEpoch); } @@ -272,7 +273,8 @@ QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) // The zone msecs is after the transition, so check it is before the next tran // If not try use the next transition instead nextTran = nextTransition(tran.atMSecsSinceEpoch); - while (forLocalMSecs >= nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)) { + while (nextTran.atMSecsSinceEpoch != invalidMSecs() + && forLocalMSecs >= nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)) { tran = nextTran; nextTran = nextTransition(tran.atMSecsSinceEpoch); } @@ -292,7 +294,8 @@ QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) // then use the prev tran as we default to the FirstOccurrence // TODO Check if faster to just always get prev tran, or if faster using 6 hour check. Data dstTran = previousTransition(tran.atMSecsSinceEpoch); - if (dstTran.daylightTimeOffset > 0 && diffPrevTran < (dstTran.daylightTimeOffset * 1000)) + if (dstTran.atMSecsSinceEpoch != invalidMSecs() + && dstTran.daylightTimeOffset > 0 && diffPrevTran < (dstTran.daylightTimeOffset * 1000)) tran = dstTran; } else if (diffNextTran >= 0 && diffNextTran <= (nextTran.daylightTimeOffset * 1000)) { // If time falls within last hour of standard time then is actually the missing hour @@ -328,10 +331,11 @@ QTimeZonePrivate::DataList QTimeZonePrivate::transitions(qint64 fromMSecsSinceEp qint64 toMSecsSinceEpoch) const { DataList list; - if (toMSecsSinceEpoch > fromMSecsSinceEpoch) { + if (toMSecsSinceEpoch >= fromMSecsSinceEpoch) { // fromMSecsSinceEpoch is inclusive but nextTransitionTime() is exclusive so go back 1 msec Data next = nextTransition(fromMSecsSinceEpoch - 1); - while (next.atMSecsSinceEpoch <= toMSecsSinceEpoch) { + while (next.atMSecsSinceEpoch != invalidMSecs() + && next.atMSecsSinceEpoch <= toMSecsSinceEpoch) { list.append(next); next = nextTransition(next.atMSecsSinceEpoch); } @@ -477,7 +481,7 @@ QString QTimeZonePrivate::isoOffsetFormat(int offsetFromUtc) .arg(qAbs(mins) % 60, 2, 10, QLatin1Char('0')); } -QByteArray QTimeZonePrivate::olsenIdToWindowsId(const QByteArray &id) +QByteArray QTimeZonePrivate::ianaIdToWindowsId(const QByteArray &id) { for (int i = 0; i < zoneDataTableSize; ++i) { const QZoneData *data = zoneData(i); @@ -487,7 +491,7 @@ QByteArray QTimeZonePrivate::olsenIdToWindowsId(const QByteArray &id) return QByteArray(); } -QByteArray QTimeZonePrivate::windowsIdToDefaultOlsenId(const QByteArray &windowsId) +QByteArray QTimeZonePrivate::windowsIdToDefaultIanaId(const QByteArray &windowsId) { const quint16 windowsIdKey = toWindowsIdKey(windowsId); for (int i = 0; i < windowsDataTableSize; ++i) { @@ -498,17 +502,17 @@ QByteArray QTimeZonePrivate::windowsIdToDefaultOlsenId(const QByteArray &windows return QByteArray(); } -QByteArray QTimeZonePrivate::windowsIdToDefaultOlsenId(const QByteArray &windowsId, +QByteArray QTimeZonePrivate::windowsIdToDefaultIanaId(const QByteArray &windowsId, QLocale::Country country) { - const QList list = windowsIdToOlsenIds(windowsId, country); + const QList list = windowsIdToIanaIds(windowsId, country); if (list.count() > 0) return list.first(); else return QByteArray(); } -QList QTimeZonePrivate::windowsIdToOlsenIds(const QByteArray &windowsId) +QList QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId) { const quint16 windowsIdKey = toWindowsIdKey(windowsId); QList list; @@ -524,7 +528,7 @@ QList QTimeZonePrivate::windowsIdToOlsenIds(const QByteArray &window return list; } -QList QTimeZonePrivate::windowsIdToOlsenIds(const QByteArray &windowsId, +QList QTimeZonePrivate::windowsIdToIanaIds(const QByteArray &windowsId, QLocale::Country country) { const quint16 windowsIdKey = toWindowsIdKey(windowsId); diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index 9f99f49fcf..108aec2654 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -146,12 +146,12 @@ public: static bool isValidId(const QByteArray &olsenId); static QString isoOffsetFormat(int offsetFromUtc); - static QByteArray olsenIdToWindowsId(const QByteArray &olsenId); - static QByteArray windowsIdToDefaultOlsenId(const QByteArray &windowsId); - static QByteArray windowsIdToDefaultOlsenId(const QByteArray &windowsId, + static QByteArray ianaIdToWindowsId(const QByteArray &ianaId); + static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId); + static QByteArray windowsIdToDefaultIanaId(const QByteArray &windowsId, QLocale::Country country); - static QList windowsIdToOlsenIds(const QByteArray &windowsId); - static QList windowsIdToOlsenIds(const QByteArray &windowsId, + static QList windowsIdToIanaIds(const QByteArray &windowsId); + static QList windowsIdToIanaIds(const QByteArray &windowsId, QLocale::Country country); protected: @@ -309,6 +309,7 @@ private: bool operator==(const QTzTransitionRule &other) { return (stdOffset == other.stdOffset && dstOffset == other.dstOffset && abbreviationIndex == other.abbreviationIndex); } }; + Data dataForTzTransition(QTzTransitionTime tran) const; QList m_tranTimes; QList m_tranRules; QList m_abbreviations; diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 4bf19178fa..1fb6bb1b5a 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -149,11 +149,13 @@ static QTzHeader parseTzHeader(QDataStream &ds, bool *ok) if (memcmp(hdr.tzh_magic, TZ_MAGIC, 4) != 0 || ds.status() != QDataStream::Ok) return hdr; - // Parse Version, 1 byte, before 2005 was '\0', since 2005 a '2' + // Parse Version, 1 byte, before 2005 was '\0', since 2005 a '2', since 2013 a '3' ds >> ch; hdr.tzh_version = ch; - if (ds.status() != QDataStream::Ok || (hdr.tzh_version != '2' && hdr.tzh_version != '\0')) + if (ds.status() != QDataStream::Ok + || (hdr.tzh_version != '2' && hdr.tzh_version != '\0' && hdr.tzh_version != '3')) { return hdr; + } // Parse reserved space, 15 bytes ds.readRawData(hdr.tzh_reserved, 15); @@ -238,30 +240,31 @@ static QList parseTzTypes(QDataStream &ds, int tzh_typecnt) return typeList; } -static QMap parseTzAbbreviations(QDataStream &ds, int tzh_charcnt) +static QMap parseTzAbbreviations(QDataStream &ds, int tzh_charcnt, QList typeList) { // Parse the abbreviation list which is tzh_charcnt long with '\0' separated strings. The - // tz_abbrind index points to the first char of the abbreviation in the array, not the - // occurrence in the list. By parsing char at a time we can track the char index and convert - // to an occurrence index. By using a map with tz_abbrind as ordered key we get both index + // QTzType.tz_abbrind index points to the first char of the abbreviation in the array, not the + // occurrence in the list. It can also point to a partial string so we need to use the actual typeList + // index values when parsing. By using a map with tz_abbrind as ordered key we get both index // methods in one data structure and can convert the types afterwards. QMap map; quint8 ch; - QByteArray abbrev; - // Track the start position of each abbreviation - int tz_abbrind = 0; + QByteArray input; + // First parse the full abbrev string for (int i = 0; i < tzh_charcnt && ds.status() == QDataStream::Ok; ++i) { ds >> ch; - if (ds.status() == QDataStream::Ok) { - if (ch == '\0') { - // Have reached end of an abbreviation, so add to map - map[tz_abbrind] = abbrev; - tz_abbrind = i + 1; - abbrev.clear(); - } else { - abbrev.append((char)ch); - } - } + if (ds.status() == QDataStream::Ok) + input.append(char(ch)); + else + return map; + } + // Then extract all the substrings pointed to by typeList + foreach (const QTzType type, typeList) { + QByteArray abbrev; + for (int i = type.tz_abbrind; input.at(i) != '\0'; ++i) + abbrev.append(input.at(i)); + // Have reached end of an abbreviation, so add to map + map[type.tz_abbrind] = abbrev; } return map; } @@ -371,7 +374,7 @@ static QDate calculatePosixDate(const QByteArray dateRule, int year) } } -static QTime parsePosixTime(const QByteArray timeRule) +static QTime parsePosixTime(const QByteArray &timeRule) { // Format "HH:mm:ss", put check parts count just in case QList parts = timeRule.split(':'); @@ -385,7 +388,7 @@ static QTime parsePosixTime(const QByteArray timeRule) return QTime(2, 0, 0); } -static int parsePosixOffset(const QByteArray timeRule) +static int parsePosixOffset(const QByteArray &timeRule) { // Format "[+|-]hh[:mm[:ss]]" QList parts = timeRule.split(':'); @@ -399,7 +402,9 @@ static int parsePosixOffset(const QByteArray timeRule) return 0; } -static QList calculatePosixTransitions(const QByteArray &posixRule, int startYear, int endYear) +static QList calculatePosixTransitions(const QByteArray &posixRule, + int startYear, int endYear, + int lastTranMSecs) { QList list; @@ -443,11 +448,13 @@ static QList calculatePosixTransitions(const QByteArray // If only the name part then no transitions if (parts.count() == 1) { QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = 0; + data.atMSecsSinceEpoch = lastTranMSecs; data.offsetFromUtc = utcOffset; data.standardTimeOffset = utcOffset; data.daylightTimeOffset = 0; data.abbreviation = stdName; + list << data; + return list; } // If not populated the total dst offset is 1 hour @@ -572,7 +579,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) QList typeList = parseTzTypes(ds, hdr.tzh_typecnt); if (ds.status() != QDataStream::Ok) return; - QMap abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt); + QMap abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) return; parseTzLeapSeconds(ds, hdr.tzh_leapcnt, false); @@ -583,7 +590,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) return; // If version 2 then parse the second block of data - if (hdr.tzh_version == '2') { + if (hdr.tzh_version == '2' || hdr.tzh_version == '3') { ok = false; QTzHeader hdr2 = parseTzHeader(ds, &ok); if (!ok || ds.status() != QDataStream::Ok) @@ -594,7 +601,7 @@ void QTzTimeZonePrivate::init(const QByteArray &olsenId) typeList = parseTzTypes(ds, hdr2.tzh_typecnt); if (ds.status() != QDataStream::Ok) return; - abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt); + abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt, typeList); if (ds.status() != QDataStream::Ok) return; parseTzLeapSeconds(ds, hdr2.tzh_leapcnt, true); @@ -682,12 +689,14 @@ QString QTzTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch, if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files - return m_icu->displayName(atMSecsSinceEpoch, nameType, locale); + // TODO Some valid TZ names are not valid ICU names, use translation table? + if (m_icu->isValid()) + return m_icu->displayName(atMSecsSinceEpoch, nameType, locale); #else Q_UNUSED(nameType) Q_UNUSED(locale) - return abbreviation(atMSecsSinceEpoch); #endif // QT_USE_ICU + return abbreviation(atMSecsSinceEpoch); } QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, @@ -698,19 +707,59 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType, if (!m_icu) m_icu = new QIcuTimeZonePrivate(m_id); // TODO small risk may not match if tran times differ due to outdated files - return m_icu->displayName(timeType, nameType, locale); + // TODO Some valid TZ names are not valid ICU names, use translation table? + if (m_icu->isValid()) + return m_icu->displayName(timeType, nameType, locale); #else Q_UNUSED(timeType) Q_UNUSED(nameType) Q_UNUSED(locale) - const int atMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); - QTimeZonePrivate::Data tran = data(atMSecsSinceEpoch); - while ((timeType == QTimeZone::StandardTime && tran.daylightTimeOffset != 0) - || (timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset == 0)) { - tran = nextTransition(tran.atMSecsSinceEpoch); - } - return tran.abbreviation; #endif // QT_USE_ICU + // If no ICU available then have to use abbreviations instead + // Abbreviations don't have GenericTime + if (timeType == QTimeZone::GenericTime) + timeType = QTimeZone::StandardTime; + + // Get current tran, if valid and is what we want, then use it + const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch(); + QTimeZonePrivate::Data tran = data(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise get next tran and if valid and is what we want, then use it + tran = nextTransition(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise get prev tran and if valid and is what we want, then use it + tran = previousTransition(currentMSecs); + if (tran.atMSecsSinceEpoch != invalidMSecs()) + tran = previousTransition(tran.atMSecsSinceEpoch); + if (tran.atMSecsSinceEpoch != invalidMSecs() + && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) { + return tran.abbreviation; + } + + // Otherwise is strange sequence, so work backwards through trans looking for first match, if any + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch <= currentMSecs) { + tran = dataForTzTransition(m_tranTimes.at(i)); + if ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0) + || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0)) { + return tran.abbreviation; + } + } + } + + // Otherwise if no match use current data + return data(currentMSecs).abbreviation; } QString QTzTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const @@ -749,35 +798,55 @@ bool QTzTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const return (daylightTimeOffset(atMSecsSinceEpoch) != 0); } +QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime tran) const +{ + QTimeZonePrivate::Data data; + data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch; + QTzTransitionRule rule = m_tranRules.at(tran.ruleIndex); + data.standardTimeOffset = rule.stdOffset; + data.daylightTimeOffset = rule.dstOffset; + data.offsetFromUtc = rule.stdOffset + rule.dstOffset; + data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); + return data; +} + QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const { - QTimeZonePrivate::Data data = invalidData(); - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = lastTran; tran > 0; --tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch <= forMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch); - int year = dt.date().year(); - QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = posixTrans.size() - 1; i > 0; --i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch + &&!m_posixRule.isEmpty() && forMSecsSinceEpoch >= 0) { + const int year = QDateTime::fromMSecsSinceEpoch(forMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = posixTrans.size() - 1; i >= 0; --i) { if (posixTrans.at(i).atMSecsSinceEpoch <= forMSecsSinceEpoch) { + QTimeZonePrivate::Data data; data = posixTrans.at(i); data.atMSecsSinceEpoch = forMSecsSinceEpoch; return data; } } } - data.atMSecsSinceEpoch = forMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch <= forMSecsSinceEpoch) { + Data data = dataForTzTransition(m_tranTimes.at(i)); + data.atMSecsSinceEpoch = forMSecsSinceEpoch; + return data; + } + } + + // Otherwise use the earliest transition we have + if (m_tranTimes.size() > 0) { + Data data = dataForTzTransition(m_tranTimes.at(0)); + data.atMSecsSinceEpoch = forMSecsSinceEpoch; + return data; + } + + // Otherwise we have no rules, so probably an invalid tz, so return invalid data + return invalidData(); } bool QTzTimeZonePrivate::hasTransitions() const @@ -787,60 +856,54 @@ bool QTzTimeZonePrivate::hasTransitions() const QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSinceEpoch) const { - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = 0; tran < lastTran; ++tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch > afterMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(afterMSecsSinceEpoch); - int year = dt.date().year(); - QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = 0; i < posixTrans.size() - 1; ++i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch + &&!m_posixRule.isEmpty() && afterMSecsSinceEpoch >= 0) { + const int year = QDateTime::fromMSecsSinceEpoch(afterMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = 0; i < posixTrans.size(); ++i) { if (posixTrans.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) return posixTrans.at(i); } } - // Otherwise use the transition we found - QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = m_tranTimes.at(tran).atMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = 0; i < m_tranTimes.size(); ++i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch > afterMSecsSinceEpoch) { + return dataForTzTransition(m_tranTimes.at(i)); + } + } + + // Otherwise we have no rule, or there is no next transition, so return invalid data + return invalidData(); } QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const { - int lastTran = m_tranTimes.size() - 1; - int tran; - for (tran = lastTran; tran > 0; --tran) { - if (m_tranTimes.at(tran).atMSecsSinceEpoch < beforeMSecsSinceEpoch) - break; - } - // If after the last transition time then we need to use the posix rule if available - if (tran >= lastTran && !m_posixRule.isEmpty()) { - QDateTime dt = QDateTime::fromMSecsSinceEpoch(beforeMSecsSinceEpoch); - int year = dt.date().year(); - QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, year + 1); - for (int i = posixTrans.size() - 1; i > 0; --i) { + // If the required time is after the last transition and we have a POSIX rule then use it + if (m_tranTimes.size() > 0 && m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch + &&!m_posixRule.isEmpty() && beforeMSecsSinceEpoch > 0) { + const int year = QDateTime::fromMSecsSinceEpoch(beforeMSecsSinceEpoch, Qt::UTC).date().year(); + const int lastMSecs = (m_tranTimes.size() > 0) ? m_tranTimes.last().atMSecsSinceEpoch : 0; + QList posixTrans = calculatePosixTransitions(m_posixRule, year - 1, + year + 1, lastMSecs); + for (int i = posixTrans.size() - 1; i >= 0; --i) { if (posixTrans.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) return posixTrans.at(i); } } - // Otherwise use the transition we found - QTimeZonePrivate::Data data; - data.atMSecsSinceEpoch = m_tranTimes.at(tran).atMSecsSinceEpoch; - QTzTransitionRule rule = m_tranRules.at(m_tranTimes.at(tran).ruleIndex); - data.standardTimeOffset = rule.stdOffset; - data.daylightTimeOffset = rule.dstOffset; - data.offsetFromUtc = rule.stdOffset + rule.dstOffset; - data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex)); - return data; + + // Otherwise if we can find a valid tran then use its rule + for (int i = m_tranTimes.size() - 1; i >= 0; --i) { + if (m_tranTimes.at(i).atMSecsSinceEpoch < beforeMSecsSinceEpoch) { + return dataForTzTransition(m_tranTimes.at(i)); + } + } + + // Otherwise we have no rule, so return invalid data + return invalidData(); } // TODO Could cache the value and monitor the required files for any changes diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp index f411d35b3d..04588b2ba8 100644 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ b/src/corelib/tools/qtimezoneprivate_win.cpp @@ -305,7 +305,7 @@ static void calculateTransitionsForYear(const QWinTimeZonePrivate::QWinTransitio QDate daylightDate = calculateTransitionLocalDate(rule.daylightTimeRule, year); QTime daylightTime = QTime(rule.daylightTimeRule.wHour, rule.daylightTimeRule.wMinute, rule.daylightTimeRule.wSecond); - if (standardDate.isValid() && standardTime.isValid()) + if (daylightDate.isValid() && daylightTime.isValid()) *dstMSecs = timeToMSecs(daylightDate, daylightTime) + (rule.standardTimeBias * 60000); else *dstMSecs = QTimeZonePrivate::invalidMSecs(); @@ -362,7 +362,7 @@ void QWinTimeZonePrivate::init(const QByteArray &olsenId) m_windowsId = windowsSystemZoneId(); m_id = systemTimeZoneId(); } else { - m_windowsId = olsenIdToWindowsId(olsenId); + m_windowsId = ianaIdToWindowsId(olsenId); m_id = olsenId; } @@ -488,6 +488,9 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons do { // Convert the transition rules into msecs for the year we want to try rule = ruleForYear(year); + // If no transition rules to calculate then no DST, so just use rule for std + if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) + break; calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); if (stdMSecs < dstMSecs) { first = stdMSecs; @@ -543,6 +546,9 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::nextTransition(qint64 afterMSecsSinc do { // Convert the transition rules into msecs for the year we want to try rule = ruleForYear(year); + // If no transition rules to calculate then no next transition + if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) + return invalidData(); calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); // Find the first and second transition for the year if (stdMSecs < dstMSecs) { @@ -591,6 +597,9 @@ QTimeZonePrivate::Data QWinTimeZonePrivate::previousTransition(qint64 beforeMSec do { // Convert the transition rules into msecs for the year we want to try rule = ruleForYear(year); + // If no transition rules to calculate then no previous transition + if (rule.standardTimeRule.wMonth == 0 && rule.daylightTimeRule.wMonth == 0) + return invalidData(); calculateTransitionsForYear(rule, year, &stdMSecs, &dstMSecs); if (stdMSecs < dstMSecs) { first = stdMSecs; @@ -620,10 +629,10 @@ QByteArray QWinTimeZonePrivate::systemTimeZoneId() const QByteArray olsenId; // If we have a real country, then try get a specific match for that country if (country != QLocale::AnyCountry) - olsenId = windowsIdToDefaultOlsenId(windowsId, country); + olsenId = windowsIdToDefaultIanaId(windowsId, country); // If we don't have a real country, or there wasn't a specific match, try the global default if (olsenId.isEmpty()) { - olsenId = windowsIdToDefaultOlsenId(windowsId); + olsenId = windowsIdToDefaultIanaId(windowsId); // If no global default then probably an unknown Windows ID so return UTC if (olsenId.isEmpty()) return QByteArrayLiteral("UTC"); @@ -635,7 +644,7 @@ QSet QWinTimeZonePrivate::availableTimeZoneIds() const { QSet set; foreach (const QByteArray &winId, availableWindowsIds()) { - foreach (const QByteArray &olsenId, windowsIdToOlsenIds(winId)) + foreach (const QByteArray &olsenId, windowsIdToIanaIds(winId)) set << olsenId; } return set; diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 08a3b63ee4..26aaf931b3 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -364,6 +364,20 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const return 0; } +Qt::WindowState QPlatformIntegration::defaultWindowState(Qt::WindowFlags flags) const +{ + // Leave popup-windows as is + if (flags & Qt::Popup & ~Qt::Window) + return Qt::WindowNoState; + + if (styleHint(QPlatformIntegration::ShowIsFullScreen).toBool()) + return Qt::WindowFullScreen; + else if (styleHint(QPlatformIntegration::ShowIsMaximized).toBool()) + return Qt::WindowMaximized; + + return Qt::WindowNoState; +} + Qt::KeyboardModifiers QPlatformIntegration::queryKeyboardModifiers() const { return QGuiApplication::keyboardModifiers(); diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index d397270c10..580fc15233 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -152,6 +152,7 @@ public: }; virtual QVariant styleHint(StyleHint hint) const; + virtual Qt::WindowState defaultWindowState(Qt::WindowFlags) const; virtual Qt::KeyboardModifiers queryKeyboardModifiers() const; virtual QList possibleKeys(const QKeyEvent *) const; diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index 04ea9c27d5..68eb724454 100644 --- a/src/gui/kernel/qstylehints.cpp +++ b/src/gui/kernel/qstylehints.cpp @@ -176,6 +176,9 @@ int QStyleHints::cursorFlashTime() const Returns \c true if the platform defaults to windows being fullscreen, otherwise \c false. + \note The platform may still choose to show certain windows non-fullscreen, + such as popups or dialogs. This method only returns the default behavior. + \sa QWindow::show() */ bool QStyleHints::showIsFullScreen() const diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 4a40dd7e29..2e1d8f9976 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -1651,18 +1651,17 @@ QObject *QWindow::focusObject() const /*! Shows the window. - This equivalent to calling showFullScreen() or showNormal(), depending - on whether the platform defaults to windows being fullscreen or not, and - whether the window is a popup. + This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), + depending on the platform's default behavior for the window type and flags. - \sa showFullScreen(), showNormal(), hide(), QStyleHints::showIsFullScreen(), flags() + \sa showFullScreen(), showMaximized(), showNormal(), hide(), QStyleHints::showIsFullScreen(), flags() */ void QWindow::show() { - bool isPopup = d_func()->windowFlags & Qt::Popup & ~Qt::Window; - if (!isPopup && qApp->styleHints()->showIsFullScreen()) + Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(d_func()->windowFlags); + if (defaultState == Qt::WindowFullScreen) showFullScreen(); - else if (!isPopup && !(d_func()->windowFlags & Qt::Dialog & ~Qt::Window) && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ShowIsMaximized).toBool()) + else if (defaultState == Qt::WindowMaximized) showMaximized(); else showNormal(); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 4c02abdfd5..424ed554a2 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -146,17 +146,16 @@ static inline uint line_emulation(uint emulation) } #ifndef QT_NO_DEBUG -static bool qt_painter_thread_test(int devType, const char *what, bool extraCondition = false) +static bool qt_painter_thread_test(int devType, const char *what) { switch (devType) { case QInternal::Image: case QInternal::Printer: case QInternal::Picture: // can be drawn onto these devices safely from any thread - if (extraCondition) - break; + break; default: - if (!extraCondition && QThread::currentThread() != qApp->thread()) { + if (QThread::currentThread() != qApp->thread()) { qWarning("QPainter: It is not safe to use %s outside the GUI thread", what); return false; } diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp index 3ab4eedb26..3064e5d4e2 100644 --- a/src/plugins/platforms/android/src/androidjnimain.cpp +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -40,6 +40,7 @@ ** ****************************************************************************/ +#include #include #include @@ -691,7 +692,7 @@ static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state { m_activityActive = (state == Qt::ApplicationActive); - if (!m_androidPlatformIntegration) + if (!m_androidPlatformIntegration || !QGuiApplicationPrivate::platformIntegration()) return; QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp index 6d0ec306ab..ae3e257d3c 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.cpp @@ -242,6 +242,15 @@ QVariant QAndroidPlatformIntegration::styleHint(StyleHint hint) const } } +Qt::WindowState QAndroidPlatformIntegration::defaultWindowState(Qt::WindowFlags flags) const +{ + // Don't maximize dialogs on Android + if (flags & Qt::Dialog & ~Qt::Window) + return Qt::WindowNoState; + + return QPlatformIntegration::defaultWindowState(flags); +} + static const QLatin1String androidThemeName("android"); QStringList QAndroidPlatformIntegration::themeNames() const { diff --git a/src/plugins/platforms/android/src/qandroidplatformintegration.h b/src/plugins/platforms/android/src/qandroidplatformintegration.h index 3b34cdf7df..bd08ad694c 100644 --- a/src/plugins/platforms/android/src/qandroidplatformintegration.h +++ b/src/plugins/platforms/android/src/qandroidplatformintegration.h @@ -121,6 +121,7 @@ public: #endif QVariant styleHint(StyleHint hint) const; + Qt::WindowState defaultWindowState(Qt::WindowFlags flags) const Q_DECL_OVERRIDE; QStringList themeNames() const; QPlatformTheme *createPlatformTheme(const QString &name) const; diff --git a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp index 0ceac97e35..308bb70faf 100644 --- a/src/plugins/platforms/android/src/qandroidplatformtheme.cpp +++ b/src/plugins/platforms/android/src/qandroidplatformtheme.cpp @@ -125,7 +125,7 @@ const QFont *QAndroidPlatformTheme::font(Font type) const return &(it.value()); // default in case the style has not set a font - static QFont systemFont("Roboto", 12.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi + static QFont systemFont("Roboto", 14.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi if (type == QPlatformTheme::SystemFont) return &systemFont; return 0; diff --git a/src/plugins/platforms/cocoa/qcocoamenubar.mm b/src/plugins/platforms/cocoa/qcocoamenubar.mm index 0fea55ac68..5a8664747e 100644 --- a/src/plugins/platforms/cocoa/qcocoamenubar.mm +++ b/src/plugins/platforms/cocoa/qcocoamenubar.mm @@ -175,6 +175,7 @@ void QCocoaMenuBar::handleReparent(QWindow *newParentWindow) if (newParentWindow == NULL) { m_window = NULL; } else { + newParentWindow->create(); m_window = static_cast(newParentWindow->handle()); m_window->setMenubar(this); } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index c22f254aef..4da47f4f1f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -465,8 +465,10 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) { Qt::WindowType type = static_cast(int(flags & Qt::WindowType_Mask)); NSInteger styleMask = NSBorderlessWindowMask; + if (flags & Qt::FramelessWindowHint) + return styleMask; if ((type & Qt::Popup) == Qt::Popup) { - if (!windowIsPopupType(type) && !(flags & Qt::FramelessWindowHint)) + if (!windowIsPopupType(type)) styleMask = (NSUtilityWindowMask | NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask); } else { @@ -485,7 +487,7 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) } else { styleMask = NSResizableWindowMask | NSClosableWindowMask | NSTitledWindowMask; } - } else if (!(flags & Qt::FramelessWindowHint)) { + } else { if (flags & Qt::WindowMaximizeButtonHint) styleMask |= NSResizableWindowMask; if (flags & Qt::WindowTitleHint) diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index 775074baae..cf702c82af 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -58,6 +58,25 @@ self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; self.window.rootViewController = [[[QIOSViewController alloc] init] autorelease]; +#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_7_0) + QSysInfo::MacVersion iosVersion = QSysInfo::MacintoshVersion; + + // We prefer to keep the root viewcontroller in fullscreen layout, so that + // we don't have to compensate for the viewcontroller position. This also + // gives us the same behavior on iOS 5/6 as on iOS 7, where full screen layout + // is the only way. + if (iosVersion < QSysInfo::MV_IOS_7_0) + self.window.rootViewController.wantsFullScreenLayout = YES; + + // Use translucent statusbar by default on iOS6 iPhones (unless the user changed + // the default in the Info.plist), so that windows placed under the stausbar are + // still visible, just like on iOS7. + if (iosVersion >= QSysInfo::MV_IOS_6_0 && iosVersion < QSysInfo::MV_IOS_7_0 + && [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone + && [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault) + [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent]; +#endif + self.window.hidden = NO; return YES; diff --git a/src/plugins/platforms/ios/qiosbackingstore.mm b/src/plugins/platforms/ios/qiosbackingstore.mm index 2dadc5672b..5ea5fbd8d1 100644 --- a/src/plugins/platforms/ios/qiosbackingstore.mm +++ b/src/plugins/platforms/ios/qiosbackingstore.mm @@ -56,6 +56,9 @@ QIOSBackingStore::QIOSBackingStore(QWindow *window) fmt.setDepthBufferSize(16); fmt.setStencilBufferSize(8); + // Needed to prevent QOpenGLContext::makeCurrent() from failing + window->setSurfaceType(QSurface::OpenGLSurface); + m_context->setFormat(fmt); m_context->setScreen(window->screen()); m_context->create(); @@ -69,9 +72,6 @@ QIOSBackingStore::~QIOSBackingStore() void QIOSBackingStore::beginPaint(const QRegion &) { - // Needed to prevent QOpenGLContext::makeCurrent() from failing - window()->setSurfaceType(QSurface::OpenGLSurface); - m_context->makeCurrent(window()); QIOSWindow *iosWindow = static_cast(window()->handle()); @@ -102,6 +102,8 @@ void QIOSBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin // the child window overlaps a sibling window that's draws using a separate QOpenGLContext. return; } + + m_context->makeCurrent(window); m_context->swapBuffers(window); } @@ -115,7 +117,7 @@ void QIOSBackingStore::resize(const QSize &size, const QRegion &staticContents) // backing store and always keep the paint device's size in sync with the // window size in beginPaint(). - if (size != window()->size()) + if (size != window()->size() && !window()->inherits("QWidgetWindow")) qWarning() << "QIOSBackingStore needs to have the same size as its window"; } diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index 41b0d7f93a..1c76d29389 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -53,10 +53,11 @@ class QPlatformScreen; bool isQtApplication(); -CGRect toCGRect(const QRect &rect); -QRect fromCGRect(const CGRect &rect); -CGPoint toCGPoint(const QPoint &point); -QPoint fromCGPoint(const CGPoint &point); +CGRect toCGRect(const QRectF &rect); +QRectF fromCGRect(const CGRect &rect); +CGPoint toCGPoint(const QPointF &point); +QPointF fromCGPoint(const CGPoint &point); + Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation); UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation); QRect fromPortraitToPrimary(const QRect &rect, QPlatformScreen *screen); diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index be68e4d7d5..d749b8f514 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -58,24 +58,24 @@ bool isQtApplication() return isQt; } -CGRect toCGRect(const QRect &rect) +CGRect toCGRect(const QRectF &rect) { return CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()); } -QRect fromCGRect(const CGRect &rect) +QRectF fromCGRect(const CGRect &rect) { - return QRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); + return QRectF(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); } -CGPoint toCGPoint(const QPoint &point) +CGPoint toCGPoint(const QPointF &point) { return CGPointMake(point.x(), point.y()); } -QPoint fromCGPoint(const CGPoint &point) +QPointF fromCGPoint(const CGPoint &point) { - return QPoint(point.x, point.y); + return QPointF(point.x, point.y); } Qt::ScreenOrientation toQtScreenOrientation(UIDeviceOrientation uiDeviceOrientation) diff --git a/src/plugins/platforms/ios/qiosinputcontext.h b/src/plugins/platforms/ios/qiosinputcontext.h index 78c1b260e6..533ba686e1 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.h +++ b/src/plugins/platforms/ios/qiosinputcontext.h @@ -44,6 +44,7 @@ #include +#include #include QT_BEGIN_NAMESPACE @@ -60,13 +61,17 @@ public: void showInputPanel(); void hideInputPanel(); bool isInputPanelVisible() const; + void setFocusObject(QObject *object); - void focusViewChanged(UIView *view); + void focusWindowChanged(QWindow *focusWindow); + void scrollRootView(); private: QIOSKeyboardListener *m_keyboardListener; - UIView *m_focusView; + UIView *m_focusView; + QTransform m_inputItemTransform; bool m_hasPendingHideRequest; + bool m_inSetFocusObject; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm index d430589037..0e43429015 100644 --- a/src/plugins/platforms/ios/qiosinputcontext.mm +++ b/src/plugins/platforms/ios/qiosinputcontext.mm @@ -48,7 +48,12 @@ @public QIOSInputContext *m_context; BOOL m_keyboardVisible; + BOOL m_keyboardVisibleAndDocked; QRectF m_keyboardRect; + QRectF m_keyboardEndRect; + NSTimeInterval m_duration; + UIViewAnimationCurve m_curve; + UIViewController *m_viewController; } @end @@ -60,8 +65,30 @@ if (self) { m_context = context; m_keyboardVisible = NO; - // After the keyboard became undockable (iOS5), UIKeyboardWillShow/UIKeyboardWillHide - // no longer works for all cases. So listen to keyboard frame changes instead: + m_keyboardVisibleAndDocked = NO; + m_duration = 0; + m_curve = UIViewAnimationCurveEaseOut; + m_viewController = 0; + + if (isQtApplication()) { + // Get the root view controller that is on the same screen as the keyboard: + for (UIWindow *uiWindow in [[UIApplication sharedApplication] windows]) { + if (uiWindow.screen == [UIScreen mainScreen]) { + m_viewController = [uiWindow.rootViewController retain]; + break; + } + } + Q_ASSERT(m_viewController); + } + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardWillShow:) + name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(keyboardWillHide:) + name:@"UIKeyboardWillHideNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidChangeFrame:) @@ -72,25 +99,68 @@ - (void) dealloc { + [m_viewController release]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIKeyboardWillShowNotification" object:nil]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:@"UIKeyboardWillHideNotification" object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UIKeyboardDidChangeFrameNotification" object:nil]; [super dealloc]; } +- (QRectF) getKeyboardRect:(NSNotification *)notification +{ + // For Qt applications we rotate the keyboard rect to align with the screen + // orientation (which is the interface orientation of the root view controller). + // For hybrid apps we follow native behavior, and return the rect unmodified: + CGRect keyboardFrame = [[notification userInfo][UIKeyboardFrameEndUserInfoKey] CGRectValue]; + if (isQtApplication()) { + UIView *view = m_viewController.view; + return fromCGRect(CGRectOffset([view convertRect:keyboardFrame fromView:view.window], 0, -view.bounds.origin.y)); + } else { + return fromCGRect(keyboardFrame); + } +} + - (void) keyboardDidChangeFrame:(NSNotification *)notification { - CGRect frame; - [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&frame]; - - m_keyboardRect = fromPortraitToPrimary(fromCGRect(frame), QGuiApplication::primaryScreen()->handle()); + m_keyboardRect = [self getKeyboardRect:notification]; m_context->emitKeyboardRectChanged(); - BOOL visible = CGRectIntersectsRect(frame, [UIScreen mainScreen].bounds); + BOOL visible = m_keyboardRect.intersects(fromCGRect([UIScreen mainScreen].bounds)); if (m_keyboardVisible != visible) { m_keyboardVisible = visible; m_context->emitInputPanelVisibleChanged(); } + + // If the keyboard was visible and docked from before, this is just a geometry + // change (normally caused by an orientation change). In that case, update scroll: + if (m_keyboardVisibleAndDocked) + m_context->scrollRootView(); +} + +- (void) keyboardWillShow:(NSNotification *)notification +{ + // Note that UIKeyboardWillShowNotification is only sendt when the keyboard is docked. + m_keyboardVisibleAndDocked = YES; + m_keyboardEndRect = [self getKeyboardRect:notification]; + if (!m_duration) { + m_duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + m_curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue] << 16; + } + m_context->scrollRootView(); +} + +- (void) keyboardWillHide:(NSNotification *)notification +{ + // Note that UIKeyboardWillHideNotification is also sendt when the keyboard is undocked. + m_keyboardVisibleAndDocked = NO; + m_keyboardEndRect = [self getKeyboardRect:notification]; + m_context->scrollRootView(); } @end @@ -100,7 +170,11 @@ QIOSInputContext::QIOSInputContext() , m_keyboardListener([[QIOSKeyboardListener alloc] initWithQIOSInputContext:this]) , m_focusView(0) , m_hasPendingHideRequest(false) + , m_inSetFocusObject(false) { + if (isQtApplication()) + connect(qGuiApp->inputMethod(), &QInputMethod::cursorRectangleChanged, this, &QIOSInputContext::scrollRootView); + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSInputContext::focusWindowChanged); } QIOSInputContext::~QIOSInputContext() @@ -142,10 +216,76 @@ bool QIOSInputContext::isInputPanelVisible() const return m_keyboardListener->m_keyboardVisible; } -void QIOSInputContext::focusViewChanged(UIView *view) +void QIOSInputContext::setFocusObject(QObject *) { + m_inputItemTransform = qApp->inputMethod()->inputItemTransform(); + + if (!m_focusView || !m_focusView.isFirstResponder) + return; + + // Since m_focusView is the first responder, it means that the keyboard is open and we + // should update keyboard layout. But there seem to be no way to tell it to reread the + // UITextInputTraits from m_focusView. To work around that, we quickly resign first + // responder status just to reassign it again. To not remove the focusObject in the same + // go, we need to call the super implementation of resignFirstResponder. Since the call + // will cause a 'keyboardWillHide' notification to be sendt, we also block scrollRootView + // to avoid artifacts: + m_inSetFocusObject = true; + SEL sel = @selector(resignFirstResponder); + [[m_focusView superclass] instanceMethodForSelector:sel](m_focusView, sel); + m_inSetFocusObject = false; + [m_focusView becomeFirstResponder]; +} + +void QIOSInputContext::focusWindowChanged(QWindow *focusWindow) +{ + UIView *view = reinterpret_cast *>(focusWindow->handle()->winId()); if ([m_focusView isFirstResponder]) [view becomeFirstResponder]; [m_focusView release]; m_focusView = [view retain]; } + +void QIOSInputContext::scrollRootView() +{ + // Scroll the root view (screen) if: + // - our backend controls the root view controller on the main screen (no hybrid app) + // - the focus object is on the same screen as the keyboard. + // - the first responder is a QUIView, and not some other foreign UIView. + // - the keyboard is docked. Otherwise the user can move the keyboard instead. + // - the inputItem has not been moved/scrolled + if (!isQtApplication() || !m_focusView || m_inSetFocusObject) + return; + + if (m_inputItemTransform != qApp->inputMethod()->inputItemTransform()) { + // The inputItem has moved since the last scroll update. To avoid competing + // with the application where the cursor/inputItem should be, we bail: + return; + } + + UIView *view = m_keyboardListener->m_viewController.view; + qreal scrollTo = 0; + + if (m_focusView.isFirstResponder + && m_keyboardListener->m_keyboardVisibleAndDocked + && m_focusView.window == view.window) { + QRectF cursorRect = qGuiApp->inputMethod()->cursorRectangle(); + cursorRect.translate(qGuiApp->focusWindow()->geometry().topLeft()); + qreal keyboardY = m_keyboardListener->m_keyboardEndRect.y(); + int statusBarY = qGuiApp->primaryScreen()->availableGeometry().y(); + const int margin = 20; + + if (cursorRect.bottomLeft().y() > keyboardY - margin) + scrollTo = qMin(view.bounds.size.height - keyboardY, cursorRect.y() - statusBarY - margin); + } + + if (scrollTo != view.bounds.origin.y) { + // Scroll the view the same way a UIScrollView works: by changing bounds.origin: + CGRect newBounds = view.bounds; + newBounds.origin.y = scrollTo; + [UIView animateWithDuration:m_keyboardListener->m_duration delay:0 + options:m_keyboardListener->m_curve + animations:^{ view.bounds = newBounds; } + completion:0]; + } +} diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 44ac749454..5c8f67bda2 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -160,7 +160,7 @@ QPlatformServices *QIOSIntegration::services() const QVariant QIOSIntegration::styleHint(StyleHint hint) const { switch (hint) { - case ShowIsFullScreen: + case ShowIsMaximized: return true; case SetFocusOnTouchRelease: return true; diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index e70ff4b1a9..173bd11719 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -50,8 +50,10 @@ QT_BEGIN_NAMESPACE -class QIOSScreen : public QPlatformScreen +class QIOSScreen : public QObject, public QPlatformScreen { + Q_OBJECT + public: QIOSScreen(unsigned int screenIndex); ~QIOSScreen(); @@ -73,6 +75,10 @@ public: UIScreen *uiScreen() const; void updateProperties(); + void layoutWindows(); + +public slots: + void updateStatusBarVisibility(); private: UIScreen *m_uiScreen; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index de6585fd19..57522cb1a3 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -139,6 +139,8 @@ QIOSScreen::QIOSScreen(unsigned int screenIndex) m_unscaledDpi = 163; // Regular iPhone DPI } + connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &QIOSScreen::updateStatusBarVisibility); + updateProperties(); } @@ -156,7 +158,7 @@ void QIOSScreen::updateProperties() } bool inPortrait = UIInterfaceOrientationIsPortrait(uiWindow.rootViewController.interfaceOrientation); - QRect geometry = inPortrait ? fromCGRect(m_uiScreen.bounds) + QRect geometry = inPortrait ? fromCGRect(m_uiScreen.bounds).toRect() : QRect(m_uiScreen.bounds.origin.x, m_uiScreen.bounds.origin.y, m_uiScreen.bounds.size.height, m_uiScreen.bounds.size.width); @@ -182,7 +184,66 @@ void QIOSScreen::updateProperties() } if (screen()) - resizeMaximizedWindows(); + layoutWindows(); +} + +void QIOSScreen::updateStatusBarVisibility() +{ + QWindow *focusWindow = QGuiApplication::focusWindow(); + + // If we don't have a focus window we leave the status + // bar as is, so that the user can activate a new window + // with the same window state without the status bar jumping + // back and forth. + if (!focusWindow) + return; + + UIView *view = reinterpret_cast(focusWindow->handle()->winId()); +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_IOS_7_0) { + [view.viewController setNeedsStatusBarAppearanceUpdate]; + } else +#endif + { + bool wasHidden = [UIApplication sharedApplication].statusBarHidden; + QIOSViewController *viewController = static_cast(view.viewController); + [[UIApplication sharedApplication] + setStatusBarHidden:[viewController prefersStatusBarHidden] + withAnimation:UIStatusBarAnimationNone]; + + if ([UIApplication sharedApplication].statusBarHidden != wasHidden) + updateProperties(); + } +} + +void QIOSScreen::layoutWindows() +{ + QList windows = QGuiApplication::topLevelWindows(); + + const QRect oldGeometry = screen()->geometry(); + const QRect oldAvailableGeometry = screen()->availableGeometry(); + const QRect newGeometry = geometry(); + const QRect newAvailableGeometry = availableGeometry(); + + for (int i = 0; i < windows.size(); ++i) { + QWindow *window = windows.at(i); + + if (platformScreenForWindow(window) != this) + continue; + + QIOSWindow *platformWindow = static_cast(window->handle()); + if (!platformWindow) + continue; + + // FIXME: Handle more complex cases of no-state and/or child windows when rotating + + if (window->windowState() & Qt::WindowFullScreen + || (window->windowState() & Qt::WindowNoState && window->geometry() == oldGeometry)) + platformWindow->applyGeometry(newGeometry); + else if (window->windowState() & Qt::WindowMaximized + || (window->windowState() & Qt::WindowNoState && window->geometry() == oldAvailableGeometry)) + platformWindow->applyGeometry(newAvailableGeometry); + } } QRect QIOSScreen::geometry() const @@ -247,4 +308,6 @@ UIScreen *QIOSScreen::uiScreen() const return m_uiScreen; } +#include "moc_qiosscreen.cpp" + QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index d5a61cb3f4..a0017808d3 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -42,5 +42,6 @@ #import @interface QIOSViewController : UIViewController +- (BOOL)prefersStatusBarHidden; @end diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 1d5e69beac..2e7e44d32c 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -42,9 +42,11 @@ #import "qiosviewcontroller.h" #include +#include #include #include "qiosscreen.h" #include "qiosglobal.h" +#include "qioswindow.h" @implementation QIOSViewController @@ -55,12 +57,22 @@ return YES; } +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_6_0) -(NSUInteger)supportedInterfaceOrientations { // We need to tell iOS that we support all orientations in order to set // status bar orientation when application content orientation changes. return UIInterfaceOrientationMaskAll; } +#endif + +#if QT_IOS_DEPLOYMENT_TARGET_BELOW(__IPHONE_6_0) +-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation +{ + Q_UNUSED(interfaceOrientation); + return YES; +} +#endif - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { @@ -74,5 +86,28 @@ qiosScreen->updateProperties(); } +#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_7_0) +- (UIStatusBarStyle)preferredStatusBarStyle +{ + // Since we don't place anything behind the status bare by default, we + // end up with a black area, so we have to enable the white text mode + // of the iOS7 statusbar. + return UIStatusBarStyleLightContent; + + // FIXME: Try to detect the content underneath the statusbar and choose + // an appropriate style, and/or expose Qt APIs to control the style. +} +#endif + +- (BOOL)prefersStatusBarHidden +{ + QWindow *focusWindow = QGuiApplication::focusWindow(); + if (!focusWindow) + return [UIApplication sharedApplication].statusBarHidden; + + QIOSWindow *topLevel = static_cast(focusWindow->handle())->topLevelWindow(); + return topLevel->window()->windowState() == Qt::WindowFullScreen; +} + @end diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 6e8683af00..a5e122bda1 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -85,7 +85,11 @@ public: WId winId() const { return WId(m_view); }; + QIOSWindow *topLevelWindow() const; + private: + void applyGeometry(const QRect &rect); + QUIView *m_view; QRect m_normalGeometry; @@ -97,6 +101,8 @@ private: inline Qt::WindowType windowType() { return static_cast(int(window()->flags() & Qt::WindowType_Mask)); } inline bool windowIsPopup() { return windowType() & Qt::Popup & ~Qt::Window; } + + friend class QIOSScreen; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 2413a45e11..0dd810bdf6 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -105,18 +105,10 @@ CAEAGLLayer *eaglLayer = static_cast(self.layer); eaglLayer.opaque = TRUE; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, + [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; - // Set up text input - autocapitalizationType = UITextAutocapitalizationTypeNone; - autocorrectionType = UITextAutocorrectionTypeNo; - enablesReturnKeyAutomatically = NO; - keyboardAppearance = UIKeyboardAppearanceDefault; - keyboardType = UIKeyboardTypeDefault; - returnKeyType = UIReturnKeyDone; - secureTextEntry = NO; - m_nextTouchId = 0; + [self updateTextInputTraits]; if (isQtApplication()) self.hidden = YES; @@ -153,6 +145,15 @@ self.clipsToBounds = NO; } +- (void)setNeedsDisplay +{ + [super setNeedsDisplay]; + + // We didn't implement drawRect: so we have to manually + // mark the layer as needing display. + [self.layer setNeedsDisplay]; +} + - (void)layoutSubviews { // This method is the de facto way to know that view has been resized, @@ -165,15 +166,53 @@ qWarning() << m_qioswindow->window() << "is backed by a UIView that has a transform set. This is not supported."; - QRect geometry = fromCGRect(self.frame); - m_qioswindow->QPlatformWindow::setGeometry(geometry); - QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), geometry); - QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), QRect(QPoint(), geometry.size())); + // The original geometry requested by setGeometry() might be different + // from what we end up with after applying window constraints. + QRect requestedGeometry = m_qioswindow->geometry(); - // If we have a new size here we need to resize the FBO's corresponding buffers, - // but we defer that to when the application calls makeCurrent. + QRect actualGeometry; + if (m_qioswindow->window()->isTopLevel()) { + UIWindow *uiWindow = self.window; + UIView *rootView = uiWindow.rootViewController.view; + CGRect rootViewPositionInRelationToRootViewController = + [rootView convertRect:uiWindow.bounds fromView:uiWindow]; - [super layoutSubviews]; + actualGeometry = fromCGRect(CGRectOffset([self.superview convertRect:self.frame toView:rootView], + -rootViewPositionInRelationToRootViewController.origin.x, + -rootViewPositionInRelationToRootViewController.origin.y + + rootView.bounds.origin.y)).toRect(); + } else { + actualGeometry = fromCGRect(self.frame).toRect(); + } + + // Persist the actual/new geometry so that QWindow::geometry() can + // be queried on the resize event. + m_qioswindow->QPlatformWindow::setGeometry(actualGeometry); + + QRect previousGeometry = requestedGeometry != actualGeometry ? + requestedGeometry : qt_window_private(m_qioswindow->window())->geometry; + + QWindowSystemInterface::handleGeometryChange(m_qioswindow->window(), actualGeometry, previousGeometry); + QWindowSystemInterface::flushWindowSystemEvents(); + + if (actualGeometry.size() != previousGeometry.size()) { + // Trigger expose event on resize + [self setNeedsDisplay]; + + // A new size means we also need to resize the FBO's corresponding buffers, + // but we defer that to when the application calls makeCurrent. + } +} + +- (void)displayLayer:(CALayer *)layer +{ + QRect geometry = fromCGRect(layer.frame).toRect(); + Q_ASSERT(m_qioswindow->geometry() == geometry); + Q_ASSERT(self.hidden == !m_qioswindow->window()->isVisible()); + + QRegion region = self.hidden ? QRegion() : QRect(QPoint(), geometry.size()); + QWindowSystemInterface::handleExposeEvent(m_qioswindow->window(), region); + QWindowSystemInterface::flushWindowSystemEvents(); } - (void)updateTouchList:(NSSet *)touches withState:(Qt::TouchPointState)state @@ -195,7 +234,7 @@ } else { touchPoint.state = state; touchPoint.pressure = (state == Qt::TouchPointReleased) ? 0.0 : 1.0; - QPoint touchPos = fromCGPoint([uiTouch locationInView:rootView]); + QPoint touchPos = fromCGPoint([uiTouch locationInView:rootView]).toPoint(); touchPoint.area = QRectF(touchPos, QSize(0, 0)); touchPoint.normalPosition = QPointF(touchPos.x() / rootViewSize.width, touchPos.y() / rootViewSize.height); } @@ -296,6 +335,7 @@ // user cannot type. And since the keyboard will open when a view becomes // the first responder, it's now a good time to inform QPA that the QWindow // this view backs became active: + [self updateTextInputTraits]; QWindowSystemInterface::handleWindowActivated(m_qioswindow->window()); return [super becomeFirstResponder]; } @@ -318,8 +358,11 @@ { QString string = QString::fromUtf8([text UTF8String]); int key = 0; - if ([text isEqualToString:@"\n"]) + if ([text isEqualToString:@"\n"]) { key = (int)Qt::Key_Return; + if (self.returnKeyType == UIReturnKeyDone) + [self resignFirstResponder]; + } // Send key event to window system interface QWindowSystemInterface::handleKeyEvent( @@ -337,6 +380,47 @@ 0, QEvent::KeyRelease, (int)Qt::Key_Backspace, Qt::NoModifier); } +- (void)updateTextInputTraits +{ + // Ask the current focus object what kind of input it + // expects, and configure the keyboard appropriately: + QObject *focusObject = QGuiApplication::focusObject(); + if (!focusObject) + return; + QInputMethodQueryEvent queryEvent(Qt::ImEnabled | Qt::ImHints); + if (!QCoreApplication::sendEvent(focusObject, &queryEvent)) + return; + if (!queryEvent.value(Qt::ImEnabled).toBool()) + return; + + Qt::InputMethodHints hints = static_cast(queryEvent.value(Qt::ImHints).toUInt()); + + self.returnKeyType = (hints & Qt::ImhMultiLine) ? UIReturnKeyDefault : UIReturnKeyDone; + self.secureTextEntry = BOOL(hints & Qt::ImhHiddenText); + self.autocorrectionType = (hints & Qt::ImhNoPredictiveText) ? + UITextAutocorrectionTypeNo : UITextAutocorrectionTypeDefault; + + if (hints & Qt::ImhUppercaseOnly) + self.autocapitalizationType = UITextAutocapitalizationTypeAllCharacters; + else if (hints & Qt::ImhNoAutoUppercase) + self.autocapitalizationType = UITextAutocapitalizationTypeNone; + else + self.autocapitalizationType = UITextAutocapitalizationTypeSentences; + + if (hints & Qt::ImhUrlCharactersOnly) + self.keyboardType = UIKeyboardTypeURL; + else if (hints & Qt::ImhEmailCharactersOnly) + self.keyboardType = UIKeyboardTypeEmailAddress; + else if (hints & Qt::ImhDigitsOnly) + self.keyboardType = UIKeyboardTypeNumberPad; + else if (hints & Qt::ImhFormattedNumbersOnly) + self.keyboardType = UIKeyboardTypeDecimalPad; + else if (hints & Qt::ImhDialableCharactersOnly) + self.keyboardType = UIKeyboardTypeNumberPad; + else + self.keyboardType = UIKeyboardTypeDefault; +} + @end @implementation UIView (QIOS) @@ -393,8 +477,8 @@ bool QIOSWindow::blockedByModal() void QIOSWindow::setVisible(bool visible) { - QPlatformWindow::setVisible(visible); m_view.hidden = !visible; + [m_view setNeedsDisplay]; if (!isQtApplication()) return; @@ -412,6 +496,10 @@ void QIOSWindow::setVisible(bool visible) if (visible) { requestActivateWindow(); + + if (window()->isTopLevel()) + static_cast(screen())->updateStatusBarVisibility(); + } else { // Activate top-most visible QWindow: NSArray *subviews = m_view.viewController.view.subviews; @@ -429,40 +517,90 @@ void QIOSWindow::setVisible(bool visible) void QIOSWindow::setGeometry(const QRect &rect) { - // If the window is in fullscreen, just bookkeep the requested - // geometry in case the window goes into Qt::WindowNoState later: m_normalGeometry = rect; - if (window()->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) - return; - // Since we don't support transformations on the UIView, we can set the frame - // directly and let UIKit deal with translating that into bounds and center. - // Changing the size of the view will end up in a call to -[QUIView layoutSubviews] - // which will update QWindowSystemInterface with the new size. - m_view.frame = toCGRect(rect); + if (window()->windowState() != Qt::WindowNoState) { + QPlatformWindow::setGeometry(rect); + + // The layout will realize the requested geometry was not applied, and + // send geometry-change events that match the actual geometry. + [m_view setNeedsLayout]; + + if (window()->inherits("QWidgetWindow")) { + // QWidget wrongly assumes that setGeometry resets the window + // state back to Qt::NoWindowState, so we need to inform it that + // that his is not the case by re-issuing the current window state. + QWindowSystemInterface::handleWindowStateChanged(window(), window()->windowState()); + + // It also needs to be told immediately that the geometry it requested + // did not apply, otherwise it will continue on as if it did, instead + // of waiting for a resize event. + [m_view layoutIfNeeded]; + } + + return; + } + + applyGeometry(rect); +} + +void QIOSWindow::applyGeometry(const QRect &rect) +{ + // Geometry changes are asynchronous, but QWindow::geometry() is + // expected to report back the 'requested geometry' until we get + // a callback with the updated geometry from the window system. + // The baseclass takes care of persisting this for us. + QPlatformWindow::setGeometry(rect); + + if (window()->isTopLevel()) { + // The QWindow is in QScreen coordinates, which maps to a possibly rotated root-view-controller. + // Since the root-view-controller might be translated in relation to the UIWindow, we need to + // check specifically for that and compensate. Also check if the root view has been scrolled + // as a result of the keyboard being open. + UIWindow *uiWindow = m_view.window; + UIView *rootView = uiWindow.rootViewController.view; + CGRect rootViewPositionInRelationToRootViewController = + [rootView convertRect:uiWindow.bounds fromView:uiWindow]; + + m_view.frame = CGRectOffset([m_view.superview convertRect:toCGRect(rect) fromView:rootView], + rootViewPositionInRelationToRootViewController.origin.x, + rootViewPositionInRelationToRootViewController.origin.y + + rootView.bounds.origin.y); + } else { + // Easy, in parent's coordinates + m_view.frame = toCGRect(rect); + } + + // iOS will automatically trigger -[layoutSubviews:] for resize, + // but not for move, so we force it just in case. + [m_view setNeedsLayout]; + + if (window()->inherits("QWidgetWindow")) + [m_view layoutIfNeeded]; } void QIOSWindow::setWindowState(Qt::WindowState state) { - // FIXME: Figure out where or how we should disable/enable the statusbar. - // Perhaps setting QWindow to maximized should also mean that we'll show - // the statusbar, and vice versa for fullscreen? + // Update the QWindow representation straight away, so that + // we can update the statusbar visibility based on the new + // state before applying geometry changes. + qt_window_private(window())->windowState = state; - if (state != Qt::WindowNoState) - m_normalGeometry = geometry(); + if (window()->isTopLevel() && window()->isVisible() && window()->isActive()) + static_cast(screen())->updateStatusBarVisibility(); switch (state) { case Qt::WindowNoState: - setGeometry(m_normalGeometry); + applyGeometry(m_normalGeometry); break; case Qt::WindowMaximized: - setGeometry(screen()->availableGeometry()); + applyGeometry(screen()->availableGeometry()); break; case Qt::WindowFullScreen: - setGeometry(screen()->geometry()); + applyGeometry(screen()->geometry()); break; case Qt::WindowMinimized: - setGeometry(QRect()); + applyGeometry(QRect()); break; case Qt::WindowActive: Q_UNREACHABLE(); @@ -486,6 +624,23 @@ void QIOSWindow::setParent(const QPlatformWindow *parentWindow) } } +QIOSWindow *QIOSWindow::topLevelWindow() const +{ + QWindow *window = this->window(); + while (window) { + QWindow *parent = window->parent(); + if (!parent) + parent = window->transientParent(); + + if (!parent) + break; + + window = parent; + } + + return static_cast(window->handle()); +} + void QIOSWindow::requestActivateWindow() { // Note that several windows can be active at the same time if they exist in the same @@ -499,8 +654,6 @@ void QIOSWindow::requestActivateWindow() if (window()->isTopLevel()) raise(); - QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); - static_cast(context)->focusViewChanged(m_view); QWindowSystemInterface::handleWindowActivated(window()); } diff --git a/src/plugins/platforms/qnx/qnx.pro b/src/plugins/platforms/qnx/qnx.pro index aeacbeef69..bc7219de5c 100644 --- a/src/plugins/platforms/qnx/qnx.pro +++ b/src/plugins/platforms/qnx/qnx.pro @@ -2,9 +2,6 @@ TARGET = qqnx QT += platformsupport-private core-private gui-private -# The PPS based platform integration is currently used for both BB10 and plain QNX -CONFIG += qqnx_pps - # Uncomment this to build with support for IMF once it becomes available in the BBNDK #CONFIG += qqnx_imf @@ -132,7 +129,8 @@ CONFIG(qqnx_pps) { qqnxclipboard.h \ qqnxbuttoneventnotifier.h - LIBS += -lpps -lclipboard + LIBS += -lpps + !contains(DEFINES, QT_NO_CLIPBOARD): LIBS += -lclipboard CONFIG(qqnx_imf) { DEFINES += QQNX_IMF diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp index 6afc3cad21..b57227a60b 100644 --- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp @@ -58,12 +58,13 @@ QT_BEGIN_NAMESPACE QQnxEglWindow::QQnxEglWindow(QWindow *window, screen_context_t context, bool needRootWindow) : QQnxWindow(window, context, needRootWindow), - m_requestedBufferSize(window->geometry().size()), m_platformOpenGLContext(0), m_newSurfaceRequested(true), m_eglSurface(EGL_NO_SURFACE) { initWindow(); + m_requestedBufferSize = screen()->rootWindow() == this ? + screen()->geometry().size() : window->geometry().size(); } QQnxEglWindow::~QQnxEglWindow() @@ -145,6 +146,9 @@ EGLSurface QQnxEglWindow::getSurface() void QQnxEglWindow::setGeometry(const QRect &rect) { + //If this is the root window, it has to be shown fullscreen + const QRect &newGeometry = screen()->rootWindow() == this ? screen()->geometry() : rect; + //We need to request that the GL context updates // the EGLsurface on which it is rendering. { @@ -152,11 +156,11 @@ void QQnxEglWindow::setGeometry(const QRect &rect) // setting m_requestedBufferSize and therefore extended the scope to include // that test. const QMutexLocker locker(&m_mutex); - m_requestedBufferSize = rect.size(); - if (m_platformOpenGLContext != 0 && bufferSize() != rect.size()) + m_requestedBufferSize = newGeometry.size(); + if (m_platformOpenGLContext != 0 && bufferSize() != newGeometry.size()) m_newSurfaceRequested.testAndSetRelease(false, true); } - QQnxWindow::setGeometry(rect); + QQnxWindow::setGeometry(newGeometry); } QSize QQnxEglWindow::requestedBufferSize() const diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp index 1e58d482d4..b25c0b5b29 100644 --- a/src/plugins/platforms/qnx/qqnxwindow.cpp +++ b/src/plugins/platforms/qnx/qqnxwindow.cpp @@ -136,7 +136,7 @@ void QQnxWindow::setGeometry(const QRect &rect) // Calling flushWindowSystemEvents() here would flush input events which // could result in re-entering QQnxWindow::setGeometry() again. - QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); //This does not work + QWindowSystemInterface::setSynchronousWindowsSystemEvents(true); QWindowSystemInterface::handleGeometryChange(window(), newGeometry); QWindowSystemInterface::handleExposeEvent(window(), newGeometry); QWindowSystemInterface::setSynchronousWindowsSystemEvents(false); @@ -608,13 +608,15 @@ void QQnxWindow::initWindow() setWindowState(window()->windowState()); if (window()->parent() && window()->parent()->handle()) setParent(window()->parent()->handle()); - setGeometryHelper(window()->geometry()); + if (screen()->rootWindow() == this) { - setGeometry(screen()->geometry()); + setGeometryHelper(screen()->geometry()); + QWindowSystemInterface::handleGeometryChange(window(), screen()->geometry()); + } else { + setGeometryHelper(window()->geometry()); } } - void QQnxWindow::createWindowGroup() { // Generate a random window group name diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index e2594207fe..2743ef029d 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -86,6 +86,10 @@ QWindowsKeyMapper::~QWindowsKeyMapper() #define VK_OEM_3 0xC0 #endif +// We not only need the scancode itself but also the extended bit of key messages. Thus we need +// the additional bit when masking the scancode. +enum { scancodeBitmask = 0x1ff }; + // Key recorder ------------------------------------------------------------------------[ start ] -- struct KeyRecord { KeyRecord(int c, int a, int s, const QString &t) : code(c), ascii(a), state(s), text(t) {} @@ -567,7 +571,7 @@ void QWindowsKeyMapper::updateKeyMap(const MSG &msg) { unsigned char kbdBuffer[256]; // Will hold the complete keyboard state GetKeyboardState(kbdBuffer); - const quint32 scancode = (msg.lParam >> 16) & 0xff; + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; updatePossibleKeyCodes(kbdBuffer, scancode, msg.wParam); } @@ -754,7 +758,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, const MSG &ms { const int msgType = msg.message; - const quint32 scancode = (msg.lParam >> 16) & 0xff; + const quint32 scancode = (msg.lParam >> 16) & scancodeBitmask; const quint32 vk_key = msg.wParam; quint32 nModifiers = 0; diff --git a/src/plugins/platforms/xcb/qglxintegration.cpp b/src/plugins/platforms/xcb/qglxintegration.cpp index e6fa8fc898..e504d93fba 100644 --- a/src/plugins/platforms/xcb/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/qglxintegration.cpp @@ -415,12 +415,17 @@ bool QGLXContext::m_supportsThreading = true; // If this list grows to any significant size, change it a // proper string table and make the implementation below use // binary search. -static const char *qglx_threadedgl_blacklist[] = { +static const char *qglx_threadedgl_blacklist_renderer[] = { "Chromium", // QTBUG-32225 (initialization fails) "Mesa DRI Intel(R) Sandybridge Mobile", // QTBUG-34492 (flickering in fullscreen) 0 }; +static const char *qglx_threadedgl_blacklist_vendor[] = { + "nouveau", // QTCREATORBUG-10875 (crash in creator) + 0 +}; + void QGLXContext::queryDummyContext() { if (m_queriedDummyContext) @@ -437,8 +442,8 @@ void QGLXContext::queryDummyContext() oldSurface = oldContext->surface(); QScopedPointer surface; - const char *vendor = glXGetClientString(glXGetCurrentDisplay(), GLX_VENDOR); - if (vendor && !strcmp(vendor, "ATI")) { + const char *glxvendor = glXGetClientString(glXGetCurrentDisplay(), GLX_VENDOR); + if (glxvendor && !strcmp(glxvendor, "ATI")) { QWindow *window = new QWindow; window->resize(64, 64); window->setSurfaceType(QSurface::OpenGLSurface); @@ -454,11 +459,19 @@ void QGLXContext::queryDummyContext() context.create(); context.makeCurrent(surface.data()); - const char *renderer = (const char *) glGetString(GL_RENDERER); - m_supportsThreading = true; - for (int i = 0; qglx_threadedgl_blacklist[i]; ++i) { - if (strstr(renderer, qglx_threadedgl_blacklist[i]) != 0) { + + const char *renderer = (const char *) glGetString(GL_RENDERER); + for (int i = 0; qglx_threadedgl_blacklist_renderer[i]; ++i) { + if (strstr(renderer, qglx_threadedgl_blacklist_renderer[i]) != 0) { + m_supportsThreading = false; + break; + } + } + + const char *vendor = (const char *) glGetString(GL_VENDOR); + for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) { + if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != 0) { m_supportsThreading = false; break; } diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index cc8c42f96b..4d2735ca85 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -1749,10 +1749,26 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(void *event, int valuatorNum, doub return true; } -bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event, int opCode) +// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: +// - "pad0" became "extension" +// - "pad1" and "pad" became "pad0" +// New and old version of this struct share the following fields: +// NOTE: API might change again in the next release of xcb in which case this comment will +// need to be updated to reflect the reality. +typedef struct qt_xcb_ge_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; +} qt_xcb_ge_event_t; + +bool QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *ev, int opCode) { - // xGenericEvent has "extension" on the second byte, xcb_ge_event_t has "pad0". - if (event->pad0 == opCode) { + qt_xcb_ge_event_t *event = (qt_xcb_ge_event_t *)ev; + // xGenericEvent has "extension" on the second byte, the same is true for xcb_ge_event_t starting from + // the xcb version 1.9.3, prior to that it was called "pad0". + if (event->extension == opCode) { // xcb event structs contain stuff that wasn't on the wire, the full_sequence field // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes. // Move this data back to have the same layout in memory as it was on the wire diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 933294e21f..1da0be9781 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -6999,23 +6999,23 @@ void QWidget::setUpdatesEnabled(bool enable) } /*! - Shows the widget and its child widgets. This function is - equivalent to setVisible(true) in the normal case, and equivalent - to showFullScreen() if the QStyleHints::showIsFullScreen() hint - is true and the window is not a popup. + Shows the widget and its child widgets. - \sa raise(), showEvent(), hide(), setVisible(), showMinimized(), showMaximized(), - showNormal(), isVisible(), windowFlags() + This is equivalent to calling showFullScreen(), showMaximized(), or setVisible(true), + depending on the platform's default behavior for the window flags. + + \sa raise(), showEvent(), hide(), setVisible(), showMinimized(), showMaximized(), + showNormal(), isVisible(), windowFlags(), flags() */ void QWidget::show() { - bool isPopup = data->window_flags & Qt::Popup & ~Qt::Window; - if (isWindow() && !isPopup && qApp->styleHints()->showIsFullScreen()) + Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(data->window_flags); + if (defaultState == Qt::WindowFullScreen) showFullScreen(); - else if (isWindow() && !(data->window_flags & Qt::Dialog & ~Qt::Window) && !isPopup && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ShowIsMaximized).toBool()) + else if (defaultState == Qt::WindowMaximized) showMaximized(); else - setVisible(true); + setVisible(true); // FIXME: Why not showNormal(), like QWindow::show()? } /*! \internal diff --git a/src/widgets/kernel/qwidget_qpa.cpp b/src/widgets/kernel/qwidget_qpa.cpp index 3c4985591e..93234f3958 100644 --- a/src/widgets/kernel/qwidget_qpa.cpp +++ b/src/widgets/kernel/qwidget_qpa.cpp @@ -510,9 +510,9 @@ void QWidgetPrivate::show_sys() QWindow *window = q->windowHandle(); - q->setAttribute(Qt::WA_Mapped); if (q->testAttribute(Qt::WA_DontShowOnScreen)) { invalidateBuffer(q->rect()); + q->setAttribute(Qt::WA_Mapped); if (q->isWindow() && q->windowModality() != Qt::NonModal && window) { // add our window to the modal window list QGuiApplicationPrivate::showModalWindow(window); diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index edcbb08d1c..2e96247873 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -715,10 +715,6 @@ void QWidgetWindow::handleWindowStateChangedEvent(QWindowStateChangeEvent *event break; } - // Note that widgetState == m_widget->data->window_state when triggered by QWidget::setWindowState(). - if (!(widgetState & Qt::WindowMinimized)) - m_widget->setAttribute(Qt::WA_Mapped); - // Sent event if the state changed (that is, it is not triggered by // QWidget::setWindowState(), which also sends an event to the widget). if (widgetState != int(m_widget->data->window_state)) { diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 0cbd1c720c..3a4fd449c8 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -1834,14 +1834,6 @@ QSize QMenu::sizeHint() const void QMenu::popup(const QPoint &p, QAction *atAction) { Q_D(QMenu); - -#ifdef Q_OS_ANDROID - if (!d->platformMenu.isNull() && !testAttribute(Qt::WA_SetStyle)) { - d->platformMenu->showPopup(window()->windowHandle(), p, 0); - return; - } -#endif - if (d->scroll) { // reset scroll state from last popup if (d->scroll->scrollOffset) d->itemsDirty = 1; // sizeHint will be incorrect if there is previous scroll diff --git a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp index 1e2b0ed649..6c80c5ff47 100644 --- a/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp +++ b/tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp @@ -2903,6 +2903,12 @@ void tst_QDateTime::timeZones() const // - Test 03:00:00 = 1 hour after tran hourAfterStd = QDateTime(QDate(2013, 10, 27), QTime(3, 0, 0), cet); QCOMPARE(hourAfterStd.toMSecsSinceEpoch(), dstToStdMSecs + 3600000); + + // Test Time Zone that has transitions but no future transitions afer a given date + QTimeZone sgt("Asia/Singapore"); + QDateTime future(QDate(2015, 1, 1), QTime(0, 0, 0), sgt); + QVERIFY(future.isValid()); + QCOMPARE(future.offsetFromUtc(), 28800); } void tst_QDateTime::invalid() const diff --git a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp index ed08bcefbf..d7643ff55d 100644 --- a/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp +++ b/tests/auto/corelib/tools/qtimezone/tst_qtimezone.cpp @@ -57,6 +57,7 @@ private slots: void nullTest(); void dataStreamTest(); void availableTimeZoneIds(); + void stressTest(); void windowsId(); // Backend tests void utcTest(); @@ -75,7 +76,7 @@ private: tst_QTimeZone::tst_QTimeZone() { - // Set to true to print debug output + // Set to true to print debug output, test Display Names and run long stress tests debug = false; } @@ -367,11 +368,59 @@ void tst_QTimeZone::availableTimeZoneIds() } } +void tst_QTimeZone::stressTest() +{ + QList idList = QTimeZone::availableTimeZoneIds(); + foreach (const QByteArray &id, idList) { + QTimeZone testZone = QTimeZone(id); + QCOMPARE(testZone.isValid(), true); + QCOMPARE(testZone.id(), id); + QDateTime testDate = QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0), Qt::UTC); + testZone.country(); + testZone.comment(); + testZone.displayName(testDate); + testZone.displayName(QTimeZone::DaylightTime); + testZone.displayName(QTimeZone::StandardTime); + testZone.abbreviation(testDate); + testZone.offsetFromUtc(testDate); + testZone.standardTimeOffset(testDate); + testZone.daylightTimeOffset(testDate); + testZone.hasDaylightTime(); + testZone.isDaylightTime(testDate); + testZone.offsetData(testDate); + testZone.hasTransitions(); + testZone.nextTransition(testDate); + testZone.previousTransition(testDate); + // Dates known to be outside possible tz file pre-calculated rules range + QDateTime lowDate1 = QDateTime(QDate(1800, 1, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime lowDate2 = QDateTime(QDate(1800, 6, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime highDate1 = QDateTime(QDate(2200, 1, 1), QTime(0, 0, 0), Qt::UTC); + QDateTime highDate2 = QDateTime(QDate(2200, 6, 1), QTime(0, 0, 0), Qt::UTC); + testZone.nextTransition(lowDate1); + testZone.nextTransition(lowDate2); + testZone.previousTransition(lowDate2); + testZone.previousTransition(lowDate2); + testZone.nextTransition(highDate1); + testZone.nextTransition(highDate2); + testZone.previousTransition(highDate1); + testZone.previousTransition(highDate2); + if (debug) { + // This could take a long time, depending on platform and database + qDebug() << "Stress test calculating transistions for" << testZone.id(); + testZone.transitions(lowDate1, highDate1); + } + testDate.setTimeZone(testZone); + testDate.isValid(); + testDate.offsetFromUtc(); + testDate.timeZoneAbbreviation(); + } +} + void tst_QTimeZone::windowsId() { /* Current Windows zones for "Central Standard Time": - Region Olsen Id(s) + Region IANA Id(s) Default "America/Chicago" Canada "America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute" Mexico "America/Matamoros" @@ -380,24 +429,24 @@ void tst_QTimeZone::windowsId() "America/North_Dakota/New_Salem" AnyCountry "CST6CDT" */ - QCOMPARE(QTimeZone::olsenIdToWindowsId("America/Chicago"), + QCOMPARE(QTimeZone::ianaIdToWindowsId("America/Chicago"), QByteArray("Central Standard Time")); - QCOMPARE(QTimeZone::olsenIdToWindowsId("America/Resolute"), + QCOMPARE(QTimeZone::ianaIdToWindowsId("America/Resolute"), QByteArray("Central Standard Time")); // Partials shouldn't match - QCOMPARE(QTimeZone::olsenIdToWindowsId("America/Chi"), QByteArray()); - QCOMPARE(QTimeZone::olsenIdToWindowsId("InvalidZone"), QByteArray()); - QCOMPARE(QTimeZone::olsenIdToWindowsId(QByteArray()), QByteArray()); + QCOMPARE(QTimeZone::ianaIdToWindowsId("America/Chi"), QByteArray()); + QCOMPARE(QTimeZone::ianaIdToWindowsId("InvalidZone"), QByteArray()); + QCOMPARE(QTimeZone::ianaIdToWindowsId(QByteArray()), QByteArray()); // Check default value - QCOMPARE(QTimeZone::windowsIdToDefaultOlsenId("Central Standard Time"), + QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time"), QByteArray("America/Chicago")); - QCOMPARE(QTimeZone::windowsIdToDefaultOlsenId("Central Standard Time", QLocale::Canada), + QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time", QLocale::Canada), QByteArray("America/Winnipeg")); - QCOMPARE(QTimeZone::windowsIdToDefaultOlsenId("Central Standard Time", QLocale::AnyCountry), + QCOMPARE(QTimeZone::windowsIdToDefaultIanaId("Central Standard Time", QLocale::AnyCountry), QByteArray("CST6CDT")); - QCOMPARE(QTimeZone::windowsIdToDefaultOlsenId(QByteArray()), QByteArray()); + QCOMPARE(QTimeZone::windowsIdToDefaultIanaId(QByteArray()), QByteArray()); // No country is sorted list of all zones QList list; @@ -406,39 +455,39 @@ void tst_QTimeZone::windowsId() << "America/North_Dakota/Center" << "America/North_Dakota/New_Salem" << "America/Rainy_River" << "America/Rankin_Inlet" << "America/Resolute" << "America/Winnipeg" << "CST6CDT"; - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time"), list); + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time"), list); // Check country with no match returns empty list list.clear(); - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time", QLocale::NewZealand), + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::NewZealand), list); // Check valid country returns list in preference order list.clear(); list << "America/Winnipeg" << "America/Rainy_River" << "America/Rankin_Inlet" << "America/Resolute"; - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time", QLocale::Canada), list); + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::Canada), list); list.clear(); list << "America/Matamoros"; - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time", QLocale::Mexico), list); + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::Mexico), list); list.clear(); list << "America/Chicago" << "America/Indiana/Knox" << "America/Indiana/Tell_City" << "America/Menominee" << "America/North_Dakota/Beulah" << "America/North_Dakota/Center" << "America/North_Dakota/New_Salem"; - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time", QLocale::UnitedStates), + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::UnitedStates), list); list.clear(); list << "CST6CDT"; - QCOMPARE(QTimeZone::windowsIdToOlsenIds("Central Standard Time", QLocale::AnyCountry), + QCOMPARE(QTimeZone::windowsIdToIanaIds("Central Standard Time", QLocale::AnyCountry), list); // Check no windowsId return empty list.clear(); - QCOMPARE(QTimeZone::windowsIdToOlsenIds(QByteArray()), list); - QCOMPARE(QTimeZone::windowsIdToOlsenIds(QByteArray(), QLocale::AnyCountry), list); + QCOMPARE(QTimeZone::windowsIdToIanaIds(QByteArray()), list); + QCOMPARE(QTimeZone::windowsIdToIanaIds(QByteArray(), QLocale::AnyCountry), list); } void tst_QTimeZone::utcTest() @@ -639,10 +688,11 @@ void tst_QTimeZone::tzTest() QCOMPARE(dat.standardTimeOffset, 3600); QCOMPARE(dat.daylightTimeOffset, 0); + // Test previous to low value is invalid dat = tzp.previousTransition(-9999999999999); - QCOMPARE(dat.atMSecsSinceEpoch, (qint64)-2422054408000); - QCOMPARE(dat.standardTimeOffset, 3600); - QCOMPARE(dat.daylightTimeOffset, 0); + QCOMPARE(dat.atMSecsSinceEpoch, std::numeric_limits::min()); + QCOMPARE(dat.standardTimeOffset, std::numeric_limits::min()); + QCOMPARE(dat.daylightTimeOffset, std::numeric_limits::min()); dat = tzp.nextTransition(-9999999999999); QCOMPARE(dat.atMSecsSinceEpoch, (qint64)-2422054408000); diff --git a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp index ab05c64fe5..71a90e83f7 100644 --- a/tests/auto/other/qobjectrace/tst_qobjectrace.cpp +++ b/tests/auto/other/qobjectrace/tst_qobjectrace.cpp @@ -47,6 +47,12 @@ enum { OneMinute = 60 * 1000, TwoMinutes = OneMinute * 2 }; + +struct Functor +{ + void operator()() const {}; +}; + class tst_QObjectRace: public QObject { Q_OBJECT @@ -122,11 +128,7 @@ signals: private slots: void checkStopWatch() { -#if defined(Q_OS_WINCE) || defined(Q_OS_VXWORKS) - if (stopWatch.elapsed() >= OneMinute / 2) -#else - if (stopWatch.elapsed() >= OneMinute) -#endif + if (stopWatch.elapsed() >= 5000) quit(); QObject o; @@ -188,16 +190,34 @@ class MyObject : public QObject void signal7(); }; +namespace { +const char *_slots[] = { SLOT(slot1()) , SLOT(slot2()) , SLOT(slot3()), + SLOT(slot4()) , SLOT(slot5()) , SLOT(slot6()), + SLOT(slot7()) }; +const char *_signals[] = { SIGNAL(signal1()), SIGNAL(signal2()), SIGNAL(signal3()), + SIGNAL(signal4()), SIGNAL(signal5()), SIGNAL(signal6()), + SIGNAL(signal7()) }; + +typedef void (MyObject::*PMFType)(); +const PMFType _slotsPMF[] = { &MyObject::slot1, &MyObject::slot2, &MyObject::slot3, + &MyObject::slot4, &MyObject::slot5, &MyObject::slot6, + &MyObject::slot7 }; + +const PMFType _signalsPMF[] = { &MyObject::signal1, &MyObject::signal2, &MyObject::signal3, + &MyObject::signal4, &MyObject::signal5, &MyObject::signal6, + &MyObject::signal7 }; + +} class DestroyThread : public QThread { Q_OBJECT - QObject **objects; + MyObject **objects; int number; public: - void setObjects(QObject **o, int n) + void setObjects(MyObject **o, int n) { objects = o; number = n; @@ -206,8 +226,29 @@ public: } void run() { - for(int i = 0; i < number; i++) + for (int i = number-1; i >= 0; --i) { + /* Do some more connection and disconnection between object in this thread that have not been destroyed yet */ + + const int nAlive = i+1; + connect (objects[((i+1)*31) % nAlive], _signals[(12*i)%7], objects[((i+2)*37) % nAlive], _slots[(15*i+2)%7] ); + disconnect(objects[((i+1)*31) % nAlive], _signals[(12*i)%7], objects[((i+2)*37) % nAlive], _slots[(15*i+2)%7] ); + + connect (objects[((i+4)*41) % nAlive], _signalsPMF[(18*i)%7], objects[((i+5)*43) % nAlive], _slotsPMF[(19*i+2)%7] ); + disconnect(objects[((i+4)*41) % nAlive], _signalsPMF[(18*i)%7], objects[((i+5)*43) % nAlive], _slotsPMF[(19*i+2)%7] ); + + QMetaObject::Connection c = connect(objects[((i+5)*43) % nAlive], _signalsPMF[(9*i+1)%7], Functor()); + disconnect(c); + + disconnect(objects[i], _signalsPMF[(10*i+5)%7], 0, 0); + disconnect(objects[i], _signals[(11*i+6)%7], 0, 0); + + disconnect(objects[i], 0, objects[(i*17+6) % nAlive], 0); + if (i%4 == 1) { + disconnect(objects[i], 0, 0, 0); + } + delete objects[i]; + } } }; @@ -216,27 +257,24 @@ public: void tst_QObjectRace::destroyRace() { - enum { ThreadCount = 10, ObjectCountPerThread = 733, + enum { ThreadCount = 10, ObjectCountPerThread = 2777, ObjectCount = ThreadCount * ObjectCountPerThread }; - const char *_slots[] = { SLOT(slot1()) , SLOT(slot2()) , SLOT(slot3()), - SLOT(slot4()) , SLOT(slot5()) , SLOT(slot6()), - SLOT(slot7()) }; - - const char *_signals[] = { SIGNAL(signal1()), SIGNAL(signal2()), SIGNAL(signal3()), - SIGNAL(signal4()), SIGNAL(signal5()), SIGNAL(signal6()), - SIGNAL(signal7()) }; - - QObject *objects[ObjectCount]; + MyObject *objects[ObjectCount]; for (int i = 0; i < ObjectCount; ++i) objects[i] = new MyObject; - for (int i = 0; i < ObjectCount * 11; ++i) { + for (int i = 0; i < ObjectCount * 17; ++i) { connect(objects[(i*13) % ObjectCount], _signals[(2*i)%7], objects[((i+2)*17) % ObjectCount], _slots[(3*i+2)%7] ); connect(objects[((i+6)*23) % ObjectCount], _signals[(5*i+4)%7], objects[((i+8)*41) % ObjectCount], _slots[(i+6)%7] ); + + connect(objects[(i*67) % ObjectCount], _signalsPMF[(2*i)%7], + objects[((i+1)*71) % ObjectCount], _slotsPMF[(3*i+2)%7] ); + connect(objects[((i+3)*73) % ObjectCount], _signalsPMF[(5*i+4)%7], + objects[((i+5)*79) % ObjectCount], Functor() ); } DestroyThread *threads[ThreadCount]; diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp index 61a2abc084..9cb391d5f4 100644 --- a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -212,7 +212,14 @@ void tst_QFileSystemModel::rootPath() QString oldRootPath = model->rootPath(); const QStringList documentPaths = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); QVERIFY(!documentPaths.isEmpty()); - const QString documentPath = documentPaths.front(); + QString documentPath = documentPaths.front(); + // In particular on Linux, ~/Documents (the first + // DocumentsLocation) may not exist, so choose ~ in that case: + if (!QFile::exists(documentPath)) { + documentPath = QDir::homePath(); + qWarning("%s: first documentPath \"%s\" does not exist. Using ~ (\"%s\") instead.", + Q_FUNC_INFO, qPrintable(documentPaths.front()), qPrintable(documentPath)); + } root = model->setRootPath(documentPath); QTRY_VERIFY(model->rowCount(root) >= 0); diff --git a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp index fe1df6c8f0..15c92663ec 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsscene/tst_qgraphicsscene.cpp @@ -4072,6 +4072,7 @@ void tst_QGraphicsScene::isActive() { + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QWidget toplevel2; QHBoxLayout *layout = new QHBoxLayout; toplevel2.setLayout(layout); @@ -4085,12 +4086,13 @@ void tst_QGraphicsScene::isActive() QVERIFY(!scene1.hasFocus()); QVERIFY(!scene2.hasFocus()); + toplevel2.move(availableGeometry.topLeft() + QPoint(50, 50)); toplevel2.show(); QApplication::setActiveWindow(&toplevel2); QVERIFY(QTest::qWaitForWindowActive(&toplevel2)); QCOMPARE(QApplication::activeWindow(), &toplevel2); - QVERIFY(scene1.isActive()); + QTRY_VERIFY(scene1.isActive()); QVERIFY(!scene2.isActive()); QVERIFY(scene1.hasFocus()); QVERIFY(!scene2.hasFocus()); @@ -4133,6 +4135,7 @@ void tst_QGraphicsScene::isActive() QVERIFY(!scene2.hasFocus()); QGraphicsView topLevelView; + topLevelView.move(availableGeometry.topLeft() + QPoint(500, 50)); topLevelView.show(); QApplication::setActiveWindow(&topLevelView); topLevelView.setFocus(); diff --git a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp index 7a0ba50ff0..37cc6522a2 100644 --- a/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicsview/tst_qgraphicsview.cpp @@ -4687,13 +4687,17 @@ public: void tst_QGraphicsView::hoverLeave() { + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QGraphicsScene scene; QGraphicsView view(&scene); + view.resize(160, 160); + view.move(availableGeometry.center() - QPoint(80, 80)); GraphicsItemWithHover *item = new GraphicsItemWithHover; scene.addItem(item); // move the cursor out of the way - QCursor::setPos(1,1); + const QPoint outOfWindow = view.geometry().topRight() + QPoint(50, 0); + QCursor::setPos(outOfWindow); view.show(); qApp->setActiveWindow(&view); @@ -4701,16 +4705,14 @@ void tst_QGraphicsView::hoverLeave() QPoint pos = view.viewport()->mapToGlobal(view.mapFromScene(item->mapToScene(10, 10))); QCursor::setPos(pos); - QTest::qWait(200); - QVERIFY(item->receivedEnterEvent); + QTRY_VERIFY(item->receivedEnterEvent); QCOMPARE(item->enterWidget, view.viewport()); - QCursor::setPos(1,1); - QTest::qWait(200); + QCursor::setPos(outOfWindow); #ifdef Q_OS_MAC QEXPECT_FAIL("", "QTBUG-26274 - behaviour regression", Abort); #endif - QVERIFY(item->receivedLeaveEvent); + QTRY_VERIFY(item->receivedLeaveEvent); QCOMPARE(item->leaveWidget, view.viewport()); } diff --git a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp index 2dd2089f81..3271b31692 100644 --- a/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp +++ b/tests/auto/widgets/graphicsview/qgraphicswidget/tst_qgraphicswidget.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include "../../../qtest-config.h" @@ -1780,8 +1781,11 @@ void tst_QGraphicsWidget::verifyFocusChain() void tst_QGraphicsWidget::updateFocusChainWhenChildDie() { + const QRect availableGeometry = QGuiApplication::primaryScreen()->availableGeometry(); QGraphicsScene scene; QGraphicsView view(&scene); + view.resize(200, 150); + view.move(availableGeometry.topLeft() + QPoint(50, 50)); view.show(); QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -1801,6 +1805,9 @@ void tst_QGraphicsWidget::updateFocusChainWhenChildDie() QVERIFY(w1_1->hasFocus()); QWidget myWidget(0); QLineEdit edit(&myWidget); + (new QHBoxLayout(&myWidget))->addWidget(&edit); + edit.setMinimumWidth(160); // Windows + myWidget.move(availableGeometry.topLeft() + QPoint(350, 50)); myWidget.show(); edit.setFocus(); QTRY_VERIFY(edit.hasFocus()); @@ -1809,8 +1816,9 @@ void tst_QGraphicsWidget::updateFocusChainWhenChildDie() w->setParentItem(parent); //We don't crash perfect QVERIFY(w); - QTest::mouseMove(view.viewport()); - QTest::mouseClick(view.viewport(), Qt::LeftButton, 0); + const QPoint center(view.viewport()->width() / 2, view.viewport()->height() / 2); + QTest::mouseMove(view.viewport(), center); + QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, center); #ifdef Q_OS_MAC QEXPECT_FAIL("", "QTBUG-23699", Continue); #endif diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 7bb5fd4614..03d6c1cdbd 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -7192,10 +7192,6 @@ void tst_QWidget::hideOpaqueChildWhileHidden() #if !defined(Q_OS_WINCE) void tst_QWidget::updateWhileMinimized() { -#ifdef Q_OS_UNIX - if (qgetenv("XDG_CURRENT_DESKTOP").contains("Unity")) - QSKIP("This test fails on Unity."); // Minimized windows are not unmapped for some reason. -#endif // Q_OS_UNIX UpdateWidget widget; // Filter out activation change and focus events to avoid update() calls in QWidget. widget.updateOnActivationChangeAndFocusIn = false; diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp index f5585c583a..1bbbfd610e 100644 --- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp +++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp @@ -91,8 +91,6 @@ private slots: void tst_showWithoutActivating(); void tst_paintEventOnSecondShow(); - void obscuredNativeMapped(); - #ifndef QT_NO_DRAGANDDROP void tst_dnd(); #endif @@ -370,32 +368,6 @@ void tst_QWidget_window::tst_paintEventOnSecondShow() QTRY_VERIFY(w.paintEventReceived); } -// QTBUG-33520, a toplevel fully obscured by native children should still receive Qt::WA_Mapped -void tst_QWidget_window::obscuredNativeMapped() -{ - enum { size = 200 }; - - QWidget topLevel; - topLevel.setWindowFlags(Qt::FramelessWindowHint); - QWidget *child = new QWidget(&topLevel); - child->resize(size, size); - topLevel.resize(size, size); - topLevel.move(QGuiApplication::primaryScreen()->availableGeometry().center() - QPoint(size /2 , size / 2)); - child->winId(); - topLevel.show(); - QTRY_VERIFY(topLevel.testAttribute(Qt::WA_Mapped)); -#if defined(Q_OS_MAC) - QSKIP("This test fails on Mac."); // Minimized windows are not unmapped for some reason. -#elif defined(Q_OS_UNIX) - if (qgetenv("XDG_CURRENT_DESKTOP").contains("Unity")) - QSKIP("This test fails on Unity."); // Minimized windows are not unmapped for some reason. -#endif // Q_OS_UNIX - topLevel.setWindowState(Qt::WindowMinimized); - QTRY_VERIFY(!topLevel.testAttribute(Qt::WA_Mapped)); - topLevel.setWindowState(Qt::WindowNoState); - QTRY_VERIFY(topLevel.testAttribute(Qt::WA_Mapped)); -} - #ifndef QT_NO_DRAGANDDROP /* DnD test for QWidgetWindow (handleDrag*Event() functions). diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 57c22a23cc..e2fef2c665 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -194,6 +194,7 @@ Configure::Configure(int& argc, char** argv) dictionary[ "QT_CUPS" ] = "auto"; dictionary[ "CFG_GCC_SYSROOT" ] = "yes"; dictionary[ "SLOG2" ] = "no"; + dictionary[ "PPS" ] = "no"; dictionary[ "SYSTEM_PROXIES" ] = "no"; dictionary[ "WERROR" ] = "auto"; dictionary[ "QREAL" ] = "double"; @@ -879,6 +880,10 @@ void Configure::parseCmdLine() dictionary[ "SLOG2" ] = "no"; } else if (configCmdLine.at(i) == "-slog2") { dictionary[ "SLOG2" ] = "yes"; + } else if (configCmdLine.at(i) == "-no-pps") { + dictionary[ "PPS" ] = "no"; + } else if (configCmdLine.at(i) == "-pps") { + dictionary[ "PPS" ] = "yes"; } else if (configCmdLine.at(i) == "-no-system-proxies") { dictionary[ "SYSTEM_PROXIES" ] = "no"; } else if (configCmdLine.at(i) == "-system-proxies") { @@ -1648,6 +1653,7 @@ void Configure::applySpecSpecifics() } else if ((platform() == QNX) || (platform() == BLACKBERRY)) { dictionary["STACK_PROTECTOR_STRONG"] = "auto"; dictionary["SLOG2"] = "auto"; + dictionary["PPS"] = "auto"; dictionary["QT_XKBCOMMON"] = "no"; dictionary[ "ANGLE" ] = "no"; dictionary[ "FONT_CONFIG" ] = "auto"; @@ -1864,6 +1870,9 @@ bool Configure::displayHelp() if ((platform() == QNX) || (platform() == BLACKBERRY)) { desc("SLOG2", "yes", "-slog2", "Compile with slog2 support."); desc("SLOG2", "no", "-no-slog2", "Do not compile with slog2 support."); + + desc("PPS", "yes", "-pps", "Compile with PPS support."); + desc("PPS", "no", "-no-pps", "Do not compile with PPS support."); } desc("ANGLE", "yes", "-angle", "Use the ANGLE implementation of OpenGL ES 2.0."); @@ -2203,6 +2212,8 @@ bool Configure::checkAvailability(const QString &part) available = (platform() == QNX || platform() == BLACKBERRY) && compilerSupportsFlag("qcc -fstack-protector-strong"); } else if (part == "SLOG2") { available = tryCompileProject("unix/slog2"); + } else if (part == "PPS") { + available = (platform() == QNX || platform() == BLACKBERRY) && tryCompileProject("unix/pps"); } else if (part == "NEON") { available = (dictionary["QT_ARCH"] == "arm") && tryCompileProject("unix/neon"); } else if (part == "FONT_CONFIG") { @@ -2348,6 +2359,10 @@ void Configure::autoDetection() dictionary["SLOG2"] = checkAvailability("SLOG2") ? "yes" : "no"; } + if (dictionary["PPS"] == "auto") { + dictionary["PPS"] = checkAvailability("PPS") ? "yes" : "no"; + } + if (dictionary["QT_EVENTFD"] == "auto") dictionary["QT_EVENTFD"] = checkAvailability("QT_EVENTFD") ? "yes" : "no"; @@ -3174,6 +3189,9 @@ void Configure::generateQConfigPri() if (dictionary[ "SLOG2" ] == "yes") configStream << " slog2"; + if (dictionary[ "PPS" ] == "yes") + configStream << " qqnx_pps"; + if (dictionary["DIRECTWRITE"] == "yes") configStream << " directwrite"; @@ -3540,8 +3558,10 @@ void Configure::displayConfig() sout << " HarfBuzz-NG support....." << dictionary[ "HARFBUZZ" ] << endl; sout << " PCRE support............" << dictionary[ "PCRE" ] << endl; sout << " ICU support............." << dictionary[ "ICU" ] << endl; - if ((platform() == QNX) || (platform() == BLACKBERRY)) + if ((platform() == QNX) || (platform() == BLACKBERRY)) { sout << " SLOG2 support..........." << dictionary[ "SLOG2" ] << endl; + sout << " PPS support............." << dictionary[ "PPS" ] << endl; + } sout << " ANGLE..................." << dictionary[ "ANGLE" ] << endl; sout << endl;