summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/corelib/serialization/cbordump/cbordump.pro14
-rw-r--r--examples/corelib/serialization/cbordump/main.cpp765
-rw-r--r--examples/corelib/serialization/cbordump/tag-transform.xslt25
-rw-r--r--examples/corelib/serialization/convert/cborconverter.cpp393
-rw-r--r--examples/corelib/serialization/convert/cborconverter.h85
-rw-r--r--examples/corelib/serialization/convert/convert.pro29
-rw-r--r--examples/corelib/serialization/convert/converter.h103
-rw-r--r--examples/corelib/serialization/convert/datastreamconverter.cpp273
-rw-r--r--examples/corelib/serialization/convert/datastreamconverter.h85
-rw-r--r--examples/corelib/serialization/convert/jsonconverter.cpp212
-rw-r--r--examples/corelib/serialization/convert/jsonconverter.h85
-rw-r--r--examples/corelib/serialization/convert/main.cpp234
-rw-r--r--examples/corelib/serialization/convert/nullconverter.cpp99
-rw-r--r--examples/corelib/serialization/convert/nullconverter.h69
-rw-r--r--examples/corelib/serialization/convert/textconverter.cpp160
-rw-r--r--examples/corelib/serialization/convert/textconverter.h70
-rw-r--r--examples/corelib/serialization/convert/xmlconverter.cpp514
-rw-r--r--examples/corelib/serialization/convert/xmlconverter.h69
-rw-r--r--examples/corelib/serialization/serialization.pro5
-rw-r--r--examples/corelib/threads/queuedcustomtype/window.cpp3
-rw-r--r--examples/examples.pro3
-rw-r--r--examples/gui/doc/src/rasterwindow.qdoc5
-rw-r--r--examples/gui/rasterwindow/rasterwindow.cpp4
-rw-r--r--examples/network/doc/images/secureudpclient-example.pngbin0 -> 23211 bytes
-rw-r--r--examples/network/doc/images/secureudpserver-example.pngbin0 -> 38412 bytes
-rw-r--r--examples/network/doc/src/secureudpclient.qdoc124
-rw-r--r--examples/network/doc/src/secureudpserver.qdoc131
-rw-r--r--examples/network/loopback/dialog.cpp55
-rw-r--r--examples/network/loopback/dialog.h27
-rw-r--r--examples/network/loopback/main.cpp4
-rw-r--r--examples/network/network-chat/client.cpp2
-rw-r--r--examples/network/network-chat/connection.cpp264
-rw-r--r--examples/network/network-chat/connection.h17
-rw-r--r--examples/network/network-chat/peermanager.cpp54
-rw-r--r--examples/network/network-chat/peermanager.h4
-rw-r--r--examples/network/network-chat/server.cpp3
-rw-r--r--examples/network/network.pro1
-rw-r--r--examples/network/secureudpclient/addressdialog.cpp118
-rw-r--r--examples/network/secureudpclient/addressdialog.h85
-rw-r--r--examples/network/secureudpclient/addressdialog.ui132
-rw-r--r--examples/network/secureudpclient/association.cpp197
-rw-r--r--examples/network/secureudpclient/association.h97
-rw-r--r--examples/network/secureudpclient/main.cpp64
-rw-r--r--examples/network/secureudpclient/mainwindow.cpp185
-rw-r--r--examples/network/secureudpclient/mainwindow.h111
-rw-r--r--examples/network/secureudpclient/mainwindow.ui198
-rw-r--r--examples/network/secureudpclient/secureudpclient.pro22
-rw-r--r--examples/network/secureudpserver/main.cpp64
-rw-r--r--examples/network/secureudpserver/mainwindow.cpp138
-rw-r--r--examples/network/secureudpserver/mainwindow.h92
-rw-r--r--examples/network/secureudpserver/mainwindow.ui207
-rw-r--r--examples/network/secureudpserver/nicselector.cpp95
-rw-r--r--examples/network/secureudpserver/nicselector.h84
-rw-r--r--examples/network/secureudpserver/nicselector.ui144
-rw-r--r--examples/network/secureudpserver/secureudpserver.pro21
-rw-r--r--examples/network/secureudpserver/server.cpp277
-rw-r--r--examples/network/secureudpserver/server.h107
-rw-r--r--examples/opengl/contextinfo/widget.cpp2
-rw-r--r--examples/opengl/qopenglwindow/main.cpp15
-rw-r--r--examples/vulkan/hellovulkantexture/hellovulkantexture.cpp16
-rw-r--r--examples/widgets/doc/src/customsortfiltermodel.qdoc16
-rw-r--r--examples/widgets/doc/src/gradients.qdoc7
-rw-r--r--examples/widgets/graphicsview/boxes/glextensions.h15
-rw-r--r--examples/widgets/graphicsview/boxes/scene.cpp16
-rw-r--r--examples/widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp17
-rw-r--r--examples/widgets/mainwindows/mainwindow/mainwindow.cpp1
-rw-r--r--examples/widgets/painting/deform/pathdeform.cpp11
-rw-r--r--examples/widgets/painting/gradients/gradients.cpp58
-rw-r--r--examples/widgets/painting/gradients/gradients.h7
-rw-r--r--examples/widgets/painting/gradients/gradients.html6
-rw-r--r--examples/widgets/richtext/calendar/mainwindow.cpp6
-rw-r--r--examples/widgets/richtext/textedit/example.html14
-rw-r--r--examples/widgets/richtext/textedit/textedit.cpp140
-rw-r--r--examples/widgets/tools/undo/main.cpp2
-rw-r--r--examples/widgets/touch/dials/dials.pro (renamed from examples/touch/dials/dials.pro)2
-rw-r--r--examples/widgets/touch/dials/dials.ui (renamed from examples/touch/dials/dials.ui)0
-rw-r--r--examples/widgets/touch/dials/doc/images/touch-dials-example.png (renamed from examples/touch/dials/doc/images/touch-dials-example.png)bin17676 -> 17676 bytes
-rw-r--r--examples/widgets/touch/dials/doc/src/touch-dials.qdoc (renamed from examples/touch/dials/doc/src/touch-dials.qdoc)2
-rw-r--r--examples/widgets/touch/dials/main.cpp (renamed from examples/touch/dials/main.cpp)0
-rw-r--r--examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc42
-rw-r--r--examples/widgets/touch/fingerpaint/fingerpaint.pro (renamed from examples/touch/fingerpaint/fingerpaint.pro)2
-rw-r--r--examples/widgets/touch/fingerpaint/main.cpp (renamed from examples/touch/fingerpaint/main.cpp)0
-rw-r--r--examples/widgets/touch/fingerpaint/mainwindow.cpp (renamed from examples/touch/fingerpaint/mainwindow.cpp)0
-rw-r--r--examples/widgets/touch/fingerpaint/mainwindow.h (renamed from examples/touch/fingerpaint/mainwindow.h)0
-rw-r--r--examples/widgets/touch/fingerpaint/scribblearea.cpp (renamed from examples/touch/fingerpaint/scribblearea.cpp)0
-rw-r--r--examples/widgets/touch/fingerpaint/scribblearea.h (renamed from examples/touch/fingerpaint/scribblearea.h)0
-rw-r--r--examples/widgets/touch/knobs/doc/images/touch-knobs-example.png (renamed from examples/touch/knobs/doc/images/touch-knobs-example.png)bin1290 -> 1290 bytes
-rw-r--r--examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc (renamed from examples/touch/knobs/doc/src/touch-knobs.qdoc)2
-rw-r--r--examples/widgets/touch/knobs/knob.cpp (renamed from examples/touch/knobs/knob.cpp)0
-rw-r--r--examples/widgets/touch/knobs/knob.h (renamed from examples/touch/knobs/knob.h)0
-rw-r--r--examples/widgets/touch/knobs/knobs.pro (renamed from examples/touch/knobs/knobs.pro)2
-rw-r--r--examples/widgets/touch/knobs/main.cpp (renamed from examples/touch/knobs/main.cpp)0
-rw-r--r--examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.pngbin0 -> 42493 bytes
-rw-r--r--examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc38
-rw-r--r--examples/widgets/touch/pinchzoom/graphicsview.cpp (renamed from examples/touch/pinchzoom/graphicsview.cpp)0
-rw-r--r--examples/widgets/touch/pinchzoom/graphicsview.h (renamed from examples/touch/pinchzoom/graphicsview.h)0
-rw-r--r--examples/widgets/touch/pinchzoom/images/cheese.jpg (renamed from examples/touch/pinchzoom/images/cheese.jpg)bin3029 -> 3029 bytes
-rw-r--r--examples/widgets/touch/pinchzoom/main.cpp (renamed from examples/touch/pinchzoom/main.cpp)0
-rw-r--r--examples/widgets/touch/pinchzoom/mice.qrc (renamed from examples/touch/pinchzoom/mice.qrc)0
-rw-r--r--examples/widgets/touch/pinchzoom/mouse.cpp (renamed from examples/touch/pinchzoom/mouse.cpp)0
-rw-r--r--examples/widgets/touch/pinchzoom/mouse.h (renamed from examples/touch/pinchzoom/mouse.h)0
-rw-r--r--examples/widgets/touch/pinchzoom/pinchzoom.pro (renamed from examples/touch/pinchzoom/pinchzoom.pro)2
-rw-r--r--examples/widgets/touch/touch.pro (renamed from examples/touch/touch.pro)0
-rw-r--r--examples/widgets/widgets.pro1
-rw-r--r--examples/widgets/widgets/tablet/mainwindow.cpp16
-rw-r--r--examples/widgets/widgets/tablet/mainwindow.h3
-rw-r--r--examples/widgets/widgets/tablet/tablet.pro5
-rw-r--r--examples/widgets/widgets/tablet/tabletcanvas.cpp12
-rw-r--r--examples/widgets/widgets/tablet/tabletcanvas.h1
109 files changed, 6570 insertions, 337 deletions
diff --git a/examples/corelib/serialization/cbordump/cbordump.pro b/examples/corelib/serialization/cbordump/cbordump.pro
new file mode 100644
index 0000000000..7fb2ef69f0
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/cbordump.pro
@@ -0,0 +1,14 @@
+QT += core
+QT -= gui
+
+TARGET = cbordump
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/cbordump
+INSTALLS += target
+
+SOURCES += main.cpp
diff --git a/examples/corelib/serialization/cbordump/main.cpp b/examples/corelib/serialization/cbordump/main.cpp
new file mode 100644
index 0000000000..222bd43645
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/main.cpp
@@ -0,0 +1,765 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCborStreamReader>
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QCoreApplication>
+#include <QFile>
+#include <QLocale>
+#include <QStack>
+
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+/*
+ * To regenerate:
+ * curl -O https://www.iana.org/assignments/cbor-tags/cbor-tags.xml
+ * xsltproc tag-transform.xslt cbor-tags.xml
+ */
+
+// GENERATED CODE
+struct CborTagDescription
+{
+ QCborTag tag;
+ const char *description; // with space and parentheses
+};
+
+// CBOR Tags
+static const CborTagDescription tagDescriptions[] = {
+ // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+ { QCborTag(0), " (Standard date/time string; see Section 2.4.1 [RFC7049])" },
+ { QCborTag(1), " (Epoch-based date/time; see Section 2.4.1 [RFC7049])" },
+ { QCborTag(2), " (Positive bignum; see Section 2.4.2 [RFC7049])" },
+ { QCborTag(3), " (Negative bignum; see Section 2.4.2 [RFC7049])" },
+ { QCborTag(4), " (Decimal fraction; see Section 2.4.3 [RFC7049])" },
+ { QCborTag(5), " (Bigfloat; see Section 2.4.3 [RFC7049])" },
+ { QCborTag(16), " (COSE Single Recipient Encrypted Data Object [RFC8152])" },
+ { QCborTag(17), " (COSE Mac w/o Recipients Object [RFC8152])" },
+ { QCborTag(18), " (COSE Single Signer Data Object [RFC8152])" },
+ { QCborTag(21), " (Expected conversion to base64url encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(22), " (Expected conversion to base64 encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(23), " (Expected conversion to base16 encoding; see Section 2.4.4.2 [RFC7049])" },
+ { QCborTag(24), " (Encoded CBOR data item; see Section 2.4.4.1 [RFC7049])" },
+ { QCborTag(25), " (reference the nth previously seen string)" },
+ { QCborTag(26), " (Serialised Perl object with classname and constructor arguments)" },
+ { QCborTag(27), " (Serialised language-independent object with type name and constructor arguments)" },
+ { QCborTag(28), " (mark value as (potentially) shared)" },
+ { QCborTag(29), " (reference nth marked value)" },
+ { QCborTag(30), " (Rational number)" },
+ { QCborTag(32), " (URI; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(33), " (base64url; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(34), " (base64; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(35), " (Regular expression; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(36), " (MIME message; see Section 2.4.4.3 [RFC7049])" },
+ { QCborTag(37), " (Binary UUID ( section 4.1.2))" },
+ { QCborTag(38), " (Language-tagged string)" },
+ { QCborTag(39), " (Identifier)" },
+ { QCborTag(61), " (CBOR Web Token (CWT))" },
+ { QCborTag(96), " (COSE Encrypted Data Object [RFC8152])" },
+ { QCborTag(97), " (COSE MACed Data Object [RFC8152])" },
+ { QCborTag(98), " (COSE Signed Data Object [RFC8152])" },
+ { QCborTag(256), " (mark value as having string references)" },
+ { QCborTag(257), " (Binary MIME message)" },
+ { QCborTag(258), " (Mathematical finite set)" },
+ { QCborTag(260), " (Network Address (IPv4 or IPv6 or MAC Address))" },
+ { QCborTag(264), " (Decimal fraction with arbitrary exponent)" },
+ { QCborTag(265), " (Bigfloat with arbitrary exponent)" },
+ { QCborTag(1001), " (extended time)" },
+ { QCborTag(1002), " (duration)" },
+ { QCborTag(1003), " (period)" },
+ { QCborTag(22098), " (hint that indicates an additional level of indirection)" },
+ { QCborTag(55799), " (Self-describe CBOR; see Section 2.4.5 [RFC7049])" },
+ { QCborTag(15309736), " (RAINS Message)" },
+ { QCborTag(-1), nullptr }
+};
+// END GENERATED CODE
+
+enum {
+ // See RFC 7049 section 2.
+ SmallValueBitLength = 5,
+ SmallValueMask = (1 << SmallValueBitLength) - 1, /* 0x1f */
+ Value8Bit = 24,
+ Value16Bit = 25,
+ Value32Bit = 26,
+ Value64Bit = 27
+};
+
+struct CborDumper
+{
+ enum DumpOption {
+ ShowCompact = 0x01,
+ ShowWidthIndicators = 0x02,
+ ShowAnnotated = 0x04
+ };
+ Q_DECLARE_FLAGS(DumpOptions, DumpOption)
+
+ CborDumper(QFile *f, DumpOptions opts_);
+ QCborError dump();
+
+private:
+ void dumpOne(int nestingLevel);
+ void dumpOneDetailed(int nestingLevel);
+
+ void printByteArray(const QByteArray &ba);
+ void printWidthIndicator(quint64 value, char space = '\0');
+ void printStringWidthIndicator(quint64 value);
+
+ QCborStreamReader reader;
+ QByteArray data;
+ QStack<quint8> byteArrayEncoding;
+ qint64 offset = 0;
+ DumpOptions opts;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(CborDumper::DumpOptions)
+
+static int cborNumberSize(quint64 value)
+{
+ int normalSize = 1;
+ if (value > std::numeric_limits<quint32>::max())
+ normalSize += 8;
+ else if (value > std::numeric_limits<quint16>::max())
+ normalSize += 4;
+ else if (value > std::numeric_limits<quint8>::max())
+ normalSize += 2;
+ else if (value >= Value8Bit)
+ normalSize += 1;
+ return normalSize;
+}
+
+CborDumper::CborDumper(QFile *f, DumpOptions opts_)
+ : opts(opts_)
+{
+ // try to mmap the file, this is faster
+ char *ptr = reinterpret_cast<char *>(f->map(0, f->size(), QFile::MapPrivateOption));
+ if (ptr) {
+ // worked
+ data = QByteArray::fromRawData(ptr, f->size());
+ reader.addData(data);
+ } else if ((opts & ShowAnnotated) || f->isSequential()) {
+ // details requires full contents, so allocate memory
+ data = f->readAll();
+ reader.addData(data);
+ } else {
+ // just use the QIODevice
+ reader.setDevice(f);
+ }
+}
+
+QCborError CborDumper::dump()
+{
+ byteArrayEncoding << quint8(QCborKnownTags::ExpectedBase16);
+ if (!reader.lastError()) {
+ if (opts & ShowAnnotated)
+ dumpOneDetailed(0);
+ else
+ dumpOne(0);
+ }
+
+ QCborError err = reader.lastError();
+ offset = reader.currentOffset();
+ if (err) {
+ fflush(stdout);
+ fprintf(stderr, "cbordump: decoding failed at %lld: %s\n",
+ offset, qPrintable(err.toString()));
+ if (!data.isEmpty())
+ fprintf(stderr, " bytes at %lld: %s\n", offset,
+ data.mid(offset, 9).toHex(' ').constData());
+ } else {
+ if (!opts.testFlag(ShowAnnotated))
+ printf("\n");
+ if (offset < data.size() || (reader.device() && reader.device()->bytesAvailable()))
+ fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
+ }
+
+ return err;
+}
+
+template <typename T> static inline bool canConvertTo(double v)
+{
+ // The [conv.fpint] (7.10 Floating-integral conversions) section of the
+ // standard says only exact conversions are guaranteed. Converting
+ // integrals to floating-point with loss of precision has implementation-
+ // defined behavior whether the next higher or next lower is returned;
+ // converting FP to integral is UB if it can't be represented.;
+ Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer);
+
+ double supremum = ldexp(1, std::numeric_limits<T>::digits);
+ if (v >= supremum)
+ return false;
+
+ if (v < std::numeric_limits<T>::min()) // either zero or a power of two, so it's exact
+ return false;
+
+ // we're in range
+ return v == floor(v);
+}
+
+static QString fpToString(double v, const char *suffix)
+{
+ if (qIsInf(v))
+ return v < 0 ? QStringLiteral("-inf") : QStringLiteral("inf");
+ if (qIsNaN(v))
+ return QStringLiteral("nan");
+ if (canConvertTo<qint64>(v))
+ return QString::number(qint64(v)) + ".0" + suffix;
+ if (canConvertTo<quint64>(v))
+ return QString::number(quint64(v)) + ".0" + suffix;
+
+ QString s = QString::number(v, 'g', QLocale::FloatingPointShortest);
+ if (!s.contains('.') && !s.contains('e'))
+ s += '.';
+ s += suffix;
+ return s;
+};
+
+void CborDumper::dumpOne(int nestingLevel)
+{
+ QString indent(1, QLatin1Char(' '));
+ QString indented = indent;
+ if (!opts.testFlag(ShowCompact)) {
+ indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' '));
+ indented = QLatin1Char('\n') + QString(4 + 4 * nestingLevel, QLatin1Char(' '));
+ }
+
+ switch (reader.type()) {
+ case QCborStreamReader::UnsignedInteger: {
+ quint64 u = reader.toUnsignedInteger();
+ printf("%llu", u);
+ reader.next();
+ printWidthIndicator(u);
+ return;
+ }
+
+ case QCborStreamReader::NegativeInteger: {
+ quint64 n = quint64(reader.toNegativeInteger());
+ if (n == 0) // -2^64 (wrapped around)
+ printf("-18446744073709551616");
+ else
+ printf("-%llu", n);
+ reader.next();
+ printWidthIndicator(n);
+ return;
+ }
+
+ case QCborStreamReader::ByteArray:
+ case QCborStreamReader::String: {
+ bool isLengthKnown = reader.isLengthKnown();
+ if (!isLengthKnown) {
+ printf("(_ ");
+ ++offset;
+ }
+
+ QString comma;
+ if (reader.isByteArray()) {
+ auto r = reader.readByteArray();
+ while (r.status == QCborStreamReader::Ok) {
+ printf("%s", qPrintable(comma));
+ printByteArray(r.data);
+ printStringWidthIndicator(r.data.size());
+
+ r = reader.readByteArray();
+ comma = QLatin1Char(',') + indented;
+ }
+ } else {
+ auto r = reader.readString();
+ while (r.status == QCborStreamReader::Ok) {
+ printf("%s\"%s\"", qPrintable(comma), qPrintable(r.data));
+ printStringWidthIndicator(r.data.toUtf8().size());
+
+ r = reader.readString();
+ comma = QLatin1Char(',') + indented;
+ }
+ }
+
+ if (!isLengthKnown && !reader.lastError())
+ printf(")");
+ break;
+ }
+
+ case QCborStreamReader::Array:
+ case QCborStreamReader::Map: {
+ const char *delimiters = (reader.isArray() ? "[]" : "{}");
+ printf("%c", delimiters[0]);
+
+ if (reader.isLengthKnown()) {
+ quint64 len = reader.length();
+ reader.enterContainer();
+ printWidthIndicator(len, ' ');
+ } else {
+ reader.enterContainer();
+ offset = reader.currentOffset();
+ printf("_ ");
+ }
+
+ const char *comma = "";
+ while (!reader.lastError() && reader.hasNext()) {
+ printf("%s%s", comma, qPrintable(indented));
+ comma = ",";
+ dumpOne(nestingLevel + 1);
+
+ if (reader.parentContainerType() != QCborStreamReader::Map)
+ continue;
+ if (reader.lastError())
+ break;
+ printf(": ");
+ dumpOne(nestingLevel + 1);
+ }
+
+ if (!reader.lastError()) {
+ reader.leaveContainer();
+ printf("%s%c", qPrintable(indent), delimiters[1]);
+ }
+ break;
+ }
+
+ case QCborStreamReader::Tag: {
+ QCborTag tag = reader.toTag();
+ printf("%llu", quint64(tag));
+
+ if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
+ || tag == QCborKnownTags::ExpectedBase64url)
+ byteArrayEncoding.push(quint8(tag));
+
+ if (reader.next()) {
+ printWidthIndicator(quint64(tag));
+ printf("(");
+ dumpOne(nestingLevel); // same level!
+ printf(")");
+ }
+
+ if (tag == QCborKnownTags::ExpectedBase16 || tag == QCborKnownTags::ExpectedBase64
+ || tag == QCborKnownTags::ExpectedBase64url)
+ byteArrayEncoding.pop();
+ break;
+ }
+
+ case QCborStreamReader::SimpleType:
+ switch (reader.toSimpleType()) {
+ case QCborSimpleType::False:
+ printf("false");
+ break;
+ case QCborSimpleType::True:
+ printf("true");
+ break;
+ case QCborSimpleType::Null:
+ printf("null");
+ break;
+ case QCborSimpleType::Undefined:
+ printf("undefined");
+ break;
+ default:
+ printf("simple(%u)", quint8(reader.toSimpleType()));
+ break;
+ }
+ reader.next();
+ break;
+
+ case QCborStreamReader::Float16:
+ printf("%s", qPrintable(fpToString(reader.toFloat16(), "f16")));
+ reader.next();
+ break;
+ case QCborStreamReader::Float:
+ printf("%s", qPrintable(fpToString(reader.toFloat(), "f")));
+ reader.next();
+ break;
+ case QCborStreamReader::Double:
+ printf("%s", qPrintable(fpToString(reader.toDouble(), "")));
+ reader.next();
+ break;
+ case QCborStreamReader::Invalid:
+ return;
+ }
+
+ offset = reader.currentOffset();
+}
+
+void CborDumper::dumpOneDetailed(int nestingLevel)
+{
+ auto tagDescription = [](QCborTag tag) {
+ for (auto entry : tagDescriptions) {
+ if (entry.tag == tag)
+ return entry.description;
+ if (entry.tag > tag)
+ break;
+ }
+ return "";
+ };
+ auto printOverlong = [](int actualSize, quint64 value) {
+ if (cborNumberSize(value) != actualSize)
+ printf(" (overlong)");
+ };
+ auto print = [=](const char *descr, const char *fmt, ...) {
+ qint64 prevOffset = offset;
+ offset = reader.currentOffset();
+ if (prevOffset == offset)
+ return;
+
+ QByteArray bytes = data.mid(prevOffset, offset - prevOffset);
+ QByteArray indent(nestingLevel * 2, ' ');
+ printf("%-50s # %s ", (indent + bytes.toHex(' ')).constData(), descr);
+
+ va_list va;
+ va_start(va, fmt);
+ vprintf(fmt, va);
+ va_end(va);
+
+ if (strstr(fmt, "%ll")) {
+ // Only works because all callers below that use %ll, use it as the
+ // first arg
+ va_start(va, fmt);
+ quint64 value = va_arg(va, quint64);
+ va_end(va);
+ printOverlong(bytes.size(), value);
+ }
+
+ puts("");
+ };
+
+ auto printFp = [=](const char *descr, double d) {
+ QString s = fpToString(d, "");
+ if (s.size() <= 6)
+ return print(descr, "%s", qPrintable(s));
+ return print(descr, "%a", d);
+ };
+
+ auto printString = [=](const char *descr) {
+ QByteArray indent(nestingLevel * 2, ' ');
+ const char *chunkStr = (reader.isLengthKnown() ? "" : "chunk ");
+ int width = 48 - indent.size();
+ int bytesPerLine = qMax(width / 3, 5);
+
+ qsizetype size = reader.currentStringChunkSize();
+ if (size < 0)
+ return; // error
+ if (size >= std::numeric_limits<int>::max()) {
+ fprintf(stderr, "String length too big, %lli\n", qint64(size));
+ exit(EXIT_FAILURE);
+ }
+
+ // if asking for the current string chunk changes the offset, then it
+ // was chunked
+ print(descr, "(indeterminate length)");
+
+ QByteArray bytes(size, Qt::Uninitialized);
+ auto r = reader.readStringChunk(bytes.data(), bytes.size());
+ while (r.status == QCborStreamReader::Ok) {
+ // We'll have to decode the length's width directly from CBOR...
+ const char *lenstart = data.constData() + offset;
+ const char *lenend = lenstart + 1;
+ quint8 additionalInformation = (*lenstart & SmallValueMask);
+
+ // Decode this number directly from CBOR (see RFC 7049 section 2)
+ if (additionalInformation >= Value8Bit) {
+ if (additionalInformation == Value8Bit)
+ lenend += 1;
+ else if (additionalInformation == Value16Bit)
+ lenend += 2;
+ else if (additionalInformation == Value32Bit)
+ lenend += 4;
+ else
+ lenend += 8;
+ }
+
+ {
+ QByteArray lenbytes = QByteArray::fromRawData(lenstart, lenend - lenstart);
+ printf("%-50s # %s %slength %llu",
+ (indent + lenbytes.toHex(' ')).constData(), descr, chunkStr, quint64(size));
+ printOverlong(lenbytes.size(), size);
+ puts("");
+ }
+
+ offset = reader.currentOffset();
+
+ for (int i = 0; i < r.data; i += bytesPerLine) {
+ QByteArray section = bytes.mid(i, bytesPerLine);
+ printf(" %s%s", indent.constData(), section.toHex(' ').constData());
+
+ // print the decode
+ QByteArray spaces(width > 0 ? width - section.size() * 3 + 1: 0, ' ');
+ printf("%s # \"", spaces.constData());
+ auto ptr = reinterpret_cast<const uchar *>(section.constData());
+ for (int j = 0; j < section.size(); ++j)
+ printf("%c", ptr[j] >= 0x80 || ptr[j] < 0x20 ? '.' : ptr[j]);
+
+ puts("\"");
+ }
+
+ // get the next chunk
+ size = reader.currentStringChunkSize();
+ if (size < 0)
+ return; // error
+ if (size >= std::numeric_limits<int>::max()) {
+ fprintf(stderr, "String length too big, %lli\n", qint64(size));
+ exit(EXIT_FAILURE);
+ }
+ bytes.resize(size);
+ r = reader.readStringChunk(bytes.data(), bytes.size());
+ }
+ };
+
+ if (reader.lastError())
+ return;
+
+ switch (reader.type()) {
+ case QCborStreamReader::UnsignedInteger: {
+ quint64 u = reader.toUnsignedInteger();
+ reader.next();
+ if (u < 65536 || (u % 100000) == 0)
+ print("Unsigned integer", "%llu", u);
+ else
+ print("Unsigned integer", "0x%llx", u);
+ return;
+ }
+
+ case QCborStreamReader::NegativeInteger: {
+ quint64 n = quint64(reader.toNegativeInteger());
+ reader.next();
+ print("Negative integer", n == 0 ? "-18446744073709551616" : "-%llu", n);
+ return;
+ }
+
+ case QCborStreamReader::ByteArray:
+ case QCborStreamReader::String: {
+ bool isLengthKnown = reader.isLengthKnown();
+ const char *descr = (reader.isString() ? "Text string" : "Byte string");
+ if (!isLengthKnown)
+ ++nestingLevel;
+
+ printString(descr);
+ if (reader.lastError())
+ return;
+
+ if (!isLengthKnown) {
+ --nestingLevel;
+ print("Break", "");
+ }
+ break;
+ }
+
+ case QCborStreamReader::Array:
+ case QCborStreamReader::Map: {
+ const char *descr = (reader.isArray() ? "Array" : "Map");
+ if (reader.isLengthKnown()) {
+ quint64 len = reader.length();
+ reader.enterContainer();
+ print(descr, "length %llu", len);
+ } else {
+ reader.enterContainer();
+ print(descr, "(indeterminate length)");
+ }
+
+ while (!reader.lastError() && reader.hasNext())
+ dumpOneDetailed(nestingLevel + 1);
+
+ if (!reader.lastError()) {
+ reader.leaveContainer();
+ print("Break", "");
+ }
+ break;
+ }
+
+ case QCborStreamReader::Tag: {
+ QCborTag tag = reader.toTag();
+ reader.next();
+ print("Tag", "%llu%s", quint64(tag), tagDescription(tag));
+ dumpOneDetailed(nestingLevel + 1);
+ break;
+ }
+
+ case QCborStreamReader::SimpleType: {
+ QCborSimpleType st = reader.toSimpleType();
+ reader.next();
+ switch (st) {
+ case QCborSimpleType::False:
+ print("Simple Type", "false");
+ break;
+ case QCborSimpleType::True:
+ print("Simple Type", "true");
+ break;
+ case QCborSimpleType::Null:
+ print("Simple Type", "null");
+ break;
+ case QCborSimpleType::Undefined:
+ print("Simple Type", "undefined");
+ break;
+ default:
+ print("Simple Type", "%u", quint8(st));
+ break;
+ }
+ break;
+ }
+
+ case QCborStreamReader::Float16: {
+ double d = reader.toFloat16();
+ reader.next();
+ printFp("Float16", d);
+ break;
+ }
+ case QCborStreamReader::Float: {
+ double d = reader.toFloat();
+ reader.next();
+ printFp("Float", d);
+ break;
+ }
+ case QCborStreamReader::Double: {
+ double d = reader.toDouble();
+ reader.next();
+ printFp("Double", d);
+ break;
+ }
+ case QCborStreamReader::Invalid:
+ return;
+ }
+
+ offset = reader.currentOffset();
+}
+
+void CborDumper::printByteArray(const QByteArray &ba)
+{
+ switch (byteArrayEncoding.top()) {
+ default:
+ printf("h'%s'", ba.toHex(' ').constData());
+ break;
+
+ case quint8(QCborKnownTags::ExpectedBase64):
+ printf("b64'%s'", ba.toBase64().constData());
+ break;
+
+ case quint8(QCborKnownTags::ExpectedBase64url):
+ printf("b64'%s'", ba.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals).constData());
+ break;
+ }
+}
+
+void printIndicator(quint64 value, qint64 previousOffset, qint64 offset, char space)
+{
+ int normalSize = cborNumberSize(value);
+ int actualSize = offset - previousOffset;
+
+ if (actualSize != normalSize) {
+ Q_ASSERT(actualSize > 1);
+ actualSize -= 2;
+ printf("_%d", qPopulationCount(uint(actualSize)));
+ if (space)
+ printf("%c", space);
+ }
+}
+
+void CborDumper::printWidthIndicator(quint64 value, char space)
+{
+ qint64 previousOffset = offset;
+ offset = reader.currentOffset();
+ if (opts & ShowWidthIndicators)
+ printIndicator(value, previousOffset, offset, space);
+}
+
+void CborDumper::printStringWidthIndicator(quint64 value)
+{
+ qint64 previousOffset = offset;
+ offset = reader.currentOffset();
+ if (opts & ShowWidthIndicators)
+ printIndicator(value, previousOffset, offset - uint(value), '\0');
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ setlocale(LC_ALL, "C");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("CBOR Dumper tool"));
+ parser.addHelpOption();
+
+ QCommandLineOption compact({QStringLiteral("c"), QStringLiteral("compact")},
+ QStringLiteral("Use compact form (no line breaks)"));
+ parser.addOption(compact);
+
+ QCommandLineOption showIndicators({QStringLiteral("i"), QStringLiteral("indicators")},
+ QStringLiteral("Show indicators for width of lengths and integrals"));
+ parser.addOption(showIndicators);
+
+ QCommandLineOption verbose({QStringLiteral("a"), QStringLiteral("annotated")},
+ QStringLiteral("Show bytes and annotated decoding"));
+ parser.addOption(verbose);
+
+ parser.addPositionalArgument(QStringLiteral("[source]"),
+ QStringLiteral("CBOR file to read from"));
+
+ parser.process(app);
+
+ CborDumper::DumpOptions opts;
+ if (parser.isSet(compact))
+ opts |= CborDumper::ShowCompact;
+ if (parser.isSet(showIndicators))
+ opts |= CborDumper::ShowWidthIndicators;
+ if (parser.isSet(verbose))
+ opts |= CborDumper::ShowAnnotated;
+
+ QStringList files = parser.positionalArguments();
+ if (files.isEmpty())
+ files << "-";
+ for (const QString &file : qAsConst(files)) {
+ QFile f(file);
+ if (file == "-" ? f.open(stdin, QIODevice::ReadOnly) : f.open(QIODevice::ReadOnly)) {
+ if (files.size() > 1)
+ printf("/ From \"%s\" /\n", qPrintable(file));
+
+ CborDumper dumper(&f, opts);
+ QCborError err = dumper.dump();
+ if (err)
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/corelib/serialization/cbordump/tag-transform.xslt b/examples/corelib/serialization/cbordump/tag-transform.xslt
new file mode 100644
index 0000000000..3cc1b9b293
--- /dev/null
+++ b/examples/corelib/serialization/cbordump/tag-transform.xslt
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.iana.org/assignments" xmlns="http://www.iana.org/assignments" xmlns:_="http://www.iana.org/assignments" xmlns:DEFAULT="http://www.iana.org/assignments" version="1.0">
+<xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
+<xsl:template match="/a:registry[@id='cbor-tags']">struct CborTagDescription
+{
+ QCborTag tag;
+ const char *description; // with space and parentheses
+};
+
+// <xsl:value-of select="a:registry/a:title"/>
+static const CborTagDescription tagDescriptions[] = {
+ // from https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
+<xsl:for-each select="a:registry/a:record">
+ <xsl:sort select="a:value" data-type="number"/>
+ <xsl:if test="a:semantics != ''">
+ <xsl:call-template name="row"/>
+ </xsl:if>
+ </xsl:for-each> { QCborTag(-1), nullptr }
+};
+</xsl:template>
+<xsl:template name="row"> { QCborTag(<xsl:value-of select="a:value"/>), " (<xsl:value-of select="a:semantics"/> <xsl:call-template name="xref"/>)" },
+</xsl:template>
+<xsl:template name="xref"><xsl:if test="a:xref/@type = 'rfc'"> [<xsl:value-of select="translate(a:xref/@data,'rfc','RFC')"/>]</xsl:if>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/examples/corelib/serialization/convert/cborconverter.cpp b/examples/corelib/serialization/convert/cborconverter.cpp
new file mode 100644
index 0000000000..41724c935e
--- /dev/null
+++ b/examples/corelib/serialization/convert/cborconverter.cpp
@@ -0,0 +1,393 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "cborconverter.h"
+
+#include <QCborStreamReader>
+#include <QCborStreamWriter>
+#include <QCborMap>
+#include <QCborArray>
+#include <QCborValue>
+#include <QDataStream>
+#include <QDebug>
+#include <QFloat16>
+#include <QFile>
+#include <QMetaType>
+#include <QTextStream>
+
+#include <stdio.h>
+
+static CborConverter cborConverter;
+static CborDiagnosticDumper cborDiagnosticDumper;
+
+static const char optionHelp[] =
+ "convert-float-to-int=yes|no Write integers instead of floating point, if no\n"
+ " loss of precision occurs on conversion.\n"
+ "float16=yes|always|no Write using half-precision floating point.\n"
+ " If 'always', won't check for loss of precision.\n"
+ "float32=yes|always|no Write using single-precision floating point.\n"
+ " If 'always', won't check for loss of precision.\n"
+ "signature=yes|no Prepend the CBOR signature to the file output.\n"
+ ;
+
+static const char diagnosticHelp[] =
+ "extended=no|yes Use extended CBOR diagnostic format.\n"
+ "line-wrap=yes|no Split output into multiple lines.\n"
+ ;
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<<(QDataStream &ds, QCborSimpleType st)
+{
+ return ds << quint8(st);
+}
+
+QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st)
+{
+ quint8 v;
+ ds >> v;
+ st = QCborSimpleType(v);
+ return ds;
+}
+
+QDataStream &operator<<(QDataStream &ds, QCborTag tag)
+{
+ return ds << quint64(tag);
+}
+
+QDataStream &operator>>(QDataStream &ds, QCborTag &tag)
+{
+ quint64 v;
+ ds >> v;
+ tag = QCborTag(v);
+ return ds;
+}
+
+QT_END_NAMESPACE
+
+// We can't use QCborValue::toVariant directly because that would destroy
+// non-string keys in CBOR maps (QVariantMap can't handle those). Instead, we
+// have our own set of converter functions so we can keep the keys properly.
+
+static QVariant convertCborValue(const QCborValue &value);
+
+static QVariant convertCborMap(const QCborMap &map)
+{
+ VariantOrderedMap result;
+ result.reserve(map.size());
+ for (auto pair : map)
+ result.append({ convertCborValue(pair.first), convertCborValue(pair.second) });
+ return QVariant::fromValue(result);
+}
+
+static QVariant convertCborArray(const QCborArray &array)
+{
+ QVariantList result;
+ result.reserve(array.size());
+ for (auto value : array)
+ result.append(convertCborValue(value));
+ return result;
+}
+
+static QVariant convertCborValue(const QCborValue &value)
+{
+ if (value.isArray())
+ return convertCborArray(value.toArray());
+ if (value.isMap())
+ return convertCborMap(value.toMap());
+ return value.toVariant();
+}
+
+enum TrimFloatingPoint { Double, Float, Float16 };
+static QCborValue convertFromVariant(const QVariant &v, TrimFloatingPoint fpTrimming)
+{
+ if (v.userType() == QVariant::List) {
+ const QVariantList list = v.toList();
+ QCborArray array;
+ for (const QVariant &v : list)
+ array.append(convertFromVariant(v, fpTrimming));
+
+ return array;
+ }
+
+ if (v.userType() == qMetaTypeId<VariantOrderedMap>()) {
+ const auto m = v.value<VariantOrderedMap>();
+ QCborMap map;
+ for (const auto &pair : m)
+ map.insert(convertFromVariant(pair.first, fpTrimming),
+ convertFromVariant(pair.second, fpTrimming));
+ return map;
+ }
+
+ if (v.userType() == QVariant::Double && fpTrimming != Double) {
+ float f = float(v.toDouble());
+ if (fpTrimming == Float16)
+ return float(qfloat16(f));
+ return f;
+ }
+
+ return QCborValue::fromVariant(v);
+}
+
+QString CborDiagnosticDumper::name()
+{
+ return QStringLiteral("cbor-dump");
+}
+
+Converter::Direction CborDiagnosticDumper::directions()
+{
+ return Out;
+}
+
+Converter::Options CborDiagnosticDumper::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *CborDiagnosticDumper::optionsHelp()
+{
+ return diagnosticHelp;
+}
+
+bool CborDiagnosticDumper::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant CborDiagnosticDumper::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNREACHABLE();
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ return QVariant();
+}
+
+void CborDiagnosticDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QCborValue::DiagnosticNotationOptions opts = QCborValue::LineWrapped;
+ for (const QString &s : options) {
+ QStringList pair = s.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "line-wrap") {
+ opts &= ~QCborValue::LineWrapped;
+ if (pair.last() == "yes") {
+ opts |= QCborValue::LineWrapped;
+ continue;
+ } else if (pair.last() == "no") {
+ continue;
+ }
+ }
+ if (pair.first() == "extended") {
+ opts &= ~QCborValue::ExtendedFormat;
+ if (pair.last() == "yes")
+ opts |= QCborValue::ExtendedFormat;
+ continue;
+ }
+ }
+
+ fprintf(stderr, "Unknown CBOR diagnostic option '%s'. Available options are:\n%s",
+ qPrintable(s), diagnosticHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ QTextStream out(f);
+ out << convertFromVariant(contents, Double).toDiagnosticNotation(opts)
+ << endl;
+}
+
+CborConverter::CborConverter()
+{
+ qRegisterMetaType<QCborTag>();
+ qRegisterMetaTypeStreamOperators<QCborTag>();
+ QMetaType::registerDebugStreamOperator<QCborTag>();
+}
+
+QString CborConverter::name()
+{
+ return "cbor";
+}
+
+Converter::Direction CborConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options CborConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *CborConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool CborConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".cbor")))
+ return true;
+ }
+ return f->isReadable() && f->peek(3) == QByteArray("\xd9\xd9\xf7", 3);
+}
+
+QVariant CborConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ const char *ptr = nullptr;
+ if (auto file = qobject_cast<QFile *>(f))
+ ptr = reinterpret_cast<char *>(file->map(0, file->size()));
+
+ QByteArray mapped = QByteArray::fromRawData(ptr, ptr ? f->size() : 0);
+ QCborStreamReader reader(mapped);
+ if (!ptr)
+ reader.setDevice(f);
+
+ if (reader.isTag() && reader.toTag() == QCborKnownTags::Signature)
+ reader.next();
+
+ QCborValue contents = QCborValue::fromCbor(reader);
+ qint64 offset = reader.currentOffset();
+ if (reader.lastError()) {
+ fprintf(stderr, "Error loading CBOR contents (byte %lld): %s\n", offset,
+ qPrintable(reader.lastError().toString()));
+ fprintf(stderr, " bytes: %s\n",
+ (ptr ? mapped.mid(offset, 9) : f->read(9)).toHex(' ').constData());
+ exit(EXIT_FAILURE);
+ } else if (offset < mapped.size() || (!ptr && f->bytesAvailable())) {
+ fprintf(stderr, "Warning: bytes remaining at the end of the CBOR stream\n");
+ }
+
+ if (outputConverter == nullptr)
+ outputConverter = &cborDiagnosticDumper;
+ else if (outputConverter == null)
+ return QVariant();
+ else if (!outputConverter->outputOptions().testFlag(SupportsArbitraryMapKeys))
+ return contents.toVariant();
+ return convertCborValue(contents);
+}
+
+void CborConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ bool useSignature = true;
+ bool useIntegers = true;
+ enum { Yes, No, Always } useFloat16 = Yes, useFloat = Yes;
+
+ for (const QString &s : options) {
+ QStringList pair = s.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "convert-float-to-int") {
+ if (pair.last() == "yes") {
+ useIntegers = true;
+ continue;
+ } else if (pair.last() == "no") {
+ useIntegers = false;
+ continue;
+ }
+ }
+
+ if (pair.first() == "float16") {
+ if (pair.last() == "no") {
+ useFloat16 = No;
+ continue;
+ } else if (pair.last() == "yes") {
+ useFloat16 = Yes;
+ continue;
+ } else if (pair.last() == "always") {
+ useFloat16 = Always;
+ continue;
+ }
+ }
+
+ if (pair.first() == "float32") {
+ if (pair.last() == "no") {
+ useFloat = No;
+ continue;
+ } else if (pair.last() == "yes") {
+ useFloat = Yes;
+ continue;
+ } else if (pair.last() == "always") {
+ useFloat = Always;
+ continue;
+ }
+ }
+
+ if (pair.first() == "signature") {
+ if (pair.last() == "yes") {
+ useSignature = true;
+ continue;
+ } else if (pair.last() == "no") {
+ useSignature = false;
+ continue;
+ }
+ }
+ }
+
+ fprintf(stderr, "Unknown CBOR format option '%s'. Valid options are:\n%s",
+ qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ QCborValue v = convertFromVariant(contents,
+ useFloat16 == Always ? Float16 : useFloat == Always ? Float : Double);
+ QCborStreamWriter writer(f);
+ if (useSignature)
+ writer.append(QCborKnownTags::Signature);
+
+ QCborValue::EncodingOptions opts;
+ if (useIntegers)
+ opts |= QCborValue::UseIntegers;
+ if (useFloat != No)
+ opts |= QCborValue::UseFloat;
+ if (useFloat16 != No)
+ opts |= QCborValue::UseFloat16;
+ v.toCbor(writer, opts);
+}
+
diff --git a/examples/corelib/serialization/convert/cborconverter.h b/examples/corelib/serialization/convert/cborconverter.h
new file mode 100644
index 0000000000..f0a89cb141
--- /dev/null
+++ b/examples/corelib/serialization/convert/cborconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CBORCONVERTER_H
+#define CBORCONVERTER_H
+
+#include "converter.h"
+
+class CborDiagnosticDumper : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class CborConverter : public Converter
+{
+public:
+ CborConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // CBORCONVERTER_H
diff --git a/examples/corelib/serialization/convert/convert.pro b/examples/corelib/serialization/convert/convert.pro
new file mode 100644
index 0000000000..d9b1de41e3
--- /dev/null
+++ b/examples/corelib/serialization/convert/convert.pro
@@ -0,0 +1,29 @@
+QT += core
+QT -= gui
+
+TARGET = convert
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/convert
+INSTALLS += target
+
+SOURCES += main.cpp \
+ cborconverter.cpp \
+ jsonconverter.cpp \
+ datastreamconverter.cpp \
+ textconverter.cpp \
+ xmlconverter.cpp \
+ nullconverter.cpp
+
+HEADERS += \
+ converter.h \
+ cborconverter.h \
+ jsonconverter.h \
+ datastreamconverter.h \
+ textconverter.h \
+ xmlconverter.h \
+ nullconverter.h
diff --git a/examples/corelib/serialization/convert/converter.h b/examples/corelib/serialization/convert/converter.h
new file mode 100644
index 0000000000..82e1fa1cb3
--- /dev/null
+++ b/examples/corelib/serialization/convert/converter.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CONVERTER_H
+#define CONVERTER_H
+
+#include <QIODevice>
+#include <QPair>
+#include <QVariant>
+#include <QVector>
+
+class VariantOrderedMap : public QVector<QPair<QVariant, QVariant>>
+{
+public:
+ VariantOrderedMap() = default;
+ VariantOrderedMap(const QVariantMap &map)
+ {
+ reserve(map.size());
+ for (auto it = map.begin(); it != map.end(); ++it)
+ append({it.key(), it.value()});
+ }
+};
+using Map = VariantOrderedMap;
+Q_DECLARE_METATYPE(Map)
+
+class Converter
+{
+protected:
+ Converter();
+
+public:
+ static Converter *null;
+
+ enum Direction {
+ In = 1, Out = 2, InOut = 3
+ };
+
+ enum Option {
+ SupportsArbitraryMapKeys = 0x01
+ };
+ Q_DECLARE_FLAGS(Options, Option)
+
+ virtual ~Converter() = 0;
+
+ virtual QString name() = 0;
+ virtual Direction directions() = 0;
+ virtual Options outputOptions() = 0;
+ virtual const char *optionsHelp() = 0;
+ virtual bool probeFile(QIODevice *f) = 0;
+ virtual QVariant loadFile(QIODevice *f, Converter *&outputConverter) = 0;
+ virtual void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Converter::Options)
+
+#endif // CONVERTER_H
diff --git a/examples/corelib/serialization/convert/datastreamconverter.cpp b/examples/corelib/serialization/convert/datastreamconverter.cpp
new file mode 100644
index 0000000000..7cdb844141
--- /dev/null
+++ b/examples/corelib/serialization/convert/datastreamconverter.cpp
@@ -0,0 +1,273 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "datastreamconverter.h"
+
+#include <QDataStream>
+#include <QDebug>
+#include <QTextStream>
+
+static const char optionHelp[] =
+ "byteorder=host|big|little Byte order to use.\n"
+ "version=<n> QDataStream version (default: Qt 5.0).\n"
+ ;
+
+static const char signature[] = "qds";
+
+static DataStreamDumper dataStreamDumper;
+static DataStreamConverter DataStreamConverter;
+
+QDataStream &operator<<(QDataStream &ds, const VariantOrderedMap &map)
+{
+ ds << qint64(map.size());
+ for (const auto &pair : map)
+ ds << pair.first << pair.second;
+ return ds;
+}
+
+QDataStream &operator>>(QDataStream &ds, VariantOrderedMap &map)
+{
+ map.clear();
+
+ qint64 size;
+ ds >> size;
+ map.reserve(size);
+
+ while (size-- > 0) {
+ VariantOrderedMap::value_type pair;
+ ds >> pair.first >> pair.second;
+ map.append(pair);
+ }
+
+ return ds;
+}
+
+
+static QString dumpVariant(const QVariant &v, const QString &indent = QLatin1String("\n"))
+{
+ QString result;
+ QString indented = indent + QLatin1String(" ");
+
+ int type = v.userType();
+ if (type == qMetaTypeId<VariantOrderedMap>() || type == QVariant::Map) {
+ const auto map = (type == QVariant::Map) ?
+ VariantOrderedMap(v.toMap()) : v.value<VariantOrderedMap>();
+
+ result = QLatin1String("Map {");
+ for (const auto &pair : map) {
+ result += indented + dumpVariant(pair.first, indented);
+ result.chop(1); // remove comma
+ result += QLatin1String(" => ") + dumpVariant(pair.second, indented);
+
+ }
+ result.chop(1); // remove comma
+ result += indent + QLatin1String("},");
+ } else if (type == QVariant::List) {
+ const QVariantList list = v.toList();
+
+ result = QLatin1String("List [");
+ for (const auto &item : list)
+ result += indented + dumpVariant(item, indented);
+ result.chop(1); // remove comma
+ result += indent + QLatin1String("],");
+ } else {
+ QDebug debug(&result);
+ debug.nospace() << v << ',';
+ }
+ return result;
+}
+
+QString DataStreamDumper::name()
+{
+ return QStringLiteral("datastream-dump");
+}
+
+Converter::Direction DataStreamDumper::directions()
+{
+ return Out;
+}
+
+Converter::Options DataStreamDumper::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *DataStreamDumper::optionsHelp()
+{
+ return nullptr;
+}
+
+bool DataStreamDumper::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant DataStreamDumper::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNREACHABLE();
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ return QVariant();
+}
+
+void DataStreamDumper::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ Q_UNUSED(options);
+ QString s = dumpVariant(contents);
+ s[s.size() - 1] = QLatin1Char('\n'); // replace the comma with newline
+
+ QTextStream out(f);
+ out << s;
+}
+
+DataStreamConverter::DataStreamConverter()
+{
+ qRegisterMetaType<VariantOrderedMap>();
+ qRegisterMetaTypeStreamOperators<VariantOrderedMap>();
+}
+
+QString DataStreamConverter::name()
+{
+ return QStringLiteral("datastream");
+}
+
+Converter::Direction DataStreamConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options DataStreamConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *DataStreamConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool DataStreamConverter::probeFile(QIODevice *f)
+{
+ return f->isReadable() && f->peek(sizeof(signature) - 1) == signature;
+}
+
+QVariant DataStreamConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = &dataStreamDumper;
+
+ char c;
+ if (f->read(sizeof(signature) -1) != signature ||
+ !f->getChar(&c) || (c != 'l' && c != 'B')) {
+ fprintf(stderr, "Could not load QDataStream file: invalid signature.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ QDataStream ds(f);
+ ds.setByteOrder(c == 'l' ? QDataStream::LittleEndian : QDataStream::BigEndian);
+
+ std::underlying_type<QDataStream::Version>::type version;
+ ds >> version;
+ ds.setVersion(QDataStream::Version(version));
+
+ QVariant result;
+ ds >> result;
+ return result;
+}
+
+void DataStreamConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QDataStream::Version version = QDataStream::Qt_5_0;
+ auto order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
+ for (const QString &option : options) {
+ const QStringList pair = option.split('=');
+ if (pair.size() == 2) {
+ if (pair.first() == "byteorder") {
+ if (pair.last() == "little") {
+ order = QDataStream::LittleEndian;
+ continue;
+ } else if (pair.last() == "big") {
+ order = QDataStream::BigEndian;
+ continue;
+ } else if (pair.last() == "host") {
+ order = QDataStream::ByteOrder(QSysInfo::ByteOrder);
+ continue;
+ }
+ }
+ if (pair.first() == "version") {
+ bool ok;
+ int n = pair.last().toInt(&ok);
+ if (ok) {
+ version = QDataStream::Version(n);
+ continue;
+ }
+
+ fprintf(stderr, "Invalid version number '%s': must be a number from 1 to %d.\n",
+ qPrintable(pair.last()), QDataStream::Qt_DefaultCompiledVersion);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ fprintf(stderr, "Unknown QDataStream formatting option '%s'. Available options are:\n%s",
+ qPrintable(option), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+
+ char c = order == QDataStream::LittleEndian ? 'l' : 'B';
+ f->write(signature);
+ f->write(&c, 1);
+
+ QDataStream ds(f);
+ ds.setVersion(version);
+ ds.setByteOrder(order);
+ ds << std::underlying_type<decltype(version)>::type(version);
+ ds << contents;
+}
diff --git a/examples/corelib/serialization/convert/datastreamconverter.h b/examples/corelib/serialization/convert/datastreamconverter.h
new file mode 100644
index 0000000000..1b74abc54f
--- /dev/null
+++ b/examples/corelib/serialization/convert/datastreamconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DATASTREAMCONVERTER_H
+#define DATASTREAMCONVERTER_H
+
+#include "converter.h"
+
+class DataStreamDumper : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class DataStreamConverter : public Converter
+{
+public:
+ DataStreamConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // DATASTREAMCONVERTER_H
diff --git a/examples/corelib/serialization/convert/jsonconverter.cpp b/examples/corelib/serialization/convert/jsonconverter.cpp
new file mode 100644
index 0000000000..80d1cc6827
--- /dev/null
+++ b/examples/corelib/serialization/convert/jsonconverter.cpp
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "jsonconverter.h"
+
+#include <QFile>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+
+static JsonConverter jsonConverter;
+static BinaryJsonConverter BinaryJsonConverter;
+
+static const char optionHelp[] =
+ "compact=no|yes Use compact JSON form.\n";
+
+static QJsonDocument convertFromVariant(const QVariant &v)
+{
+ QJsonDocument doc = QJsonDocument::fromVariant(v);
+ if (!doc.isObject() && !doc.isArray()) {
+ fprintf(stderr, "Could not convert contents to JSON.\n");
+ exit(EXIT_FAILURE);
+ }
+ return doc;
+}
+
+JsonConverter::JsonConverter()
+{
+}
+
+QString JsonConverter::name()
+{
+ return "json";
+}
+
+Converter::Direction JsonConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options JsonConverter::outputOptions()
+{
+ return {};
+}
+
+const char *JsonConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool JsonConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".json")))
+ return true;
+ }
+
+ if (f->isReadable()) {
+ QByteArray ba = f->peek(1);
+ return ba == "{" || ba == "[";
+ }
+ return false;
+}
+
+QVariant JsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QJsonParseError error;
+ QJsonDocument doc;
+ if (auto file = qobject_cast<QFile *>(f)) {
+ const char *ptr = reinterpret_cast<char *>(file->map(0, file->size()));
+ if (ptr)
+ doc = QJsonDocument::fromJson(QByteArray::fromRawData(ptr, file->size()), &error);
+ }
+
+ if (doc.isNull())
+ doc = QJsonDocument::fromJson(f->readAll(), &error);
+ if (error.error) {
+ fprintf(stderr, "Could not parse JSON content: offset %d: %s",
+ error.offset, qPrintable(error.errorString()));
+ exit(EXIT_FAILURE);
+ }
+ if (outputConverter == null)
+ return QVariant();
+ return doc.toVariant();
+}
+
+void JsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ QJsonDocument::JsonFormat format = QJsonDocument::Indented;
+ for (const QString &s : options) {
+ if (s == QLatin1String("compact=no")) {
+ format = QJsonDocument::Indented;
+ } else if (s == QLatin1String("compact=yes")) {
+ format = QJsonDocument::Compact;
+ } else {
+ fprintf(stderr, "Unknown option '%s' to JSON output. Valid options are:\n%s", qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ f->write(convertFromVariant(contents).toJson(format));
+}
+
+QString BinaryJsonConverter::name()
+{
+ return "binary-json";
+}
+
+Converter::Direction BinaryJsonConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options BinaryJsonConverter::outputOptions()
+{
+ return {};
+}
+
+const char *BinaryJsonConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool BinaryJsonConverter::probeFile(QIODevice *f)
+{
+ return f->isReadable() && f->peek(4) == "qbjs";
+}
+
+QVariant BinaryJsonConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = &jsonConverter;
+
+ QJsonDocument doc;
+ if (auto file = qobject_cast<QFile *>(f)) {
+ uchar *ptr = file->map(0, file->size());
+ if (ptr)
+ doc = QJsonDocument::fromRawData(reinterpret_cast<char *>(ptr), file->size());
+ }
+
+ if (doc.isNull())
+ doc = QJsonDocument::fromBinaryData(f->readAll());
+
+ if (!doc.isObject() && !doc.isArray()) {
+ fprintf(stderr, "Failed to load Binary JSON.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (outputConverter == null)
+ return QVariant();
+ return doc.toVariant();
+}
+
+void BinaryJsonConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to JSON output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ f->write(convertFromVariant(contents).toBinaryData());
+}
diff --git a/examples/corelib/serialization/convert/jsonconverter.h b/examples/corelib/serialization/convert/jsonconverter.h
new file mode 100644
index 0000000000..17170603c7
--- /dev/null
+++ b/examples/corelib/serialization/convert/jsonconverter.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef JSONCONVERTER_H
+#define JSONCONVERTER_H
+
+#include "converter.h"
+
+class JsonConverter : public Converter
+{
+public:
+ JsonConverter();
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+class BinaryJsonConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // JSONCONVERTER_H
diff --git a/examples/corelib/serialization/convert/main.cpp b/examples/corelib/serialization/convert/main.cpp
new file mode 100644
index 0000000000..e9d14792b0
--- /dev/null
+++ b/examples/corelib/serialization/convert/main.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "converter.h"
+
+#include <QCommandLineParser>
+#include <QCommandLineOption>
+#include <QCoreApplication>
+#include <QFile>
+#include <QFileInfo>
+
+#include <stdio.h>
+
+static QVector<Converter *> *availableConverters;
+
+Converter::Converter()
+{
+ if (!availableConverters)
+ availableConverters = new QVector<Converter *>;
+ availableConverters->append(this);
+}
+
+Converter::~Converter()
+{
+ availableConverters->removeAll(this);
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QStringList inputFormats;
+ QStringList outputFormats;
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ auto direction = conv->directions();
+ QString name = conv->name();
+ if (direction & Converter::In)
+ inputFormats << name;
+ if (direction & Converter::Out)
+ outputFormats << name;
+ }
+ inputFormats.sort();
+ outputFormats.sort();
+ inputFormats.prepend("auto");
+ outputFormats.prepend("auto");
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QStringLiteral("Qt file format conversion tool"));
+ parser.addHelpOption();
+
+ QCommandLineOption inputFormatOption(QStringList{"I", "input-format"});
+ inputFormatOption.setDescription(QLatin1String("Select the input format for the input file. Available formats: ") +
+ inputFormats.join(", "));
+ inputFormatOption.setValueName("format");
+ inputFormatOption.setDefaultValue(inputFormats.constFirst());
+ parser.addOption(inputFormatOption);
+
+ QCommandLineOption outputFormatOption(QStringList{"O", "output-format"});
+ outputFormatOption.setDescription(QLatin1String("Select the output format for the output file. Available formats: ") +
+ outputFormats.join(", "));
+ outputFormatOption.setValueName("format");
+ outputFormatOption.setDefaultValue(outputFormats.constFirst());
+ parser.addOption(outputFormatOption);
+
+ QCommandLineOption optionOption(QStringList{"o", "option"});
+ optionOption.setDescription(QStringLiteral("Format-specific options. Use --format-options to find out what options are available."));
+ optionOption.setValueName("options...");
+ optionOption.setDefaultValues({});
+ parser.addOption(optionOption);
+
+ QCommandLineOption formatOptionsOption("format-options");
+ formatOptionsOption.setDescription(QStringLiteral("Prints the list of valid options for --option for the converter format <format>."));
+ formatOptionsOption.setValueName("format");
+ parser.addOption(formatOptionsOption);
+
+ parser.addPositionalArgument(QStringLiteral("[source]"),
+ QStringLiteral("File to read from (stdin if none)"));
+ parser.addPositionalArgument(QStringLiteral("[destination]"),
+ QStringLiteral("File to write to (stdout if none)"));
+
+ parser.process(app);
+
+ if (parser.isSet(formatOptionsOption)) {
+ QString format = parser.value(formatOptionsOption);
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ const char *help = conv->optionsHelp();
+ if (help)
+ printf("The following options are available for format '%s':\n\n%s", qPrintable(format), help);
+ else
+ printf("Format '%s' supports no options.\n", qPrintable(format));
+ return EXIT_SUCCESS;
+ }
+ }
+
+ fprintf(stderr, "Unknown file format '%s'\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+
+ Converter *inconv = nullptr;
+ QString format = parser.value(inputFormatOption);
+ if (format != "auto") {
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ inconv = conv;
+ break;
+ }
+ }
+
+ if (!inconv) {
+ fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+ }
+
+ Converter *outconv = nullptr;
+ format = parser.value(outputFormatOption);
+ if (format != "auto") {
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->name() == format) {
+ outconv = conv;
+ break;
+ }
+ }
+
+ if (!outconv) {
+ fprintf(stderr, "Unknown file format \"%s\"\n", qPrintable(format));
+ return EXIT_FAILURE;
+ }
+ }
+
+ QStringList files = parser.positionalArguments();
+ QFile input(files.value(0));
+ QFile output(files.value(1));
+
+ if (input.fileName().isEmpty())
+ input.open(stdin, QIODevice::ReadOnly);
+ else
+ input.open(QIODevice::ReadOnly);
+ if (!input.isOpen()) {
+ fprintf(stderr, "Could not open \"%s\" for reading: %s\n",
+ qPrintable(input.fileName()), qPrintable(input.errorString()));
+ return EXIT_FAILURE;
+ }
+
+ if (output.fileName().isEmpty())
+ output.open(stdout, QIODevice::WriteOnly | QIODevice::Truncate);
+ else
+ output.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ if (!output.isOpen()) {
+ fprintf(stderr, "Could not open \"%s\" for writing: %s\n",
+ qPrintable(output.fileName()), qPrintable(output.errorString()));
+ return EXIT_FAILURE;
+ }
+
+ if (!inconv) {
+ // probe the input to find a file format
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->directions() & Converter::In && conv->probeFile(&input)) {
+ inconv = conv;
+ break;
+ }
+ }
+
+ if (!inconv) {
+ fprintf(stderr, "Could not determine input format. pass -I option.\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (!outconv) {
+ // probe the output to find a file format
+ for (Converter *conv : qAsConst(*availableConverters)) {
+ if (conv->directions() & Converter::Out && conv->probeFile(&output)) {
+ outconv = conv;
+ break;
+ }
+ }
+ }
+
+ // now finally perform the conversion
+ QVariant data = inconv->loadFile(&input, outconv);
+ Q_ASSERT_X(outconv, "Converter Tool",
+ "Internal error: converter format did not provide default");
+ outconv->saveFile(&output, data, parser.values(optionOption));
+ return EXIT_SUCCESS;
+}
diff --git a/examples/corelib/serialization/convert/nullconverter.cpp b/examples/corelib/serialization/convert/nullconverter.cpp
new file mode 100644
index 0000000000..2de492e64e
--- /dev/null
+++ b/examples/corelib/serialization/convert/nullconverter.cpp
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "nullconverter.h"
+
+static NullConverter nullConverter;
+Converter* Converter::null = &nullConverter;
+
+QString NullConverter::name()
+{
+ return QLatin1String("null");
+}
+
+Converter::Direction NullConverter::directions()
+{
+ return Out;
+}
+
+Converter::Options NullConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *NullConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool NullConverter::probeFile(QIODevice *f)
+{
+ Q_UNUSED(f);
+ return false;
+}
+
+QVariant NullConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ Q_UNUSED(f);
+ Q_UNUSED(outputConverter);
+ outputConverter = this;
+ return QVariant();
+}
+
+void NullConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to null output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ Q_UNUSED(f);
+ Q_UNUSED(contents);
+}
diff --git a/examples/corelib/serialization/convert/nullconverter.h b/examples/corelib/serialization/convert/nullconverter.h
new file mode 100644
index 0000000000..af7339f092
--- /dev/null
+++ b/examples/corelib/serialization/convert/nullconverter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef NULLCONVERTER_H
+#define NULLCONVERTER_H
+
+#include "converter.h"
+
+class NullConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // NULLCONVERTER_H
diff --git a/examples/corelib/serialization/convert/textconverter.cpp b/examples/corelib/serialization/convert/textconverter.cpp
new file mode 100644
index 0000000000..e80e69a0b5
--- /dev/null
+++ b/examples/corelib/serialization/convert/textconverter.cpp
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "textconverter.h"
+
+#include <QFile>
+#include <QTextStream>
+
+static void dumpVariant(QTextStream &out, const QVariant &v)
+{
+ switch (v.userType()) {
+ case QVariant::List: {
+ const QVariantList list = v.toList();
+ for (const QVariant &item : list)
+ dumpVariant(out, item);
+ break;
+ }
+
+ case QVariant::String: {
+ const QStringList list = v.toStringList();
+ for (const QString &s : list)
+ out << s << endl;
+ break;
+ }
+
+ case QVariant::Map: {
+ const QVariantMap map = v.toMap();
+ for (auto it = map.begin(); it != map.end(); ++it) {
+ out << it.key() << " => ";
+ dumpVariant(out, it.value());
+ }
+ break;
+ }
+
+ case QMetaType::Nullptr:
+ out << "(null)" << endl;
+ break;
+
+ default:
+ out << v.toString() << endl;
+ break;
+ }
+}
+
+QString TextConverter::name()
+{
+ return QStringLiteral("text");
+}
+
+Converter::Direction TextConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options TextConverter::outputOptions()
+{
+ return {};
+}
+
+const char *TextConverter::optionsHelp()
+{
+ return nullptr;
+}
+
+bool TextConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f))
+ return file->fileName().endsWith(QLatin1String(".txt"));
+ return false;
+}
+
+QVariant TextConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QVariantList list;
+ QTextStream in(f);
+ QString line ;
+ while (!in.atEnd()) {
+ in.readLineInto(&line);
+
+ bool ok;
+ qint64 v = line.toLongLong(&ok);
+ if (ok) {
+ list.append(v);
+ continue;
+ }
+
+ double d = line.toDouble(&ok);
+ if (ok) {
+ list.append(d);
+ continue;
+ }
+
+ list.append(line);
+ }
+
+ return list;
+}
+
+void TextConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ if (!options.isEmpty()) {
+ fprintf(stderr, "Unknown option '%s' to text output. This format has no options.\n", qPrintable(options.first()));
+ exit(EXIT_FAILURE);
+ }
+
+ QTextStream out(f);
+ dumpVariant(out, contents);
+}
+
+static TextConverter textConverter;
diff --git a/examples/corelib/serialization/convert/textconverter.h b/examples/corelib/serialization/convert/textconverter.h
new file mode 100644
index 0000000000..66f5136c02
--- /dev/null
+++ b/examples/corelib/serialization/convert/textconverter.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TEXTCONVERTER_H
+#define TEXTCONVERTER_H
+
+#include "converter.h"
+
+class TextConverter : public Converter
+{
+
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // TEXTCONVERTER_H
diff --git a/examples/corelib/serialization/convert/xmlconverter.cpp b/examples/corelib/serialization/convert/xmlconverter.cpp
new file mode 100644
index 0000000000..62908273ce
--- /dev/null
+++ b/examples/corelib/serialization/convert/xmlconverter.cpp
@@ -0,0 +1,514 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "xmlconverter.h"
+
+#include <QBitArray>
+#include <QtCborCommon>
+#include <QFile>
+#include <QFloat16>
+#include <QMetaType>
+#include <QRegularExpression>
+#include <QUrl>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+static const char optionHelp[] =
+ "compact=no|yes Use compact XML form.\n";
+
+static XmlConverter xmlConverter;
+
+static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options);
+
+static QVariantList listFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariantList list;
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ list << variantFromXml(xml, options);
+ continue;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ return list;
+}
+
+static VariantOrderedMap::value_type mapEntryFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariant key, value;
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (value.isValid())
+ break;
+ if (key.isValid())
+ value = variantFromXml(xml, options);
+ else
+ key = variantFromXml(xml, options);
+ continue;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ return { key, value };
+}
+
+static QVariant mapFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QVariantMap map1;
+ VariantOrderedMap map2;
+
+ while (!xml.atEnd() && !xml.isEndElement()) {
+ xml.readNext();
+ switch (xml.tokenType()) {
+ case QXmlStreamReader::StartElement:
+ if (xml.name() == QLatin1String("entry")) {
+ auto pair = mapEntryFromXml(xml, options);
+ if (options & Converter::SupportsArbitraryMapKeys)
+ map2.append(pair);
+ else
+ map1.insert(pair.first.toString(), pair.second);
+ continue;
+ }
+ break;
+
+ case QXmlStreamReader::EndElement:
+ continue;
+
+ case QXmlStreamReader::Comment:
+ // ignore comments
+ continue;
+
+ case QXmlStreamReader::Characters:
+ // ignore whitespace
+ if (xml.isWhitespace())
+ continue;
+ Q_FALLTHROUGH();
+
+ default:
+ break;
+ }
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(xml.name().toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ if (options & Converter::SupportsArbitraryMapKeys)
+ return QVariant::fromValue(map2);
+ return map1;
+}
+
+static QVariant variantFromXml(QXmlStreamReader &xml, Converter::Options options)
+{
+ QStringRef name = xml.name();
+ if (name == QLatin1String("list"))
+ return listFromXml(xml, options);
+ if (name == QLatin1String("map"))
+ return mapFromXml(xml, options);
+ if (name != QLatin1String("value")) {
+ fprintf(stderr, "%lld:%lld: Invalid XML key '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ QXmlStreamAttributes attrs = xml.attributes();
+ QStringRef type = attrs.value(QLatin1String("type"));
+
+ forever {
+ xml.readNext();
+ if (xml.isComment())
+ continue;
+ if (xml.isCDATA() || xml.isCharacters() || xml.isEndElement())
+ break;
+
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ QStringRef text = xml.text();
+ if (!xml.isCDATA())
+ text = text.trimmed();
+
+ QVariant result;
+ bool ok;
+ if (type.isEmpty()) {
+ // ok
+ } else if (type == QLatin1String("number")) {
+ // try integer first
+ qint64 v = text.toLongLong(&ok);
+ if (ok) {
+ result = v;
+ } else {
+ // let's see floating point
+ double d = text.toDouble(&ok);
+ result = d;
+ if (!ok) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: could not interpret '%s' as a number.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+ } else if (type == QLatin1String("bytes")) {
+ QByteArray data = text.toLatin1();
+ QStringRef encoding = attrs.value("encoding");
+ if (encoding == QLatin1String("base64url")) {
+ result = QByteArray::fromBase64(data, QByteArray::Base64UrlEncoding);
+ } else if (encoding == QLatin1String("hex")) {
+ result = QByteArray::fromHex(data);
+ } else if (encoding.isEmpty() || encoding == QLatin1String("base64")) {
+ result = QByteArray::fromBase64(data);
+ } else {
+ fprintf(stderr, "%lld:%lld: Invalid XML: unknown encoding '%s' for bytes.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(encoding.toString()));
+ exit(EXIT_FAILURE);
+ }
+ } else if (type == QLatin1String("string")) {
+ result = text.toString();
+ } else if (type == QLatin1String("null")) {
+ result = QVariant::fromValue(nullptr);
+ } else if (type == QLatin1String("CBOR simple type")) {
+ result = QVariant::fromValue(QCborSimpleType(text.toShort()));
+ } else if (type == QLatin1String("bits")) {
+ QBitArray ba;
+ ba.resize(text.size());
+ qsizetype n = 0;
+ for (qsizetype i = 0; i < text.size(); ++i) {
+ QChar c = text.at(i);
+ if (c == '1') {
+ ba.setBit(n++);
+ } else if (c == '0') {
+ ++n;
+ } else if (!c.isSpace()) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: invalid bit string '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(text.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+ ba.resize(n);
+ result = ba;
+ } else {
+ int id = QVariant::Invalid;
+ if (type == QLatin1String("datetime"))
+ id = QVariant::DateTime;
+ else if (type == QLatin1String("url"))
+ id = QVariant::Url;
+ else if (type == QLatin1String("uuid"))
+ id = QVariant::Uuid;
+ else if (type == QLatin1String("regex"))
+ id = QVariant::RegularExpression;
+ else
+ id = QMetaType::type(type.toLatin1());
+ if (id == QVariant::Invalid) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: unknown type '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ result = text.toString();
+ if (!result.convert(id)) {
+ fprintf(stderr, "%lld:%lld: Invalid XML: could not parse content as type '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(), qPrintable(type.toString()));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ do {
+ xml.readNext();
+ } while (xml.isComment() || xml.isWhitespace());
+
+ if (!xml.isEndElement()) {
+ fprintf(stderr, "%lld:%lld: Invalid XML %s '%s'.\n",
+ xml.lineNumber(), xml.columnNumber(),
+ qPrintable(xml.tokenString()), qPrintable(name.toString()));
+ exit(EXIT_FAILURE);
+ }
+
+ xml.readNext();
+ return result;
+}
+
+static void variantToXml(QXmlStreamWriter &xml, const QVariant &v)
+{
+ int type = v.userType();
+ if (type == QVariant::List) {
+ QVariantList list = v.toList();
+ xml.writeStartElement("list");
+ for (const QVariant &v : list)
+ variantToXml(xml, v);
+ xml.writeEndElement();
+ } else if (type == QVariant::Map || type == qMetaTypeId<VariantOrderedMap>()) {
+ const VariantOrderedMap map = (type == QVariant::Map) ?
+ VariantOrderedMap(v.toMap()) :
+ v.value<VariantOrderedMap>();
+
+ xml.writeStartElement("map");
+ for (const auto &pair : map) {
+ xml.writeStartElement("entry");
+ variantToXml(xml, pair.first);
+ variantToXml(xml, pair.second);
+ xml.writeEndElement();
+ }
+ xml.writeEndElement();
+ } else {
+ xml.writeStartElement("value");
+ QString typeString = QStringLiteral("type");
+ switch (type) {
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::Float:
+ case QMetaType::Double:
+ xml.writeAttribute(typeString, "number");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QByteArray:
+ xml.writeAttribute(typeString, "bytes");
+ xml.writeAttribute("encoding", "base64");
+ xml.writeCharacters(QString::fromLatin1(v.toByteArray().toBase64()));
+ break;
+
+ case QMetaType::QString:
+ xml.writeAttribute(typeString, "string");
+ xml.writeCDATA(v.toString());
+ break;
+
+ case QMetaType::Bool:
+ xml.writeAttribute(typeString, "bool");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::Nullptr:
+ xml.writeAttribute(typeString, "null");
+ break;
+
+ case QMetaType::UnknownType:
+ break;
+
+ case QMetaType::QDate:
+ case QMetaType::QTime:
+ case QMetaType::QDateTime:
+ xml.writeAttribute(typeString, "dateime");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QUrl:
+ xml.writeAttribute(typeString, "url");
+ xml.writeCharacters(v.toUrl().toString(QUrl::FullyEncoded));
+ break;
+
+ case QMetaType::QUuid:
+ xml.writeAttribute(typeString, "uuid");
+ xml.writeCharacters(v.toString());
+ break;
+
+ case QMetaType::QBitArray:
+ xml.writeAttribute(typeString, "bits");
+ xml.writeCharacters([](const QBitArray &ba) {
+ QString result;
+ for (qsizetype i = 0; i < ba.size(); ++i) {
+ if (i && i % 72 == 0)
+ result += '\n';
+ result += QLatin1Char(ba.testBit(i) ? '1' : '0');
+ }
+ return result;
+ }(v.toBitArray()));
+ break;
+
+ case QMetaType::QRegularExpression:
+ xml.writeAttribute(typeString, "regex");
+ xml.writeCharacters(v.toRegularExpression().pattern());
+ break;
+
+ default:
+ if (type == qMetaTypeId<qfloat16>()) {
+ xml.writeAttribute(typeString, "number");
+ xml.writeCharacters(QString::number(float(v.value<qfloat16>())));
+ } else if (type == qMetaTypeId<QCborSimpleType>()) {
+ xml.writeAttribute(typeString, "CBOR simple type");
+ xml.writeCharacters(QString::number(int(v.value<QCborSimpleType>())));
+ } else {
+ // does this convert to string?
+ const char *typeName = v.typeName();
+ QVariant copy = v;
+ if (copy.convert(QVariant::String)) {
+ xml.writeAttribute(typeString, QString::fromLatin1(typeName));
+ xml.writeCharacters(copy.toString());
+ } else {
+ fprintf(stderr, "XML: don't know how to serialize type '%s'.\n", typeName);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ xml.writeEndElement();
+ }
+}
+
+QString XmlConverter::name()
+{
+ return QStringLiteral("xml");
+}
+
+Converter::Direction XmlConverter::directions()
+{
+ return InOut;
+}
+
+Converter::Options XmlConverter::outputOptions()
+{
+ return SupportsArbitraryMapKeys;
+}
+
+const char *XmlConverter::optionsHelp()
+{
+ return optionHelp;
+}
+
+bool XmlConverter::probeFile(QIODevice *f)
+{
+ if (QFile *file = qobject_cast<QFile *>(f)) {
+ if (file->fileName().endsWith(QLatin1String(".xml")))
+ return true;
+ }
+
+ return f->isReadable() && f->peek(5) == "<?xml";
+}
+
+QVariant XmlConverter::loadFile(QIODevice *f, Converter *&outputConverter)
+{
+ if (!outputConverter)
+ outputConverter = this;
+
+ QXmlStreamReader xml(f);
+ xml.readNextStartElement();
+ QVariant v = variantFromXml(xml, outputConverter->outputOptions());
+ if (xml.hasError()) {
+ fprintf(stderr, "XML error: %s", qPrintable(xml.errorString()));
+ exit(EXIT_FAILURE);
+ }
+
+ return v;
+}
+
+void XmlConverter::saveFile(QIODevice *f, const QVariant &contents, const QStringList &options)
+{
+ bool compact = false;
+ for (const QString &s : options) {
+ if (s == QLatin1String("compact=no")) {
+ compact = false;
+ } else if (s == QLatin1String("compact=yes")) {
+ compact = true;
+ } else {
+ fprintf(stderr, "Unknown option '%s' to XML output. Valid options are:\n%s", qPrintable(s), optionHelp);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ QXmlStreamWriter xml(f);
+ xml.setAutoFormatting(!compact);
+ xml.writeStartDocument();
+ variantToXml(xml, contents);
+ xml.writeEndDocument();
+}
diff --git a/examples/corelib/serialization/convert/xmlconverter.h b/examples/corelib/serialization/convert/xmlconverter.h
new file mode 100644
index 0000000000..8fc0fea592
--- /dev/null
+++ b/examples/corelib/serialization/convert/xmlconverter.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef XMLCONVERTER_H
+#define XMLCONVERTER_H
+
+#include "converter.h"
+
+class XmlConverter : public Converter
+{
+ // Converter interface
+public:
+ QString name() override;
+ Direction directions() override;
+ Options outputOptions() override;
+ const char *optionsHelp() override;
+ bool probeFile(QIODevice *f) override;
+ QVariant loadFile(QIODevice *f, Converter *&outputConverter) override;
+ void saveFile(QIODevice *f, const QVariant &contents, const QStringList &options) override;
+};
+
+#endif // XMLCONVERTER_H
diff --git a/examples/corelib/serialization/serialization.pro b/examples/corelib/serialization/serialization.pro
index af4d3e6f0f..7651444f19 100644
--- a/examples/corelib/serialization/serialization.pro
+++ b/examples/corelib/serialization/serialization.pro
@@ -1,2 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS = savegame
+SUBDIRS = \
+ cbordump \
+ convert \
+ savegame
diff --git a/examples/corelib/threads/queuedcustomtype/window.cpp b/examples/corelib/threads/queuedcustomtype/window.cpp
index 7b9004d868..2cefba1e17 100644
--- a/examples/corelib/threads/queuedcustomtype/window.cpp
+++ b/examples/corelib/threads/queuedcustomtype/window.cpp
@@ -108,9 +108,8 @@ void Window::loadImage()
void Window::loadImage(const QImage &image)
{
- QDesktopWidget desktop;
QImage useImage;
- QRect space = desktop.availableGeometry();
+ QRect space = QGuiApplication::primaryScreen()->availableGeometry();
if (image.width() > 0.75*space.width() || image.height() > 0.75*space.height())
useImage = image.scaled(0.75*space.width(), 0.75*space.height(),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
diff --git a/examples/examples.pro b/examples/examples.pro
index 4ec5ca60e2..077e5828a9 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -4,8 +4,7 @@ CONFIG += no_docs_target
SUBDIRS = \
corelib \
embedded \
- qpa \
- touch
+ qpa
qtHaveModule(dbus): SUBDIRS += dbus
qtHaveModule(network): SUBDIRS += network
diff --git a/examples/gui/doc/src/rasterwindow.qdoc b/examples/gui/doc/src/rasterwindow.qdoc
index 36612e1707..0c52a62b8e 100644
--- a/examples/gui/doc/src/rasterwindow.qdoc
+++ b/examples/gui/doc/src/rasterwindow.qdoc
@@ -99,9 +99,8 @@
The resize event is guaranteed to be called prior to the window
being shown on screen and will also be called whenever the window
- is resized while on screen. We use this to resize the back buffer
- and call renderNow() if we are visible to immediately update the
- visual representation of the window on screen.
+ is resized while on screen. We use this to resize the back buffer,
+ and defer rendering to the corresponding/following expose event.
\snippet rasterwindow/rasterwindow.cpp 3
diff --git a/examples/gui/rasterwindow/rasterwindow.cpp b/examples/gui/rasterwindow/rasterwindow.cpp
index 3eacd20145..4dd2ac25ca 100644
--- a/examples/gui/rasterwindow/rasterwindow.cpp
+++ b/examples/gui/rasterwindow/rasterwindow.cpp
@@ -83,8 +83,6 @@ void RasterWindow::renderLater()
void RasterWindow::resizeEvent(QResizeEvent *resizeEvent)
{
m_backingStore->resize(resizeEvent->size());
- if (isExposed())
- renderNow();
}
//! [5]
@@ -109,7 +107,7 @@ void RasterWindow::renderNow()
QPaintDevice *device = m_backingStore->paintDevice();
QPainter painter(device);
- painter.fillRect(0, 0, width(), height(), Qt::white);
+ painter.fillRect(0, 0, width(), height(), QGradient::NightFade);
render(&painter);
painter.end();
diff --git a/examples/network/doc/images/secureudpclient-example.png b/examples/network/doc/images/secureudpclient-example.png
new file mode 100644
index 0000000000..a566aa4ce5
--- /dev/null
+++ b/examples/network/doc/images/secureudpclient-example.png
Binary files differ
diff --git a/examples/network/doc/images/secureudpserver-example.png b/examples/network/doc/images/secureudpserver-example.png
new file mode 100644
index 0000000000..a117b02834
--- /dev/null
+++ b/examples/network/doc/images/secureudpserver-example.png
Binary files differ
diff --git a/examples/network/doc/src/secureudpclient.qdoc b/examples/network/doc/src/secureudpclient.qdoc
new file mode 100644
index 0000000000..dc8538cf85
--- /dev/null
+++ b/examples/network/doc/src/secureudpclient.qdoc
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example secureudpclient
+ \title DTLS client
+ \ingroup examples-network
+ \brief This example demonstrates how to implement client-side DTLS connections.
+
+ \image secureudpclient-example.png Screenshot of the DTLS client example.
+
+ \note The DTLS client example is intended to be run alongside the \l{secureudpserver}{DTLS server} example.
+
+ The example DTLS client can establish several DTLS connections to one
+ or many DTLS servers. A client-side DTLS connection is implemented by the
+ DtlsAssociation class. This class uses QUdpSocket to read and write datagrams
+ and QDtls for encryption:
+
+ \snippet secureudpclient/association.h 0
+
+ The constructor sets the minimal TLS configuration for the new DTLS connection,
+ and sets the address and the port of the server:
+
+ \dots
+ \snippet secureudpclient/association.cpp 1
+ \dots
+
+ The QDtls::handshakeTimeout() signal is connected to the handleTimeout() slot
+ to deal with packet loss and retransmission during the handshake phase:
+
+ \dots
+ \snippet secureudpclient/association.cpp 2
+ \dots
+
+ To ensure we receive only the datagrams from the server, we connect our UDP socket to the server:
+
+ \dots
+ \snippet secureudpclient/association.cpp 3
+ \dots
+
+ The QUdpSocket::readyRead() signal is connected to the readyRead() slot:
+
+ \dots
+ \snippet secureudpclient/association.cpp 13
+ \dots
+
+ When a secure connection to a server is established, a DtlsAssociation object
+ will be sending short ping messages to the server, using a timer:
+
+ \snippet secureudpclient/association.cpp 4
+
+ startHandshake() starts a handshake with the server:
+
+ \snippet secureudpclient/association.cpp 5
+
+ The readyRead() slot reads a datagram sent by the server:
+
+ \snippet secureudpclient/association.cpp 6
+
+ If the handshake was already completed, this datagram is decrypted:
+
+ \snippet secureudpclient/association.cpp 7
+
+ otherwise, we try to continue the handshake:
+
+ \snippet secureudpclient/association.cpp 8
+
+ When the handshake has completed, we send our first ping message:
+
+ \snippet secureudpclient/association.cpp 9
+
+ The pskRequired() slot provides the Pre-Shared Key (PSK) needed during the handshake
+ phase:
+
+ \snippet secureudpclient/association.cpp 14
+
+ \note For the sake of brevity, the definition of pskRequired() is oversimplified.
+ The documentation for the QSslPreSharedKeyAuthenticator class explains in detail
+ how this slot can be properly implemented.
+
+ pingTimeout() sends an encrypted message to the server:
+
+ \snippet secureudpclient/association.cpp 10
+
+ During the handshake phase the client must handle possible timeouts, which
+ can happen due to packet loss. The handshakeTimeout() slot retransmits
+ the handshake messages:
+
+ \snippet secureudpclient/association.cpp 11
+
+ Before a client connection is destroyed, its DTLS connection must be shut down:
+
+ \snippet secureudpclient/association.cpp 12
+
+ Error messages, informational messages, and decrypted responses from servers
+ are displayed by the UI:
+
+ \snippet secureudpclient/mainwindow.cpp 0
+*/
+
diff --git a/examples/network/doc/src/secureudpserver.qdoc b/examples/network/doc/src/secureudpserver.qdoc
new file mode 100644
index 0000000000..0857f7065f
--- /dev/null
+++ b/examples/network/doc/src/secureudpserver.qdoc
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example secureudpserver
+ \title DTLS server
+ \ingroup examples-network
+ \brief This examples demonstrates how to implement a simple DTLS server.
+
+ \image secureudpserver-example.png Screenshot of the DTLS server example.
+
+ \note The DTLS server example is intended to be run alongside the \l{secureudpclient}{DTLS client} example.
+
+ The server is implemented by the DtlsServer class. It uses QUdpSocket,
+ QDtlsClientVerifier, and QDtls to test each client's reachability, complete a handshake,
+ and read and write encrypted messages.
+
+ \snippet secureudpserver/server.h 0
+
+ The constructor connects the QUdpSocket::readyRead() signal to its
+ readyRead() slot and sets the minimal needed TLS configuration:
+
+ \snippet secureudpserver/server.cpp 1
+
+ \note The server is not using a certificate and is relying on Pre-Shared
+ Key (PSK) handshake.
+
+ listen() binds QUdpSocket:
+
+ \snippet secureudpserver/server.cpp 2
+
+ The readyRead() slot processes incoming datagrams:
+
+ \dots
+ \snippet secureudpserver/server.cpp 3
+ \dots
+
+ After extracting an address and a port number, the server first tests
+ if it's a datagram from an already known peer:
+
+ \dots
+ \snippet secureudpserver/server.cpp 4
+ \dots
+
+ If it is a new, unknown address and port, the datagram is processed as a
+ potential ClientHello message, sent by a DTLS client:
+
+ \dots
+ \snippet secureudpserver/server.cpp 5
+ \dots
+
+ If it's a known DTLS client, the server either decrypts the datagram:
+
+ \dots
+ \snippet secureudpserver/server.cpp 6
+ \dots
+
+ or continues a handshake with this peer:
+
+ \dots
+ \snippet secureudpserver/server.cpp 7
+ \dots
+
+ handleNewConnection() verifies it's a reachable DTLS client, or sends a
+ HelloVerifyRequest:
+
+ \snippet secureudpserver/server.cpp 8
+ \dots
+
+ If the new client was verified to be a reachable DTLS client, the server creates
+ and configures a new QDtls object, and starts a server-side handshake:
+
+ \dots
+ \snippet secureudpserver/server.cpp 9
+ \dots
+
+ doHandshake() progresses through the handshake phase:
+
+ \snippet secureudpserver/server.cpp 11
+
+ During the handshake phase, the QDtls::pskRequired() signal is emitted and
+ the pskRequired() slot provides the preshared key:
+
+ \snippet secureudpserver/server.cpp 13
+
+ \note For the sake of brevity, the definition of pskRequired() is oversimplified.
+ The documentation for the QSslPreSharedKeyAuthenticator class explains in detail
+ how this slot can be properly implemented.
+
+ After the handshake is completed for the network peer, an encrypted DTLS
+ connection is considered to be established and the server decrypts subsequent
+ datagrams, sent by the peer, by calling decryptDatagram(). The server also
+ sends an encrypted response to the peer:
+
+ \snippet secureudpserver/server.cpp 12
+
+ The server closes its DTLS connections by calling QDtls::shutdown():
+
+ \snippet secureudpserver/server.cpp 14
+
+ During its operation, the server reports errors, informational messages, and
+ decrypted datagrams, by emitting signals errorMessage(), warningMessage(),
+ infoMessage(), and datagramReceived(). These messages are logged by the server's
+ UI:
+
+ \snippet secureudpserver/mainwindow.cpp 0
+*/
diff --git a/examples/network/loopback/dialog.cpp b/examples/network/loopback/dialog.cpp
index 99317b9c77..b4e6b0fd5e 100644
--- a/examples/network/loopback/dialog.cpp
+++ b/examples/network/loopback/dialog.cpp
@@ -48,11 +48,11 @@
**
****************************************************************************/
-#include <QtWidgets>
-#include <QtNetwork>
-
#include "dialog.h"
+#include <QtNetwork>
+#include <QtWidgets>
+
static const int TotalBytes = 50 * 1024 * 1024;
static const int PayloadSize = 64 * 1024; // 64 KB
@@ -71,15 +71,15 @@ Dialog::Dialog(QWidget *parent)
buttonBox->addButton(startButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
- connect(startButton, SIGNAL(clicked()), this, SLOT(start()));
- connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
- connect(&tcpServer, SIGNAL(newConnection()),
- this, SLOT(acceptConnection()));
- connect(&tcpClient, SIGNAL(connected()), this, SLOT(startTransfer()));
- connect(&tcpClient, SIGNAL(bytesWritten(qint64)),
- this, SLOT(updateClientProgress(qint64)));
- connect(&tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(displayError(QAbstractSocket::SocketError)));
+ connect(startButton, &QAbstractButton::clicked, this, &Dialog::start);
+ connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
+ connect(&tcpServer, &QTcpServer::newConnection,
+ this, &Dialog::acceptConnection);
+ connect(&tcpClient, &QAbstractSocket::connected, this, &Dialog::startTransfer);
+ connect(&tcpClient, &QIODevice::bytesWritten,
+ this, &Dialog::updateClientProgress);
+ connect(&tcpClient, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
+ this, &Dialog::displayError);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(clientProgressBar);
@@ -124,10 +124,18 @@ void Dialog::start()
void Dialog::acceptConnection()
{
tcpServerConnection = tcpServer.nextPendingConnection();
- connect(tcpServerConnection, SIGNAL(readyRead()),
- this, SLOT(updateServerProgress()));
- connect(tcpServerConnection, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(displayError(QAbstractSocket::SocketError)));
+ if (!tcpServerConnection) {
+ serverStatusLabel->setText(tr("Error: got invalid pending connection!"));
+ return;
+ }
+
+ connect(tcpServerConnection, &QIODevice::readyRead,
+ this, &Dialog::updateServerProgress);
+ connect(tcpServerConnection,
+ QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
+ this, &Dialog::displayError);
+ connect(tcpServerConnection, &QTcpSocket::disconnected,
+ tcpServerConnection, &QTcpSocket::deleteLater);
serverStatusLabel->setText(tr("Accepted connection"));
tcpServer.close();
@@ -136,13 +144,13 @@ void Dialog::acceptConnection()
void Dialog::startTransfer()
{
// called when the TCP client connected to the loopback server
- bytesToWrite = TotalBytes - (int)tcpClient.write(QByteArray(PayloadSize, '@'));
+ bytesToWrite = TotalBytes - int(tcpClient.write(QByteArray(PayloadSize, '@')));
clientStatusLabel->setText(tr("Connected"));
}
void Dialog::updateServerProgress()
{
- bytesReceived += (int)tcpServerConnection->bytesAvailable();
+ bytesReceived += int(tcpServerConnection->bytesAvailable());
tcpServerConnection->readAll();
serverProgressBar->setMaximum(TotalBytes);
@@ -161,17 +169,16 @@ void Dialog::updateServerProgress()
void Dialog::updateClientProgress(qint64 numBytes)
{
- // callen when the TCP client has written some bytes
- bytesWritten += (int)numBytes;
+ // called when the TCP client has written some bytes
+ bytesWritten += int(numBytes);
// only write more if not finished and when the Qt write buffer is below a certain size.
- if (bytesToWrite > 0 && tcpClient.bytesToWrite() <= 4*PayloadSize)
- bytesToWrite -= (int)tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@'));
+ if (bytesToWrite > 0 && tcpClient.bytesToWrite() <= 4 * PayloadSize)
+ bytesToWrite -= tcpClient.write(QByteArray(qMin(bytesToWrite, PayloadSize), '@'));
clientProgressBar->setMaximum(TotalBytes);
clientProgressBar->setValue(bytesWritten);
- clientStatusLabel->setText(tr("Sent %1MB")
- .arg(bytesWritten / (1024 * 1024)));
+ clientStatusLabel->setText(tr("Sent %1MB").arg(bytesWritten / (1024 * 1024)));
}
void Dialog::displayError(QAbstractSocket::SocketError socketError)
diff --git a/examples/network/loopback/dialog.h b/examples/network/loopback/dialog.h
index b23cfa030b..a70c20550a 100644
--- a/examples/network/loopback/dialog.h
+++ b/examples/network/loopback/dialog.h
@@ -60,9 +60,6 @@ class QDialogButtonBox;
class QLabel;
class QProgressBar;
class QPushButton;
-class QTcpServer;
-class QTcpSocket;
-class QAction;
QT_END_NAMESPACE
class Dialog : public QDialog
@@ -70,7 +67,7 @@ class Dialog : public QDialog
Q_OBJECT
public:
- Dialog(QWidget *parent = 0);
+ Dialog(QWidget *parent = nullptr);
public slots:
void start();
@@ -81,21 +78,21 @@ public slots:
void displayError(QAbstractSocket::SocketError socketError);
private:
- QProgressBar *clientProgressBar;
- QProgressBar *serverProgressBar;
- QLabel *clientStatusLabel;
- QLabel *serverStatusLabel;
+ QProgressBar *clientProgressBar = nullptr;
+ QProgressBar *serverProgressBar = nullptr;
+ QLabel *clientStatusLabel = nullptr;
+ QLabel *serverStatusLabel = nullptr;
- QPushButton *startButton;
- QPushButton *quitButton;
- QDialogButtonBox *buttonBox;
+ QPushButton *startButton = nullptr;
+ QPushButton *quitButton = nullptr;
+ QDialogButtonBox *buttonBox = nullptr;
QTcpServer tcpServer;
QTcpSocket tcpClient;
- QTcpSocket *tcpServerConnection;
- int bytesToWrite;
- int bytesWritten;
- int bytesReceived;
+ QTcpSocket *tcpServerConnection = nullptr;
+ int bytesToWrite = 0;
+ int bytesWritten = 0;
+ int bytesReceived = 0;
};
#endif
diff --git a/examples/network/loopback/main.cpp b/examples/network/loopback/main.cpp
index 9959c472f4..094a11d4fc 100644
--- a/examples/network/loopback/main.cpp
+++ b/examples/network/loopback/main.cpp
@@ -48,10 +48,10 @@
**
****************************************************************************/
-#include <QApplication>
-
#include "dialog.h"
+#include <QApplication>
+
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
diff --git a/examples/network/network-chat/client.cpp b/examples/network/network-chat/client.cpp
index c1eda52a0c..97c2c44b6b 100644
--- a/examples/network/network-chat/client.cpp
+++ b/examples/network/network-chat/client.cpp
@@ -78,7 +78,7 @@ void Client::sendMessage(const QString &message)
QString Client::nickName() const
{
- return QString(peerManager->userName()) + '@' + QHostInfo::localHostName()
+ return peerManager->userName() + '@' + QHostInfo::localHostName()
+ ':' + QString::number(server.serverPort());
}
diff --git a/examples/network/network-chat/connection.cpp b/examples/network/network-chat/connection.cpp
index 332d5dc56b..58cf67eb6d 100644
--- a/examples/network/network-chat/connection.cpp
+++ b/examples/network/network-chat/connection.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
@@ -55,17 +56,29 @@
static const int TransferTimeout = 30 * 1000;
static const int PongTimeout = 60 * 1000;
static const int PingInterval = 5 * 1000;
-static const char SeparatorToken = ' ';
+
+/*
+ * Protocol is defined as follows, using the CBOR Data Definition Language:
+ *
+ * protocol = [
+ * greeting, ; must start with a greeting command
+ * * command ; zero or more regular commands after
+ * ]
+ * command = plaintext / ping / pong / greeting
+ * plaintext = { 0 => text }
+ * ping = { 1 => null }
+ * pong = { 2 => null }
+ * greeting = { 3 => text }
+ */
Connection::Connection(QObject *parent)
- : QTcpSocket(parent)
+ : QTcpSocket(parent), writer(this)
{
greetingMessage = tr("undefined");
username = tr("unknown");
state = WaitingForGreeting;
currentDataType = Undefined;
- numBytesForCurrentDataType = -1;
- transferTimerId = 0;
+ transferTimerId = -1;
isGreetingMessageSent = false;
pingTimer.setInterval(PingInterval);
@@ -76,6 +89,22 @@ Connection::Connection(QObject *parent)
this, SLOT(sendGreetingMessage()));
}
+Connection::Connection(qintptr socketDescriptor, QObject *parent)
+ : Connection(parent)
+{
+ setSocketDescriptor(socketDescriptor);
+ reader.setDevice(this);
+}
+
+Connection::~Connection()
+{
+ if (isGreetingMessageSent) {
+ // Indicate clean shutdown.
+ writer.endArray();
+ waitForBytesWritten(2000);
+ }
+}
+
QString Connection::name() const
{
return username;
@@ -91,9 +120,11 @@ bool Connection::sendMessage(const QString &message)
if (message.isEmpty())
return false;
- QByteArray msg = message.toUtf8();
- QByteArray data = "MESSAGE " + QByteArray::number(msg.size()) + ' ' + msg;
- return write(data) == data.size();
+ writer.startMap(1);
+ writer.append(PlainText);
+ writer.append(message);
+ writer.endMap();
+ return true;
}
void Connection::timerEvent(QTimerEvent *timerEvent)
@@ -101,61 +132,75 @@ void Connection::timerEvent(QTimerEvent *timerEvent)
if (timerEvent->timerId() == transferTimerId) {
abort();
killTimer(transferTimerId);
- transferTimerId = 0;
+ transferTimerId = -1;
}
}
void Connection::processReadyRead()
{
- if (state == WaitingForGreeting) {
- if (!readProtocolHeader())
- return;
- if (currentDataType != Greeting) {
- abort();
- return;
+ // we've got more data, let's parse
+ reader.reparse();
+ while (reader.lastError() == QCborError::NoError) {
+ if (state == WaitingForGreeting) {
+ if (!reader.isArray())
+ break; // protocol error
+
+ reader.enterContainer(); // we'll be in this array forever
+ state = ReadingGreeting;
+ } else if (reader.containerDepth() == 1) {
+ // Current state: no command read
+ // Next state: read command ID
+ if (!reader.hasNext()) {
+ reader.leaveContainer();
+ disconnectFromHost();
+ return;
+ }
+
+ if (!reader.isMap() || !reader.isLengthKnown() || reader.length() != 1)
+ break; // protocol error
+ reader.enterContainer();
+ } else if (currentDataType == Undefined) {
+ // Current state: read command ID
+ // Next state: read command payload
+ if (!reader.isInteger())
+ break; // protocol error
+ currentDataType = DataType(reader.toInteger());
+ reader.next();
+ } else {
+ // Current state: read command payload
+ if (reader.isString()) {
+ auto r = reader.readString();
+ buffer += r.data;
+ if (r.status != QCborStreamReader::EndOfString)
+ continue;
+ } else if (reader.isNull()) {
+ reader.next();
+ } else {
+ break; // protocol error
+ }
+
+ // Next state: no command read
+ reader.leaveContainer();
+ if (transferTimerId != -1) {
+ killTimer(transferTimerId);
+ transferTimerId = -1;
+ }
+
+ if (state == ReadingGreeting) {
+ if (currentDataType != Greeting)
+ break; // protocol error
+ processGreeting();
+ } else {
+ processData();
+ }
}
- state = ReadingGreeting;
}
- if (state == ReadingGreeting) {
- if (!hasEnoughData())
- return;
-
- buffer = read(numBytesForCurrentDataType);
- if (buffer.size() != numBytesForCurrentDataType) {
- abort();
- return;
- }
-
- username = QString(buffer) + '@' + peerAddress().toString() + ':'
- + QString::number(peerPort());
- currentDataType = Undefined;
- numBytesForCurrentDataType = 0;
- buffer.clear();
-
- if (!isValid()) {
- abort();
- return;
- }
-
- if (!isGreetingMessageSent)
- sendGreetingMessage();
-
- pingTimer.start();
- pongTime.start();
- state = ReadyForUse;
- emit readyForUse();
- }
+ if (reader.lastError() != QCborError::EndOfFile)
+ abort(); // parse error
- do {
- if (currentDataType == Undefined) {
- if (!readProtocolHeader())
- return;
- }
- if (!hasEnoughData())
- return;
- processData();
- } while (bytesAvailable() > 0);
+ if (transferTimerId != -1 && reader.containerDepth() > 1)
+ transferTimerId = startTimer(TransferTimeout);
}
void Connection::sendPing()
@@ -165,112 +210,58 @@ void Connection::sendPing()
return;
}
- write("PING 1 p");
+ writer.startMap(1);
+ writer.append(Ping);
+ writer.append(nullptr); // no payload
+ writer.endMap();
}
void Connection::sendGreetingMessage()
{
- QByteArray greeting = greetingMessage.toUtf8();
- QByteArray data = "GREETING " + QByteArray::number(greeting.size()) + ' ' + greeting;
- if (write(data) == data.size())
- isGreetingMessageSent = true;
-}
+ writer.startArray(); // this array never ends
-int Connection::readDataIntoBuffer(int maxSize)
-{
- if (maxSize > MaxBufferSize)
- return 0;
+ writer.startMap(1);
+ writer.append(Greeting);
+ writer.append(greetingMessage);
+ writer.endMap();
+ isGreetingMessageSent = true;
- int numBytesBeforeRead = buffer.size();
- if (numBytesBeforeRead == MaxBufferSize) {
- abort();
- return 0;
- }
-
- while (bytesAvailable() > 0 && buffer.size() < maxSize) {
- buffer.append(read(1));
- if (buffer.endsWith(SeparatorToken))
- break;
- }
- return buffer.size() - numBytesBeforeRead;
+ if (!reader.device())
+ reader.setDevice(this);
}
-int Connection::dataLengthForCurrentDataType()
+void Connection::processGreeting()
{
- if (bytesAvailable() <= 0 || readDataIntoBuffer() <= 0
- || !buffer.endsWith(SeparatorToken))
- return 0;
-
- buffer.chop(1);
- int number = buffer.toInt();
+ username = buffer + '@' + peerAddress().toString() + ':'
+ + QString::number(peerPort());
+ currentDataType = Undefined;
buffer.clear();
- return number;
-}
-
-bool Connection::readProtocolHeader()
-{
- if (transferTimerId) {
- killTimer(transferTimerId);
- transferTimerId = 0;
- }
-
- if (readDataIntoBuffer() <= 0) {
- transferTimerId = startTimer(TransferTimeout);
- return false;
- }
- if (buffer == "PING ") {
- currentDataType = Ping;
- } else if (buffer == "PONG ") {
- currentDataType = Pong;
- } else if (buffer == "MESSAGE ") {
- currentDataType = PlainText;
- } else if (buffer == "GREETING ") {
- currentDataType = Greeting;
- } else {
- currentDataType = Undefined;
+ if (!isValid()) {
abort();
- return false;
- }
-
- buffer.clear();
- numBytesForCurrentDataType = dataLengthForCurrentDataType();
- return true;
-}
-
-bool Connection::hasEnoughData()
-{
- if (transferTimerId) {
- QObject::killTimer(transferTimerId);
- transferTimerId = 0;
+ return;
}
- if (numBytesForCurrentDataType <= 0)
- numBytesForCurrentDataType = dataLengthForCurrentDataType();
-
- if (bytesAvailable() < numBytesForCurrentDataType
- || numBytesForCurrentDataType <= 0) {
- transferTimerId = startTimer(TransferTimeout);
- return false;
- }
+ if (!isGreetingMessageSent)
+ sendGreetingMessage();
- return true;
+ pingTimer.start();
+ pongTime.start();
+ state = ReadyForUse;
+ emit readyForUse();
}
void Connection::processData()
{
- buffer = read(numBytesForCurrentDataType);
- if (buffer.size() != numBytesForCurrentDataType) {
- abort();
- return;
- }
-
switch (currentDataType) {
case PlainText:
- emit newMessage(username, QString::fromUtf8(buffer));
+ emit newMessage(username, buffer);
break;
case Ping:
- write("PONG 1 p");
+ writer.startMap(1);
+ writer.append(Pong);
+ writer.append(nullptr); // no payload
+ writer.endMap();
break;
case Pong:
pongTime.restart();
@@ -280,6 +271,5 @@ void Connection::processData()
}
currentDataType = Undefined;
- numBytesForCurrentDataType = 0;
buffer.clear();
}
diff --git a/examples/network/network-chat/connection.h b/examples/network/network-chat/connection.h
index 67d25d4d1e..fa0671a522 100644
--- a/examples/network/network-chat/connection.h
+++ b/examples/network/network-chat/connection.h
@@ -51,10 +51,12 @@
#ifndef CONNECTION_H
#define CONNECTION_H
+#include <QCborStreamReader>
+#include <QCborStreamWriter>
+#include <QElapsedTimer>
#include <QHostAddress>
#include <QString>
#include <QTcpSocket>
-#include <QTime>
#include <QTimer>
static const int MaxBufferSize = 1024000;
@@ -78,6 +80,8 @@ public:
};
Connection(QObject *parent = 0);
+ Connection(qintptr socketDescriptor, QObject *parent = 0);
+ ~Connection();
QString name() const;
void setGreetingMessage(const QString &message);
@@ -96,20 +100,19 @@ private slots:
void sendGreetingMessage();
private:
- int readDataIntoBuffer(int maxSize = MaxBufferSize);
- int dataLengthForCurrentDataType();
- bool readProtocolHeader();
bool hasEnoughData();
+ void processGreeting();
void processData();
+ QCborStreamReader reader;
+ QCborStreamWriter writer;
QString greetingMessage;
QString username;
QTimer pingTimer;
- QTime pongTime;
- QByteArray buffer;
+ QElapsedTimer pongTime;
+ QString buffer;
ConnectionState state;
DataType currentDataType;
- int numBytesForCurrentDataType;
int transferTimerId;
bool isGreetingMessageSent;
};
diff --git a/examples/network/network-chat/peermanager.cpp b/examples/network/network-chat/peermanager.cpp
index c70cc5e56d..38fa2e8e50 100644
--- a/examples/network/network-chat/peermanager.cpp
+++ b/examples/network/network-chat/peermanager.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
@@ -62,16 +63,14 @@ PeerManager::PeerManager(Client *client)
{
this->client = client;
- QStringList envVariables;
- envVariables << "USERNAME" << "USER" << "USERDOMAIN"
- << "HOSTNAME" << "DOMAINNAME";
+ static const char *envVariables[] = {
+ "USERNAME", "USER", "USERDOMAIN", "HOSTNAME", "DOMAINNAME"
+ };
- QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
- foreach (QString string, envVariables) {
- if (environment.contains(string)) {
- username = environment.value(string).toUtf8();
+ for (const char *varname : envVariables) {
+ username = qEnvironmentVariable(varname);
+ if (!username.isNull())
break;
- }
}
if (username.isEmpty())
@@ -95,7 +94,7 @@ void PeerManager::setServerPort(int port)
serverPort = port;
}
-QByteArray PeerManager::userName() const
+QString PeerManager::userName() const
{
return username;
}
@@ -108,7 +107,7 @@ void PeerManager::startBroadcasting()
bool PeerManager::isLocalHostAddress(const QHostAddress &address)
{
foreach (QHostAddress localAddress, ipAddresses) {
- if (address == localAddress)
+ if (address.isEqual(localAddress))
return true;
}
return false;
@@ -116,9 +115,14 @@ bool PeerManager::isLocalHostAddress(const QHostAddress &address)
void PeerManager::sendBroadcastDatagram()
{
- QByteArray datagram(username);
- datagram.append('@');
- datagram.append(QByteArray::number(serverPort));
+ QByteArray datagram;
+ {
+ QCborStreamWriter writer(&datagram);
+ writer.startArray(2);
+ writer.append(username);
+ writer.append(serverPort);
+ writer.endArray();
+ }
bool validBroadcastAddresses = true;
foreach (QHostAddress address, broadcastAddresses) {
@@ -142,11 +146,27 @@ void PeerManager::readBroadcastDatagram()
&senderIp, &senderPort) == -1)
continue;
- QList<QByteArray> list = datagram.split('@');
- if (list.size() != 2)
- continue;
+ int senderServerPort;
+ {
+ // decode the datagram
+ QCborStreamReader reader(datagram);
+ if (reader.lastError() != QCborError::NoError || !reader.isArray())
+ continue;
+ if (!reader.isLengthKnown() || reader.length() != 2)
+ continue;
+
+ reader.enterContainer();
+ if (reader.lastError() != QCborError::NoError || !reader.isString())
+ continue;
+ while (reader.readString().status == QCborStreamReader::Ok) {
+ // we don't actually need the username right now
+ }
+
+ if (reader.lastError() != QCborError::NoError || !reader.isUnsignedInteger())
+ continue;
+ senderServerPort = reader.toInteger();
+ }
- int senderServerPort = list.at(1).toInt();
if (isLocalHostAddress(senderIp) && senderServerPort == serverPort)
continue;
diff --git a/examples/network/network-chat/peermanager.h b/examples/network/network-chat/peermanager.h
index 0bcd67579c..b79028235b 100644
--- a/examples/network/network-chat/peermanager.h
+++ b/examples/network/network-chat/peermanager.h
@@ -68,7 +68,7 @@ public:
PeerManager(Client *client);
void setServerPort(int port);
- QByteArray userName() const;
+ QString userName() const;
void startBroadcasting();
bool isLocalHostAddress(const QHostAddress &address);
@@ -87,7 +87,7 @@ private:
QList<QHostAddress> ipAddresses;
QUdpSocket broadcastSocket;
QTimer broadcastTimer;
- QByteArray username;
+ QString username;
int serverPort;
};
diff --git a/examples/network/network-chat/server.cpp b/examples/network/network-chat/server.cpp
index cc154728d5..b3e4a07f60 100644
--- a/examples/network/network-chat/server.cpp
+++ b/examples/network/network-chat/server.cpp
@@ -61,7 +61,6 @@ Server::Server(QObject *parent)
void Server::incomingConnection(qintptr socketDescriptor)
{
- Connection *connection = new Connection(this);
- connection->setSocketDescriptor(socketDescriptor);
+ Connection *connection = new Connection(socketDescriptor, this);
emit newConnection(connection);
}
diff --git a/examples/network/network.pro b/examples/network/network.pro
index d64b16c760..1556e26ab1 100644
--- a/examples/network/network.pro
+++ b/examples/network/network.pro
@@ -30,6 +30,7 @@ qtHaveModule(widgets) {
}
qtConfig(openssl): SUBDIRS += securesocketclient
+ qtConfig(dtls): SUBDIRS += secureudpserver secureudpclient
qtConfig(sctp): SUBDIRS += multistreamserver multistreamclient
}
diff --git a/examples/network/secureudpclient/addressdialog.cpp b/examples/network/secureudpclient/addressdialog.cpp
new file mode 100644
index 0000000000..ccb58c853c
--- /dev/null
+++ b/examples/network/secureudpclient/addressdialog.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "addressdialog.h"
+#include "ui_addressdialog.h"
+
+#include <QtCore>
+#include <QtNetwork>
+#include <QtWidgets>
+
+#include <limits>
+
+AddressDialog::AddressDialog(QWidget *parent)
+ : QDialog(parent),
+ ui(new Ui::AddressDialog)
+{
+ ui->setupUi(this);
+ setupHostSelector();
+ setupPortSelector();
+}
+
+AddressDialog::~AddressDialog()
+{
+ delete ui;
+}
+
+QString AddressDialog::remoteName() const
+{
+ if (ui->addressSelector->count())
+ return ui->addressSelector->currentText();
+ return {};
+}
+
+quint16 AddressDialog::remotePort() const
+{
+ return quint16(ui->portSelector->text().toUInt());
+}
+
+void AddressDialog::setupHostSelector()
+{
+ QString name(QHostInfo::localHostName());
+ if (!name.isEmpty()) {
+ ui->addressSelector->addItem(name);
+ const QString domain = QHostInfo::localDomainName();
+ if (!domain.isEmpty())
+ ui->addressSelector->addItem(name + QChar('.') + domain);
+ }
+
+ if (name != QStringLiteral("localhost"))
+ ui->addressSelector->addItem(QStringLiteral("localhost"));
+
+ const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
+ for (const QHostAddress &ipAddress : ipAddressesList) {
+ if (!ipAddress.isLoopback())
+ ui->addressSelector->addItem(ipAddress.toString());
+ }
+
+ ui->addressSelector->insertSeparator(ui->addressSelector->count());
+
+ for (const QHostAddress &ipAddress : ipAddressesList) {
+ if (ipAddress.isLoopback())
+ ui->addressSelector->addItem(ipAddress.toString());
+ }
+}
+
+void AddressDialog::setupPortSelector()
+{
+ ui->portSelector->setValidator(new QIntValidator(0, std::numeric_limits<quint16>::max(),
+ ui->portSelector));
+ ui->portSelector->setText(QStringLiteral("22334"));
+}
diff --git a/examples/network/secureudpclient/addressdialog.h b/examples/network/secureudpclient/addressdialog.h
new file mode 100644
index 0000000000..43792faa4b
--- /dev/null
+++ b/examples/network/secureudpclient/addressdialog.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef ADDRESSDIALOG_H
+#define ADDRESSDIALOG_H
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+
+namespace Ui {
+
+class AddressDialog;
+
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class AddressDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AddressDialog(QWidget *parent = nullptr);
+ ~AddressDialog();
+
+ QString remoteName() const;
+ quint16 remotePort() const;
+
+private:
+ void setupHostSelector();
+ void setupPortSelector();
+
+ Ui::AddressDialog *ui = nullptr;
+};
+
+#endif // ADDRESSDIALOG_H
diff --git a/examples/network/secureudpclient/addressdialog.ui b/examples/network/secureudpclient/addressdialog.ui
new file mode 100644
index 0000000000..a7d9bdc253
--- /dev/null
+++ b/examples/network/secureudpclient/addressdialog.ui
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddressDialog</class>
+ <widget class="QDialog" name="AddressDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>548</width>
+ <height>143</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Host info</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Host name (server's address):</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="addressSelector">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>320</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="frame">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Server port:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="portSelector">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>320</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AddressDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AddressDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/examples/network/secureudpclient/association.cpp b/examples/network/secureudpclient/association.cpp
new file mode 100644
index 0000000000..c950260078
--- /dev/null
+++ b/examples/network/secureudpclient/association.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "association.h"
+
+QT_BEGIN_NAMESPACE
+
+DtlsAssociation::DtlsAssociation(const QHostAddress &address, quint16 port,
+ const QString &connectionName)
+ : name(connectionName),
+ crypto(QSslSocket::SslClientMode)
+{
+ //! [1]
+ auto configuration = QSslConfiguration::defaultDtlsConfiguration();
+ configuration.setPeerVerifyMode(QSslSocket::VerifyNone);
+ crypto.setPeer(address, port);
+ crypto.setDtlsConfiguration(configuration);
+ //! [1]
+
+ //! [2]
+ connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout);
+ //! [2]
+ connect(&crypto, &QDtls::pskRequired, this, &DtlsAssociation::pskRequired);
+ //! [3]
+ socket.connectToHost(address.toString(), port);
+ //! [3]
+ //! [13]
+ connect(&socket, &QUdpSocket::readyRead, this, &DtlsAssociation::readyRead);
+ //! [13]
+ //! [4]
+ pingTimer.setInterval(5000);
+ connect(&pingTimer, &QTimer::timeout, this, &DtlsAssociation::pingTimeout);
+ //! [4]
+}
+
+//! [12]
+DtlsAssociation::~DtlsAssociation()
+{
+ if (crypto.isConnectionEncrypted())
+ crypto.shutdown(&socket);
+}
+//! [12]
+
+//! [5]
+void DtlsAssociation::startHandshake()
+{
+ if (socket.state() != QAbstractSocket::ConnectedState) {
+ emit infoMessage(tr("%1: connecting UDP socket first ...").arg(name));
+ connect(&socket, &QAbstractSocket::connected, this, &DtlsAssociation::udpSocketConnected);
+ return;
+ }
+
+ if (!crypto.doHandshake(&socket))
+ emit errorMessage(tr("%1: failed to start a handshake - %2").arg(name, crypto.dtlsErrorString()));
+ else
+ emit infoMessage(tr("%1: starting a handshake").arg(name));
+}
+//! [5]
+
+void DtlsAssociation::udpSocketConnected()
+{
+ emit infoMessage(tr("%1: UDP socket is now in ConnectedState, continue with handshake ...").arg(name));
+ startHandshake();
+}
+
+void DtlsAssociation::readyRead()
+{
+ //! [6]
+ QByteArray dgram(socket.pendingDatagramSize(), Qt::Uninitialized);
+ const qint64 bytesRead = socket.readDatagram(dgram.data(), dgram.size());
+ if (bytesRead <= 0) {
+ emit warningMessage(tr("%1: spurious read notification?").arg(name));
+ return;
+ }
+
+ dgram.resize(bytesRead);
+ //! [6]
+ //! [7]
+ if (crypto.isConnectionEncrypted()) {
+ const QByteArray plainText = crypto.decryptDatagram(&socket, dgram);
+ if (plainText.size()) {
+ emit serverResponse(name, dgram, plainText);
+ return;
+ }
+
+ if (crypto.dtlsError() == QDtlsError::RemoteClosedConnectionError) {
+ emit errorMessage(tr("%1: shutdown alert received").arg(name));
+ socket.close();
+ pingTimer.stop();
+ return;
+ }
+
+ emit warningMessage(tr("%1: zero-length datagram received?").arg(name));
+ } else {
+ //! [7]
+ //! [8]
+ if (!crypto.doHandshake(&socket, dgram)) {
+ emit errorMessage(tr("%1: handshake error - %2").arg(name, crypto.dtlsErrorString()));
+ return;
+ }
+ //! [8]
+
+ //! [9]
+ if (crypto.isConnectionEncrypted()) {
+ emit infoMessage(tr("%1: encrypted connection established!").arg(name));
+ pingTimer.start();
+ pingTimeout();
+ } else {
+ //! [9]
+ emit infoMessage(tr("%1: continuing with handshake ...").arg(name));
+ }
+ }
+}
+
+//! [11]
+void DtlsAssociation::handshakeTimeout()
+{
+ emit warningMessage(tr("%1: handshake timeout, trying to re-transmit").arg(name));
+ if (!crypto.handleTimeout(&socket))
+ emit errorMessage(tr("%1: failed to re-transmit - %2").arg(name, crypto.dtlsErrorString()));
+}
+//! [11]
+
+//! [14]
+void DtlsAssociation::pskRequired(QSslPreSharedKeyAuthenticator *auth)
+{
+ Q_ASSERT(auth);
+
+ emit infoMessage(tr("%1: providing pre-shared key ...").arg(name));
+ auth->setIdentity(name.toLatin1());
+ auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f"));
+}
+//! [14]
+
+//! [10]
+void DtlsAssociation::pingTimeout()
+{
+ static const QString message = QStringLiteral("I am %1, please, accept our ping %2");
+ const qint64 written = crypto.writeDatagramEncrypted(&socket, message.arg(name).arg(ping).toLatin1());
+ if (written <= 0) {
+ emit errorMessage(tr("%1: failed to send a ping - %2").arg(name, crypto.dtlsErrorString()));
+ pingTimer.stop();
+ return;
+ }
+
+ ++ping;
+}
+//! [10]
+
+QT_END_NAMESPACE
diff --git a/examples/network/secureudpclient/association.h b/examples/network/secureudpclient/association.h
new file mode 100644
index 0000000000..be89ce695e
--- /dev/null
+++ b/examples/network/secureudpclient/association.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef ASSOCIATION_H
+#define ASSOCIATION_H
+
+#include <QtNetwork>
+#include <QtCore>
+
+QT_BEGIN_NAMESPACE
+
+//! [0]
+class DtlsAssociation : public QObject
+{
+ Q_OBJECT
+
+public:
+ DtlsAssociation(const QHostAddress &address, quint16 port,
+ const QString &connectionName);
+ ~DtlsAssociation();
+ void startHandshake();
+
+signals:
+ void errorMessage(const QString &message);
+ void warningMessage(const QString &message);
+ void infoMessage(const QString &message);
+ void serverResponse(const QString &clientInfo, const QByteArray &datagraam,
+ const QByteArray &plainText);
+
+private slots:
+ void udpSocketConnected();
+ void readyRead();
+ void handshakeTimeout();
+ void pskRequired(QSslPreSharedKeyAuthenticator *auth);
+ void pingTimeout();
+
+private:
+ QString name;
+ QUdpSocket socket;
+ QDtls crypto;
+
+ QTimer pingTimer;
+ unsigned ping = 0;
+
+ Q_DISABLE_COPY(DtlsAssociation)
+};
+//! [0]
+
+QT_END_NAMESPACE
+
+#endif // ASSOCIATION_H
diff --git a/examples/network/secureudpclient/main.cpp b/examples/network/secureudpclient/main.cpp
new file mode 100644
index 0000000000..2cf35878f2
--- /dev/null
+++ b/examples/network/secureudpclient/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QT_USE_NAMESPACE
+
+ QApplication app(argc, argv);
+ MainWindow window;
+ window.show();
+
+ return app.exec();
+}
diff --git a/examples/network/secureudpclient/mainwindow.cpp b/examples/network/secureudpclient/mainwindow.cpp
new file mode 100644
index 0000000000..2fbf757c81
--- /dev/null
+++ b/examples/network/secureudpclient/mainwindow.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore>
+#include <QtNetwork>
+
+#include "addressdialog.h"
+#include "association.h"
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include <utility>
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent),
+ ui(new Ui::MainWindow),
+ nameTemplate(QStringLiteral("Alice (clone number %1)"))
+{
+ ui->setupUi(this);
+ updateUi();
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+//! [0]
+
+const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>"));
+
+void MainWindow::addErrorMessage(const QString &message)
+{
+ ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message));
+}
+
+void MainWindow::addWarningMessage(const QString &message)
+{
+ ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message));
+}
+
+void MainWindow::addInfoMessage(const QString &message)
+{
+ ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message));
+}
+
+void MainWindow::addServerResponse(const QString &clientInfo, const QByteArray &datagram,
+ const QByteArray &plainText)
+{
+ static const QString messageColor = QStringLiteral("DarkMagenta");
+ static const QString formatter = QStringLiteral("<br>---------------"
+ "<br>%1 received a DTLS datagram:<br> %2"
+ "<br>As plain text:<br> %3");
+
+ const QString html = formatter.arg(clientInfo, QString::fromUtf8(datagram.toHex(' ')),
+ QString::fromUtf8(plainText));
+ ui->serverMessages->insertHtml(colorizer.arg(messageColor, html));
+}
+
+//! [0]
+
+void MainWindow::on_connectButton_clicked()
+{
+ if (lookupId != -1) {
+ QHostInfo::abortHostLookup(lookupId);
+ lookupId = -1;
+ port = 0;
+ updateUi();
+ return;
+ }
+
+ AddressDialog dialog;
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ const QString hostName = dialog.remoteName();
+ if (hostName.isEmpty())
+ return addWarningMessage(tr("Host name or address required to connect"));
+
+ port = dialog.remotePort();
+ QHostAddress remoteAddress;
+ if (remoteAddress.setAddress(hostName))
+ return startNewConnection(remoteAddress);
+
+ addInfoMessage(tr("Looking up the host ..."));
+ lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo)));
+ updateUi();
+}
+
+void MainWindow::updateUi()
+{
+ ui->connectButton->setText(lookupId == -1 ? tr("Connect ...") : tr("Cancel lookup"));
+ ui->shutdownButton->setEnabled(connections.size() != 0);
+}
+
+void MainWindow::lookupFinished(const QHostInfo &hostInfo)
+{
+ if (hostInfo.lookupId() != lookupId)
+ return;
+
+ lookupId = -1;
+ updateUi();
+
+ if (hostInfo.error() != QHostInfo::NoError) {
+ addErrorMessage(hostInfo.errorString());
+ return;
+ }
+
+ const QList<QHostAddress> foundAddresses = hostInfo.addresses();
+ if (foundAddresses.empty()) {
+ addWarningMessage(tr("Host not found"));
+ return;
+ }
+
+ const auto remoteAddress = foundAddresses.at(0);
+ addInfoMessage(tr("Connecting to: %1").arg(remoteAddress.toString()));
+ startNewConnection(remoteAddress);
+}
+
+void MainWindow::startNewConnection(const QHostAddress &address)
+{
+ AssocPtr newConnection(new DtlsAssociation(address, port, nameTemplate.arg(nextId)));
+ connect(newConnection.data(), &DtlsAssociation::errorMessage, this, &MainWindow::addErrorMessage);
+ connect(newConnection.data(), &DtlsAssociation::warningMessage, this, &MainWindow::addWarningMessage);
+ connect(newConnection.data(), &DtlsAssociation::infoMessage, this, &MainWindow::addInfoMessage);
+ connect(newConnection.data(), &DtlsAssociation::serverResponse, this, &MainWindow::addServerResponse);
+ connections.push_back(std::move(newConnection));
+ connections.back()->startHandshake();
+ updateUi();
+
+ ++nextId;
+}
+
+void MainWindow::on_shutdownButton_clicked()
+{
+ connections.clear();
+ updateUi();
+}
diff --git a/examples/network/secureudpclient/mainwindow.h b/examples/network/secureudpclient/mainwindow.h
new file mode 100644
index 0000000000..0d443fd376
--- /dev/null
+++ b/examples/network/secureudpclient/mainwindow.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QSharedPointer>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+namespace Ui {
+
+class MainWindow;
+
+}
+
+class QHostAddress;
+class QHostInfo;
+
+class DtlsAssociation;
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ explicit MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+
+private slots:
+
+ void addErrorMessage(const QString &message);
+ void addWarningMessage(const QString &message);
+ void addInfoMessage(const QString &message);
+ void addServerResponse(const QString &clientInfo, const QByteArray &datagram,
+ const QByteArray &plainText);
+
+ void on_connectButton_clicked();
+ void on_shutdownButton_clicked();
+
+ void lookupFinished(const QHostInfo &hostInfo);
+
+private:
+ void updateUi();
+ void startNewConnection(const QHostAddress &address);
+
+ Ui::MainWindow *ui = nullptr;
+
+ using AssocPtr = QSharedPointer<DtlsAssociation>;
+ QVector<AssocPtr> connections;
+
+ QString nameTemplate;
+ unsigned nextId = 0;
+
+ quint16 port = 0;
+ int lookupId = -1;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/network/secureudpclient/mainwindow.ui b/examples/network/secureudpclient/mainwindow.ui
new file mode 100644
index 0000000000..59a31974ee
--- /dev/null
+++ b/examples/network/secureudpclient/mainwindow.ui
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1200</width>
+ <height>550</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>1200</width>
+ <height>550</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1200</width>
+ <height>550</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>DTLS client</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>590</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>DTLS info messages:</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <widget class="QTextEdit" name="clientMessages">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>570</width>
+ <height>360</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>570</width>
+ <height>360</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>570</width>
+ <height>360</height>
+ </size>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QPushButton" name="connectButton">
+ <property name="text">
+ <string>Connect ...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="shutdownButton">
+ <property name="text">
+ <string>Shutdown connections</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="minimumSize">
+ <size>
+ <width>580</width>
+ <height>490</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>580</width>
+ <height>490</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Received datagrams:</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <widget class="QTextEdit" name="serverMessages">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>560</width>
+ <height>450</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>560</width>
+ <height>450</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>560</width>
+ <height>450</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1200</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/network/secureudpclient/secureudpclient.pro b/examples/network/secureudpclient/secureudpclient.pro
new file mode 100644
index 0000000000..44e4200994
--- /dev/null
+++ b/examples/network/secureudpclient/secureudpclient.pro
@@ -0,0 +1,22 @@
+QT += widgets network
+
+TARGET = secureudpclient
+TEMPLATE = app
+
+SOURCES += \
+ main.cpp \
+ association.cpp \
+ mainwindow.cpp \
+ addressdialog.cpp
+
+HEADERS += \
+ association.h \
+ mainwindow.h \
+ addressdialog.h
+
+FORMS += \
+ mainwindow.ui \
+ addressdialog.ui
+
+target.path = $$[QT_INSTALL_EXAMPLES]/network/secureudpclient
+INSTALLS += target
diff --git a/examples/network/secureudpserver/main.cpp b/examples/network/secureudpserver/main.cpp
new file mode 100644
index 0000000000..1a29d9d7ec
--- /dev/null
+++ b/examples/network/secureudpserver/main.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+
+#include "mainwindow.h"
+
+int main(int argc, char *argv[])
+{
+ QT_USE_NAMESPACE
+
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+
+ return a.exec();
+}
diff --git a/examples/network/secureudpserver/mainwindow.cpp b/examples/network/secureudpserver/mainwindow.cpp
new file mode 100644
index 0000000000..ef1974c311
--- /dev/null
+++ b/examples/network/secureudpserver/mainwindow.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "nicselector.h"
+#include "ui_mainwindow.h"
+
+MainWindow::MainWindow()
+ : ui(new Ui::MainWindow)
+{
+ ui->setupUi(this);
+
+ connect(&server, &DtlsServer::errorMessage, this, &MainWindow::addErrorMessage);
+ connect(&server, &DtlsServer::warningMessage, this, &MainWindow::addWarningMessage);
+ connect(&server, &DtlsServer::infoMessage, this, &MainWindow::addInfoMessage);
+ connect(&server, &DtlsServer::datagramReceived, this, &MainWindow::addClientMessage);
+
+ updateUi();
+}
+
+MainWindow::~MainWindow()
+{
+ delete ui;
+}
+
+void MainWindow::on_startButton_clicked()
+{
+ if (!server.isListening()) {
+ NicSelector ipDialog;
+ if (ipDialog.exec() == QDialog::Accepted) {
+ const QHostAddress address = ipDialog.selectedIp();
+ const quint16 port = ipDialog.selectedPort();
+ if (address.isNull()) {
+ addErrorMessage(tr("Failed to start listening, no valid address/port"));
+ } else if (server.listen(address, port)) {
+ addInfoMessage(tr("Server is listening on address %1 and port %2")
+ .arg(address.toString())
+ .arg(port));
+ }
+ }
+ } else {
+ server.close();
+ addInfoMessage(tr("Server is not accepting new connections"));
+ }
+
+ updateUi();
+}
+
+void MainWindow::on_quitButton_clicked()
+{
+ QCoreApplication::exit(0);
+}
+
+void MainWindow::updateUi()
+{
+ server.isListening() ? ui->startButton->setText(tr("Stop listening"))
+ : ui->startButton->setText(tr("Start listening"));
+}
+
+//! [0]
+const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>"));
+
+void MainWindow::addErrorMessage(const QString &message)
+{
+ ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message));
+}
+
+void MainWindow::addWarningMessage(const QString &message)
+{
+ ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message));
+}
+
+void MainWindow::addInfoMessage(const QString &message)
+{
+ ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message));
+}
+
+void MainWindow::addClientMessage(const QString &peerInfo, const QByteArray &datagram,
+ const QByteArray &plainText)
+{
+ static const QString messageColor = QStringLiteral("DarkMagenta");
+ static const QString formatter = QStringLiteral("<br>---------------"
+ "<br>A message from %1"
+ "<br>DTLS datagram:<br> %2"
+ "<br>As plain text:<br> %3");
+
+ const QString html = formatter.arg(peerInfo, QString::fromUtf8(datagram.toHex(' ')),
+ QString::fromUtf8(plainText));
+ ui->messages->insertHtml(colorizer.arg(messageColor, html));
+}
+//! [0]
diff --git a/examples/network/secureudpserver/mainwindow.h b/examples/network/secureudpserver/mainwindow.h
new file mode 100644
index 0000000000..b39d984d50
--- /dev/null
+++ b/examples/network/secureudpserver/mainwindow.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "server.h"
+
+#include <QMainWindow>
+
+QT_BEGIN_NAMESPACE
+
+namespace Ui {
+class MainWindow;
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow();
+ ~MainWindow();
+
+private slots:
+ void addErrorMessage(const QString &message);
+ void addWarningMessage(const QString &message);
+ void addInfoMessage(const QString &message);
+ void addClientMessage(const QString &peerInfo, const QByteArray &datagram,
+ const QByteArray &plainText);
+
+ void on_startButton_clicked();
+ void on_quitButton_clicked();
+
+private:
+ void updateUi();
+
+ Ui::MainWindow *ui = nullptr;
+ DtlsServer server;
+};
+
+#endif // MAINWINDOW_H
diff --git a/examples/network/secureudpserver/mainwindow.ui b/examples/network/secureudpserver/mainwindow.ui
new file mode 100644
index 0000000000..e25e19921b
--- /dev/null
+++ b/examples/network/secureudpserver/mainwindow.ui
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1090</width>
+ <height>670</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>1090</width>
+ <height>670</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1090</width>
+ <height>670</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>DTLS server</string>
+ </property>
+ <widget class="QWidget" name="centralWidget">
+ <widget class="QWidget" name="layoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>20</x>
+ <y>20</y>
+ <width>1050</width>
+ <height>576</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QGroupBox" name="infoBox">
+ <property name="minimumSize">
+ <size>
+ <width>520</width>
+ <height>540</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>520</width>
+ <height>540</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Dtls server info:</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <widget class="QTextEdit" name="serverInfo">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>500</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>500</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>500</width>
+ <height>500</height>
+ </size>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="minimumSize">
+ <size>
+ <width>520</width>
+ <height>540</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>520</width>
+ <height>540</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Received messages:</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <widget class="QTextEdit" name="messages">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>500</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>500</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>500</width>
+ <height>500</height>
+ </size>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="startButton">
+ <property name="text">
+ <string>Start listening</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="quitButton">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <widget class="QMenuBar" name="menuBar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>1090</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QStatusBar" name="statusBar"/>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/examples/network/secureudpserver/nicselector.cpp b/examples/network/secureudpserver/nicselector.cpp
new file mode 100644
index 0000000000..ea26439d74
--- /dev/null
+++ b/examples/network/secureudpserver/nicselector.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <limits>
+
+#include <QtCore>
+#include <QtNetwork>
+
+#include "nicselector.h"
+#include "ui_nicselector.h"
+
+NicSelector::NicSelector(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::NicSelector)
+{
+ ui->setupUi(this);
+ auto portValidator = new QIntValidator(0, int(std::numeric_limits<quint16>::max()),
+ ui->portSelector);
+ ui->portSelector->setValidator(portValidator);
+ ui->portSelector->setText(QStringLiteral("22334"));
+
+ const QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
+ availableAddresses.reserve(ipAddressesList.size());
+ for (const QHostAddress &ip : ipAddressesList) {
+ if (ip != QHostAddress::LocalHost && ip.toIPv4Address()) {
+ availableAddresses.push_back(ip);
+ ui->ipSelector->addItem(ip.toString());
+ }
+ }
+}
+
+NicSelector::~NicSelector()
+{
+ delete ui;
+}
+
+QHostAddress NicSelector::selectedIp() const
+{
+ if (!availableAddresses.size())
+ return {};
+
+ return availableAddresses[ui->ipSelector->currentIndex()];
+}
+
+quint16 NicSelector::selectedPort() const
+{
+ return quint16(ui->portSelector->text().toUInt());
+}
diff --git a/examples/network/secureudpserver/nicselector.h b/examples/network/secureudpserver/nicselector.h
new file mode 100644
index 0000000000..7962a78318
--- /dev/null
+++ b/examples/network/secureudpserver/nicselector.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef NICSELECTOR_H
+#define NICSELECTOR_H
+
+#include <QDialog>
+#include <QHostAddress>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+namespace Ui {
+class NicSelector;
+}
+
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+class NicSelector : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit NicSelector(QWidget *parent = nullptr);
+ ~NicSelector();
+
+ QHostAddress selectedIp() const;
+ quint16 selectedPort() const;
+
+private:
+ Ui::NicSelector *ui = nullptr;
+ QVector<QHostAddress> availableAddresses;
+};
+
+#endif // NICSELECTOR_H
diff --git a/examples/network/secureudpserver/nicselector.ui b/examples/network/secureudpserver/nicselector.ui
new file mode 100644
index 0000000000..b0ba376b66
--- /dev/null
+++ b/examples/network/secureudpserver/nicselector.ui
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NicSelector</class>
+ <widget class="QDialog" name="NicSelector">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>373</width>
+ <height>213</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>IP and port</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Listen on address:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="ipSelector">
+ <property name="minimumSize">
+ <size>
+ <width>250</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetFixedSize</enum>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="portSelector">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>NicSelector</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>NicSelector</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/examples/network/secureudpserver/secureudpserver.pro b/examples/network/secureudpserver/secureudpserver.pro
new file mode 100644
index 0000000000..910655aae4
--- /dev/null
+++ b/examples/network/secureudpserver/secureudpserver.pro
@@ -0,0 +1,21 @@
+QT += widgets network
+
+TARGET = secureudpserver
+TEMPLATE = app
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp \
+ server.cpp \
+ nicselector.cpp
+
+HEADERS += \
+ mainwindow.h \
+ server.h \
+ nicselector.h
+
+FORMS = mainwindow.ui \
+ nicselector.ui
+
+target.path = $$[QT_INSTALL_EXAMPLES]/network/secureudpserver
+INSTALLS += target
diff --git a/examples/network/secureudpserver/server.cpp b/examples/network/secureudpserver/server.cpp
new file mode 100644
index 0000000000..0d83fa9b51
--- /dev/null
+++ b/examples/network/secureudpserver/server.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "server.h"
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+QString peer_info(const QHostAddress &address, quint16 port)
+{
+ const static QString info = QStringLiteral("(%1:%2)");
+ return info.arg(address.toString()).arg(port);
+}
+
+QString connection_info(QSharedPointer<QDtls> connection)
+{
+ QString info(DtlsServer::tr("Session cipher: "));
+ info += connection->sessionCipher().name();
+
+ info += DtlsServer::tr("; session protocol: ");
+ switch (connection->sessionProtocol()) {
+ case QSsl::DtlsV1_0:
+ info += DtlsServer::tr("DTLS 1.0.");
+ break;
+ case QSsl::DtlsV1_2:
+ info += DtlsServer::tr("DTLS 1.2.");
+ break;
+ case QSsl::DtlsV1_2OrLater:
+ info += DtlsServer::tr("DTLS 1.2 or later.");
+ break;
+ default:
+ info += DtlsServer::tr("Unknown protocol.");
+ }
+
+ return info;
+}
+
+} // unnamed namespace
+
+//! [1]
+DtlsServer::DtlsServer()
+{
+ connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead);
+
+ serverConfiguration = QSslConfiguration::defaultDtlsConfiguration();
+ serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server");
+ serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
+}
+//! [1]
+
+DtlsServer::~DtlsServer()
+{
+ shutdown();
+}
+
+//! [2]
+bool DtlsServer::listen(const QHostAddress &address, quint16 port)
+{
+ if (address != serverSocket.localAddress() || port != serverSocket.localPort()) {
+ shutdown();
+ listening = serverSocket.bind(address, port);
+ if (!listening)
+ emit errorMessage(serverSocket.errorString());
+ } else {
+ listening = true;
+ }
+
+ return listening;
+}
+//! [2]
+
+bool DtlsServer::isListening() const
+{
+ return listening;
+}
+
+void DtlsServer::close()
+{
+ listening = false;
+}
+
+void DtlsServer::readyRead()
+{
+ //! [3]
+ const qint64 bytesToRead = serverSocket.pendingDatagramSize();
+ if (bytesToRead <= 0) {
+ emit warningMessage(tr("A spurious read notification"));
+ return;
+ }
+
+ QByteArray dgram(bytesToRead, Qt::Uninitialized);
+ QHostAddress peerAddress;
+ quint16 peerPort = 0;
+ const qint64 bytesRead = serverSocket.readDatagram(dgram.data(), dgram.size(),
+ &peerAddress, &peerPort);
+ if (bytesRead <= 0) {
+ emit warningMessage(tr("Failed to read a datagram: ") + serverSocket.errorString());
+ return;
+ }
+
+ dgram.resize(bytesRead);
+ //! [3]
+ //! [4]
+ if (peerAddress.isNull() || !peerPort) {
+ emit warningMessage(tr("Failed to extract peer info (address, port)"));
+ return;
+ }
+
+ const auto client = std::find_if(knownClients.begin(), knownClients.end(),
+ [&](const DtlsConnection &connection){
+ return connection->peerAddress() == peerAddress
+ && connection->peerPort() == peerPort;
+ });
+ //! [4]
+
+ //! [5]
+ if (client == knownClients.end())
+ return handleNewConnection(peerAddress, peerPort, dgram);
+ //! [5]
+
+ //! [6]
+ if ((*client)->isConnectionEncrypted()) {
+ decryptDatagram(*client, dgram);
+ if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError)
+ knownClients.erase(client);
+ return;
+ }
+ //! [6]
+
+ //! [7]
+ doHandshake(*client, dgram);
+ //! [7]
+}
+
+//! [13]
+void DtlsServer::pskRequired(QSslPreSharedKeyAuthenticator *auth)
+{
+ Q_ASSERT(auth);
+
+ emit infoMessage(tr("PSK callback, received a client's identity: '%1'")
+ .arg(QString::fromLatin1(auth->identity())));
+ auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f"));
+}
+//! [13]
+
+//! [8]
+void DtlsServer::handleNewConnection(const QHostAddress &peerAddress,
+ quint16 peerPort, const QByteArray &clientHello)
+{
+ if (!listening)
+ return;
+
+ const QString peerInfo = peer_info(peerAddress, peerPort);
+ if (cookieSender.verifyClient(&serverSocket, clientHello, peerAddress, peerPort)) {
+ emit infoMessage(peerInfo + tr(": verified, starting a handshake"));
+ //! [8]
+ //! [9]
+ DtlsConnection newConnection(new QDtls(QSslSocket::SslServerMode));
+ newConnection->setDtlsConfiguration(serverConfiguration);
+ newConnection->setPeer(peerAddress, peerPort);
+ newConnection->connect(newConnection.data(), &QDtls::pskRequired,
+ this, &DtlsServer::pskRequired);
+ knownClients.push_back(newConnection);
+ doHandshake(newConnection, clientHello);
+ //! [9]
+ } else if (cookieSender.dtlsError() != QDtlsError::NoError) {
+ emit errorMessage(tr("DTLS error: ") + cookieSender.dtlsErrorString());
+ } else {
+ emit infoMessage(peerInfo + tr(": not verified yet"));
+ }
+}
+
+//! [11]
+void DtlsServer::doHandshake(DtlsConnection newConnection, const QByteArray &clientHello)
+{
+ const bool result = newConnection->doHandshake(&serverSocket, clientHello);
+ if (!result) {
+ emit errorMessage(newConnection->dtlsErrorString());
+ return;
+ }
+
+ const QString peerInfo = peer_info(newConnection->peerAddress(),
+ newConnection->peerPort());
+ switch (newConnection->handshakeState()) {
+ case QDtls::HandshakeInProgress:
+ emit infoMessage(peerInfo + tr(": handshake is in progress ..."));
+ break;
+ case QDtls::HandshakeComplete:
+ emit infoMessage(tr("Connection with %1 encrypted. %2")
+ .arg(peerInfo, connection_info(newConnection)));
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+}
+//! [11]
+
+//! [12]
+void DtlsServer::decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage)
+{
+ Q_ASSERT(connection->isConnectionEncrypted());
+
+ const QString peerInfo = peer_info(connection->peerAddress(), connection->peerPort());
+ const QByteArray dgram = connection->decryptDatagram(&serverSocket, clientMessage);
+ if (dgram.size()) {
+ emit datagramReceived(peerInfo, clientMessage, dgram);
+ connection->writeDatagramEncrypted(&serverSocket, tr("to %1: ACK").arg(peerInfo).toLatin1());
+ } else if (connection->dtlsError() == QDtlsError::NoError) {
+ emit warningMessage(peerInfo + ": " + tr("0 byte dgram, could be a re-connect attempt?"));
+ } else {
+ emit errorMessage(peerInfo + ": " + connection->dtlsErrorString());
+ }
+}
+//! [12]
+
+//! [14]
+void DtlsServer::shutdown()
+{
+ for (DtlsConnection &connection : knownClients)
+ connection->shutdown(&serverSocket);
+
+ knownClients.clear();
+ serverSocket.close();
+}
+//! [14]
+
+QT_END_NAMESPACE
diff --git a/examples/network/secureudpserver/server.h b/examples/network/secureudpserver/server.h
new file mode 100644
index 0000000000..b720368e7b
--- /dev/null
+++ b/examples/network/secureudpserver/server.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef SERVER_H
+#define SERVER_H
+
+#include <QtCore>
+#include <QtNetwork>
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+//! [0]
+class DtlsServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ DtlsServer();
+ ~DtlsServer();
+
+ bool listen(const QHostAddress &address, quint16 port);
+ bool isListening() const;
+ void close();
+
+signals:
+ void errorMessage(const QString &message);
+ void warningMessage(const QString &message);
+ void infoMessage(const QString &message);
+
+ void datagramReceived(const QString &peerInfo, const QByteArray &cipherText,
+ const QByteArray &plainText);
+
+private slots:
+ void readyRead();
+ void pskRequired(QSslPreSharedKeyAuthenticator *auth);
+
+private:
+ void handleNewConnection(const QHostAddress &peerAddress, quint16 peerPort,
+ const QByteArray &clientHello);
+
+ using DtlsConnection = QSharedPointer<QDtls>;
+ void doHandshake(DtlsConnection newConnection, const QByteArray &clientHello);
+ void decryptDatagram(DtlsConnection connection, const QByteArray &clientMessage);
+ void shutdown();
+
+ bool listening = false;
+ QUdpSocket serverSocket;
+
+ QSslConfiguration serverConfiguration;
+ QDtlsClientVerifier cookieSender;
+ QVector<DtlsConnection> knownClients;
+
+ Q_DISABLE_COPY(DtlsServer)
+};
+//! [0]
+
+QT_END_NAMESPACE
+
+#endif // SERVER_H
diff --git a/examples/opengl/contextinfo/widget.cpp b/examples/opengl/contextinfo/widget.cpp
index fc21983f38..a5d9e98bf8 100644
--- a/examples/opengl/contextinfo/widget.cpp
+++ b/examples/opengl/contextinfo/widget.cpp
@@ -252,7 +252,7 @@ Widget::Widget(QWidget *parent)
if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
str << " Qt::AA_UseSoftwareOpenGL";
if (QCoreApplication::testAttribute(Qt::AA_UseDesktopOpenGL))
- str << " Qt::AA_UseSoftwareOpenGL";
+ str << " Qt::AA_UseDesktopOpenGL";
layout->addWidget(new QLabel(description));
setLayout(layout);
diff --git a/examples/opengl/qopenglwindow/main.cpp b/examples/opengl/qopenglwindow/main.cpp
index 4f008b45a6..8932269ddf 100644
--- a/examples/opengl/qopenglwindow/main.cpp
+++ b/examples/opengl/qopenglwindow/main.cpp
@@ -92,7 +92,6 @@ private:
QMatrix4x4 m_view;
QMatrix4x4 m_model_triangle;
QMatrix4x4 m_model_text;
- QBrush m_brush;
FragmentToy m_fragment_toy;
QStaticText m_text_layout;
@@ -111,16 +110,12 @@ OpenGLWindow::OpenGLWindow()
, m_text_layout("The triangle and this text is rendered with QPainter")
, m_animate(true)
{
+ setGeometry(300, 300, 500, 500);
+
m_view.lookAt(QVector3D(3,1,1),
QVector3D(0,0,0),
QVector3D(0,1,0));
- QLinearGradient gradient(QPointF(-1,-1), QPointF(1,1));
- gradient.setColorAt(0, Qt::red);
- gradient.setColorAt(1, Qt::green);
-
- m_brush = QBrush(gradient);
-
setAnimating(m_animate);
}
@@ -134,11 +129,11 @@ void OpenGLWindow::paintGL()
QMatrix4x4 mvp = m_projection * m_view * m_model_triangle;
p.setTransform(mvp.toTransform(), true);
- p.fillPath(painterPathForTriangle(), m_brush);
+ p.fillPath(painterPathForTriangle(), QBrush(QGradient(QGradient::NightFade)));
QTransform text_transform = (m_window_painter_matrix * m_view * m_model_text).toTransform();
p.setTransform(text_transform, false);
- p.setPen(QPen(Qt::white));
+ p.setPen(QPen(Qt::black));
m_text_layout.prepare(text_transform);
qreal x = - (m_text_layout.size().width() / 2);
qreal y = 0;
@@ -197,7 +192,7 @@ int main(int argc, char **argv)
fmt.setDepthBufferSize(24);
fmt.setStencilBufferSize(8);
window.setFormat(fmt);
- window.showMaximized();
+ window.show();
return app.exec();
}
diff --git a/examples/vulkan/hellovulkantexture/hellovulkantexture.cpp b/examples/vulkan/hellovulkantexture/hellovulkantexture.cpp
index 67ae0ca5dc..257191ef4e 100644
--- a/examples/vulkan/hellovulkantexture/hellovulkantexture.cpp
+++ b/examples/vulkan/hellovulkantexture/hellovulkantexture.cpp
@@ -223,6 +223,16 @@ bool VulkanRenderer::createTextureImage(const QSize &size, VkImage *image, VkDev
VkMemoryRequirements memReq;
m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
+ if (!(memReq.memoryTypeBits & (1 << memIndex))) {
+ VkPhysicalDeviceMemoryProperties physDevMemProps;
+ m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps);
+ for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
+ if (!(memReq.memoryTypeBits & (1 << i)))
+ continue;
+ memIndex = i;
+ }
+ }
+
VkMemoryAllocateInfo allocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr,
@@ -294,12 +304,12 @@ void VulkanRenderer::ensureTexture()
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
- barrier.srcAccessMask = 0; // VK_ACCESS_HOST_WRITE_BIT ### no, keep validation layer happy (??)
+ barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.image = m_texImage;
m_devFuncs->vkCmdPipelineBarrier(cb,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr,
1, &barrier);
@@ -312,7 +322,7 @@ void VulkanRenderer::ensureTexture()
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.image = m_texStaging;
m_devFuncs->vkCmdPipelineBarrier(cb,
- VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr,
1, &barrier);
diff --git a/examples/widgets/doc/src/customsortfiltermodel.qdoc b/examples/widgets/doc/src/customsortfiltermodel.qdoc
index 6eab846e89..9f0d13dd83 100644
--- a/examples/widgets/doc/src/customsortfiltermodel.qdoc
+++ b/examples/widgets/doc/src/customsortfiltermodel.qdoc
@@ -121,14 +121,14 @@
\snippet itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp 6
- We use QRegExp to define a pattern for the addresses we are looking
- for. The QRegExp::indexIn() function attempts to find a match in
- the given string and returns the position of the first match, or
- -1 if there was no match. If the given string contains the
- pattern, we use QRegExp's \l {QRegExp::cap()}{cap()} function to
- retrieve the actual address. The \l {QRegExp::cap()}{cap()}
- function returns the text captured by the \e nth
- subexpression. The entire match has index 0 and the parenthesized
+ We use QRegularExpression to define a pattern for the addresses we
+ are looking for. The \l {QRegularExpression::match()}{match()} function
+ returns a QRegularExpressionMatch object which contains the result of
+ the matching. If there is a match,
+ \l {QRegularExpressionMatch::hasMatch()}{hasMatch()} returns true. The
+ result of the match can be retrieved with QRegularExpressionMatch's
+ \l {QRegularExpressionMatch::captured()}{captured()} function.
+ The entire match has index 0 and the parenthesized
subexpressions have indexes starting from 1 (excluding
non-capturing parentheses).
diff --git a/examples/widgets/doc/src/gradients.qdoc b/examples/widgets/doc/src/gradients.qdoc
index 936bd2b014..457e6f837a 100644
--- a/examples/widgets/doc/src/gradients.qdoc
+++ b/examples/widgets/doc/src/gradients.qdoc
@@ -51,7 +51,12 @@
gradient. You can move points, and add new ones, by clicking with the left
mouse button, and remove points by clicking with the right button.
- There are three default configurations available at the bottom of
+ There are three example configurations available at the bottom of
the page that are provided as suggestions on how a color table could be
configured.
+
+ Qt also provides a suite of named gradient presets. They are based on the
+ free WebGradients collection. Click on the name in the Presets box to show
+ the gradient. Use the arrow buttons to browse through the available
+ presets.
*/
diff --git a/examples/widgets/graphicsview/boxes/glextensions.h b/examples/widgets/graphicsview/boxes/glextensions.h
index ac73e61641..685700d866 100644
--- a/examples/widgets/graphicsview/boxes/glextensions.h
+++ b/examples/widgets/graphicsview/boxes/glextensions.h
@@ -78,18 +78,11 @@ glMapBuffer
glUnmapBuffer
*/
-#ifndef Q_OS_MAC
-# ifndef APIENTRYP
-# ifdef APIENTRY
-# define APIENTRYP APIENTRY *
-# else
-# define APIENTRY
-# define APIENTRYP *
-# endif
-# endif
-#else
+#ifndef APIENTRY
# define APIENTRY
-# define APIENTRYP *
+#endif
+#ifndef APIENTRYP
+# define APIENTRYP APIENTRY *
#endif
#ifndef GL_VERSION_1_2
diff --git a/examples/widgets/graphicsview/boxes/scene.cpp b/examples/widgets/graphicsview/boxes/scene.cpp
index f51cad99ac..1637c1f781 100644
--- a/examples/widgets/graphicsview/boxes/scene.cpp
+++ b/examples/widgets/graphicsview/boxes/scene.cpp
@@ -952,17 +952,17 @@ void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
return;
if (event->buttons() & Qt::LeftButton) {
- m_trackBalls[0].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[0].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
} else {
- m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
}
if (event->buttons() & Qt::RightButton) {
- m_trackBalls[1].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[1].move(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
} else {
- m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
}
if (event->buttons() & Qt::MidButton) {
@@ -980,12 +980,12 @@ void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event)
return;
if (event->buttons() & Qt::LeftButton) {
- m_trackBalls[0].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[0].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
}
if (event->buttons() & Qt::RightButton) {
- m_trackBalls[1].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[1].push(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
}
@@ -1002,12 +1002,12 @@ void Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
return;
if (event->button() == Qt::LeftButton) {
- m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[0].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
}
if (event->button() == Qt::RightButton) {
- m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugate());
+ m_trackBalls[1].release(pixelPosToViewPos(event->scenePos()), m_trackBalls[2].rotation().conjugated());
event->accept();
}
diff --git a/examples/widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp b/examples/widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp
index c93368b390..35426657d9 100644
--- a/examples/widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp
+++ b/examples/widgets/itemviews/customsortfiltermodel/mysortfilterproxymodel.cpp
@@ -101,15 +101,20 @@ bool MySortFilterProxyModel::lessThan(const QModelIndex &left,
if (leftData.type() == QVariant::DateTime) {
return leftData.toDateTime() < rightData.toDateTime();
} else {
- static QRegExp emailPattern("[\\w\\.]*@[\\w\\.]*)");
+ static const QRegularExpression emailPattern("[\\w\\.]*@[\\w\\.]*");
QString leftString = leftData.toString();
- if(left.column() == 1 && emailPattern.indexIn(leftString) != -1)
- leftString = emailPattern.cap(1);
-
+ if (left.column() == 1) {
+ const QRegularExpressionMatch match = emailPattern.match(leftString);
+ if (match.hasMatch())
+ leftString = match.captured(0);
+ }
QString rightString = rightData.toString();
- if(right.column() == 1 && emailPattern.indexIn(rightString) != -1)
- rightString = emailPattern.cap(1);
+ if (right.column() == 1) {
+ const QRegularExpressionMatch match = emailPattern.match(rightString);
+ if (match.hasMatch())
+ rightString = match.captured(0);
+ }
return QString::localeAwareCompare(leftString, rightString) < 0;
}
diff --git a/examples/widgets/mainwindows/mainwindow/mainwindow.cpp b/examples/widgets/mainwindows/mainwindow/mainwindow.cpp
index fe31207326..4ba180f312 100644
--- a/examples/widgets/mainwindows/mainwindow/mainwindow.cpp
+++ b/examples/widgets/mainwindows/mainwindow/mainwindow.cpp
@@ -97,6 +97,7 @@ MainWindow::MainWindow(const CustomSizeHintMap &customSizeHints,
QWidget *parent, Qt::WindowFlags flags)
: QMainWindow(parent, flags)
{
+ Q_UNUSED(message);
setObjectName("MainWindow");
setWindowTitle("Qt Main Window Example");
diff --git a/examples/widgets/painting/deform/pathdeform.cpp b/examples/widgets/painting/deform/pathdeform.cpp
index 490a8508d6..805804716f 100644
--- a/examples/widgets/painting/deform/pathdeform.cpp
+++ b/examples/widgets/painting/deform/pathdeform.cpp
@@ -50,7 +50,8 @@
#include "pathdeform.h"
-#include <QApplication>
+#include <QGuiApplication>
+#include <QScreen>
#include <QtDebug>
#include <QMouseEvent>
#include <QTimerEvent>
@@ -247,7 +248,7 @@ void PathDeformControls::layoutForSmallScreen()
deformSlider->setValue(80);
fontSizeSlider->setValue(120);
- QRect screen_size = QApplication::desktop()->screenGeometry();
+ QRect screen_size = QGuiApplication::primaryScreen()->geometry();
radiusSlider->setValue(qMin(screen_size.width(), screen_size.height())/5);
m_renderer->setText(tr("Qt"));
@@ -277,7 +278,7 @@ PathDeformWidget::PathDeformWidget(QWidget *parent, bool smallScreen)
connect(m_renderer, SIGNAL(clicked()), this, SLOT(showControls()));
connect(m_controls, SIGNAL(okPressed()), this, SLOT(hideControls()));
- connect(m_controls, SIGNAL(quitPressed()), QApplication::instance(), SLOT(quit()));
+ connect(m_controls, SIGNAL(quitPressed()), QCoreApplication::instance(), SLOT(quit()));
}
@@ -482,6 +483,10 @@ void PathDeformRenderer::timerEvent(QTimerEvent *e)
void PathDeformRenderer::mousePressEvent(QMouseEvent *e)
{
+ if (m_show_doc) {
+ setDescriptionEnabled(false);
+ return;
+ }
setDescriptionEnabled(false);
m_repaintTimer.stop();
diff --git a/examples/widgets/painting/gradients/gradients.cpp b/examples/widgets/painting/gradients/gradients.cpp
index 78c174a8bf..6d9f514a8d 100644
--- a/examples/widgets/painting/gradients/gradients.cpp
+++ b/examples/widgets/painting/gradients/gradients.cpp
@@ -301,8 +301,15 @@ GradientWidget::GradientWidget(QWidget *parent)
m_reflectSpreadButton = new QRadioButton(tr("Reflect Spread"), spreadGroup);
m_repeatSpreadButton = new QRadioButton(tr("Repeat Spread"), spreadGroup);
+ QGroupBox *presetsGroup = new QGroupBox(mainGroup);
+ presetsGroup->setTitle(tr("Presets"));
+ QPushButton *prevPresetButton = new QPushButton(tr("<"), presetsGroup);
+ m_presetButton = new QPushButton(tr("(unset)"), presetsGroup);
+ QPushButton *nextPresetButton = new QPushButton(tr(">"), presetsGroup);
+ updatePresetName();
+
QGroupBox *defaultsGroup = new QGroupBox(mainGroup);
- defaultsGroup->setTitle(tr("Defaults"));
+ defaultsGroup->setTitle(tr("Examples"));
QPushButton *default1Button = new QPushButton(tr("1"), defaultsGroup);
QPushButton *default2Button = new QPushButton(tr("2"), defaultsGroup);
QPushButton *default3Button = new QPushButton(tr("3"), defaultsGroup);
@@ -327,11 +334,12 @@ GradientWidget::GradientWidget(QWidget *parent)
mainLayout->addWidget(m_renderer);
mainLayout->addWidget(mainGroup);
- mainGroup->setFixedWidth(180);
+ mainGroup->setFixedWidth(200);
QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
mainGroupLayout->addWidget(editorGroup);
mainGroupLayout->addWidget(typeGroup);
mainGroupLayout->addWidget(spreadGroup);
+ mainGroupLayout->addWidget(presetsGroup);
mainGroupLayout->addWidget(defaultsGroup);
mainGroupLayout->addStretch(1);
mainGroupLayout->addWidget(showSourceButton);
@@ -353,6 +361,11 @@ GradientWidget::GradientWidget(QWidget *parent)
spreadGroupLayout->addWidget(m_repeatSpreadButton);
spreadGroupLayout->addWidget(m_reflectSpreadButton);
+ QHBoxLayout *presetsGroupLayout = new QHBoxLayout(presetsGroup);
+ presetsGroupLayout->addWidget(prevPresetButton);
+ presetsGroupLayout->addWidget(m_presetButton, 1);
+ presetsGroupLayout->addWidget(nextPresetButton);
+
QHBoxLayout *defaultsGroupLayout = new QHBoxLayout(defaultsGroup);
defaultsGroupLayout->addWidget(default1Button);
defaultsGroupLayout->addWidget(default2Button);
@@ -375,6 +388,13 @@ GradientWidget::GradientWidget(QWidget *parent)
connect(m_repeatSpreadButton, &QRadioButton::clicked,
m_renderer, &GradientRenderer::setRepeatSpread);
+ connect(prevPresetButton, &QPushButton::clicked,
+ this, &GradientWidget::setPrevPreset);
+ connect(m_presetButton, &QPushButton::clicked,
+ this, &GradientWidget::setPreset);
+ connect(nextPresetButton, &QPushButton::clicked,
+ this, &GradientWidget::setNextPreset);
+
connect(default1Button, &QPushButton::clicked,
this, &GradientWidget::setDefault1);
connect(default2Button, &QPushButton::clicked,
@@ -471,6 +491,40 @@ void GradientWidget::setDefault(int config)
m_renderer->setGradientStops(stops);
}
+void GradientWidget::updatePresetName()
+{
+ QMetaEnum presetEnum = QMetaEnum::fromType<QGradient::Preset>();
+ m_presetButton->setText(QLatin1String(presetEnum.key(m_presetIndex)));
+}
+
+void GradientWidget::changePresetBy(int indexOffset)
+{
+ QMetaEnum presetEnum = QMetaEnum::fromType<QGradient::Preset>();
+ m_presetIndex = qBound(0, m_presetIndex + indexOffset, presetEnum.keyCount() - 1);
+
+ QGradient::Preset preset = static_cast<QGradient::Preset>(presetEnum.value(m_presetIndex));
+ QGradient gradient(preset);
+ if (gradient.type() != QGradient::LinearGradient)
+ return;
+
+ QLinearGradient *linearGradientPointer = static_cast<QLinearGradient *>(&gradient);
+ QLineF objectStopsLine(linearGradientPointer->start(), linearGradientPointer->finalStop());
+ qreal scaleX = qFuzzyIsNull(objectStopsLine.dx()) ? 1.0 : (0.8 * m_renderer->width() / qAbs(objectStopsLine.dx()));
+ qreal scaleY = qFuzzyIsNull(objectStopsLine.dy()) ? 1.0 : (0.8 * m_renderer->height() / qAbs(objectStopsLine.dy()));
+ QLineF logicalStopsLine = QTransform::fromScale(scaleX, scaleY).map(objectStopsLine);
+ logicalStopsLine.translate(m_renderer->rect().center() - logicalStopsLine.center());
+ QPolygonF logicalStops;
+ logicalStops << logicalStopsLine.p1() << logicalStopsLine.p2();
+
+ m_linearButton->animateClick();
+ m_padSpreadButton->animateClick();
+ m_editor->setGradientStops(gradient.stops());
+ m_renderer->hoverPoints()->setPoints(logicalStops);
+ m_renderer->setGradientStops(gradient.stops());
+
+ updatePresetName();
+}
+
GradientRenderer::GradientRenderer(QWidget *parent)
: ArthurFrame(parent)
{
diff --git a/examples/widgets/painting/gradients/gradients.h b/examples/widgets/painting/gradients/gradients.h
index b4db298bb4..c6525d18f8 100644
--- a/examples/widgets/painting/gradients/gradients.h
+++ b/examples/widgets/painting/gradients/gradients.h
@@ -164,9 +164,14 @@ public slots:
void setDefault2() { setDefault(2); }
void setDefault3() { setDefault(3); }
void setDefault4() { setDefault(4); }
+ void setPreset() { changePresetBy(0); }
+ void setPrevPreset() { changePresetBy(-1); }
+ void setNextPreset() { changePresetBy(1); }
private:
void setDefault(int i);
+ void updatePresetName();
+ void changePresetBy(int indexOffset);
GradientRenderer *m_renderer;
GradientEditor *m_editor;
@@ -177,7 +182,9 @@ private:
QRadioButton *m_padSpreadButton;
QRadioButton *m_reflectSpreadButton;
QRadioButton *m_repeatSpreadButton;
+ QPushButton *m_presetButton;
+ int m_presetIndex = 0;
};
#endif // GRADIENTS_H
diff --git a/examples/widgets/painting/gradients/gradients.html b/examples/widgets/painting/gradients/gradients.html
index 1ea2c0ed6c..82c449035f 100644
--- a/examples/widgets/painting/gradients/gradients.html
+++ b/examples/widgets/painting/gradients/gradients.html
@@ -24,8 +24,12 @@ green and blue components while the last defines the alpha of the
gradient. You can move points, and add new ones, by clicking with the left
mouse button, and remove points by clicking with the right button.</p>
-<p>There are three default configurations available at the bottom of
+<p>There are three example configurations available at the bottom of
the page that are provided as suggestions on how a color table could be
configured.</p>
+<p>Qt also provides a suite of named gradient presets. They are based on the
+free WebGradients collection. Click on the name in the Presets box to show the
+gradient. Use the arrow buttons to browse through the available presets.</p>
+
</html>
diff --git a/examples/widgets/richtext/calendar/mainwindow.cpp b/examples/widgets/richtext/calendar/mainwindow.cpp
index 12c418e0b1..38dc0e2849 100644
--- a/examples/widgets/richtext/calendar/mainwindow.cpp
+++ b/examples/widgets/richtext/calendar/mainwindow.cpp
@@ -66,7 +66,7 @@ MainWindow::MainWindow()
QComboBox *monthCombo = new QComboBox;
for (int month = 1; month <= 12; ++month)
- monthCombo->addItem(QDate::longMonthName(month));
+ monthCombo->addItem(QLocale::system().monthName(month));
QDateTimeEdit *yearEdit = new QDateTimeEdit;
yearEdit->setDisplayFormat("yyyy");
@@ -168,7 +168,7 @@ void MainWindow::insertCalendar()
QTextTableCell cell = table->cellAt(0, weekDay-1);
//! [11] //! [12]
QTextCursor cellCursor = cell.firstCursorPosition();
- cellCursor.insertText(QString("%1").arg(QDate::longDayName(weekDay)), boldFormat);
+ cellCursor.insertText(QLocale::system().dayName(weekDay), boldFormat);
}
//! [12]
@@ -194,7 +194,7 @@ void MainWindow::insertCalendar()
cursor.endEditBlock();
//! [14]
setWindowTitle(tr("Calendar for %1 %2"
- ).arg(QDate::longMonthName(selectedDate.month())
+ ).arg(QLocale::system().monthName(selectedDate.month())
).arg(selectedDate.year()));
}
//! [14]
diff --git a/examples/widgets/richtext/textedit/example.html b/examples/widgets/richtext/textedit/example.html
index e3a56d1154..99090a697f 100644
--- a/examples/widgets/richtext/textedit/example.html
+++ b/examples/widgets/richtext/textedit/example.html
@@ -1,14 +1,14 @@
<html><head><meta name="qrichtext" content="1" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>QTextEdit Example</title><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;">
-<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600;">QTextEdit</span></p>
+<h1 align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt; font-weight:600;">QTextEdit</span></h1>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">The QTextEdit widget is an advanced editor that supports formatted rich text. It can be used to display HTML and other rich document formats. Internally, QTextEdit uses the QTextDocument class to describe both the high-level structure of each document and the low-level formatting of paragraphs.</span></p>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">If you are viewing this document in the <span style=" font-style:italic;">textedit</span> example, you can edit this document to explore Qt's rich text editing features. We have included some comments in each of the following sections to encourage you to experiment. </p>
-<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:18pt; font-weight:600;"><span style=" font-size:16pt;">Font and Paragraph Styles</span></p>
+<h2 style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:18pt; font-weight:600;"><span style=" font-size:16pt;">Font and Paragraph Styles</span></h2>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">QTextEdit supports </span><span style=" font-size:11pt; font-weight:600;">bold</span><span style=" font-size:11pt;">, </span><span style=" font-size:11pt; font-style:italic;">italic</span><span style=" font-size:11pt;">, and </span><span style=" font-size:11pt; text-decoration: underline;">underlined</span><span style=" font-size:11pt;"> font styles, and can display </span><span style=" font-size:11pt; font-weight:600; color:#00007f;">multicolored</span><span style=" font-size:11pt;"> </span><span style=" font-size:11pt; font-weight:600; color:#aa0000;">text</span><span style=" font-size:11pt;">. Font families such as </span><span style=" font-family:'Times New Roman'; font-size:11pt; font-weight:600;">Times New Roman</span><span style=" font-size:11pt;"> and </span><span style=" font-family:'Courier'; font-size:11pt; font-weight:600;">Courier</span><span style=" font-size:11pt;"> can also be used directly. </span><span style=" font-size:11pt; font-style:italic;">If you place the cursor in a region of styled text, the controls in the tool bars will change to reflect the current style.</span></p>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">Paragraphs can be formatted so that the text is left-aligned, right-aligned, centered, or fully justified.</p>
<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-style:italic;">Try changing the alignment of some text and resize the editor to see how the text layout changes.</span> </p>
-<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">Lists</span></p>
+<h2 align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:16pt; font-weight:600;">Lists</span></h2>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">Different kinds of lists can be included in rich text documents. Standard bullet lists can be nested, using different symbols for each level of the list: </span></p>
<ul style="-qt-list-indent: 1;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Disc symbols are typically used for top-level list items. </li></ul>
<ul type=circle style="-qt-list-indent: 2;"><li style=" font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Circle symbols can be used to distinguish between items in lower-level lists.</li></ul>
@@ -24,10 +24,10 @@ p, li { white-space: pre-wrap; }
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">The list will automatically be renumbered if you add or remove items. <span style=" font-style:italic;">Try adding new sections to the above list or removing existing item to see the numbers change.</span> </p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
-<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Images</span></p>
+<h2 style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Images</span></h2>
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">Inline images are treated like ordinary ranges of characters in the text editor, so they flow with the surrounding text. Images can also be selected in the same way as text, making it easy to cut, copy, and paste them. </span></p>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><img src=":/images/logo32.png" /><span style=" font-style:italic;"> Try to select this image by clicking and dragging over it with the mouse, or use the text cursor to select it by holding down Shift and using the arrow keys. You can then cut or copy it, and paste it into different parts of this document.</span></p>
-<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Tables</span></p>
+<h2 align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Tables</span></h2>
<p align="justify" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:16pt; font-weight:600;"><span style=" font-size:11pt; font-weight:400;">QTextEdit can arrange and format tables, supporting features such as row and column spans, text formatting within cells, and size constraints for columns. </span></p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
@@ -72,8 +72,8 @@ p, li { white-space: pre-wrap; }
<td></td></tr></table>
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"></p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt; font-style:italic;">Try adding text to the cells in the table and experiment with the alignment of the paragraphs.</p>
-<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Hyperlinks</span></p>
+<h2 style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Hyperlinks</span></h2>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt;">QTextEdit is designed to support hyperlinks between documents, and this feature is used extensively in </span><span style=" font-size:11pt; font-style:italic;">Qt Assistant</span><span style=" font-size:11pt;">. Hyperlinks are automatically created when an HTML file is imported into an editor. Since the rich text framework supports hyperlinks natively, they can also be created programatically.</span></p>
-<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Undo and Redo</span></p>
+<h2 style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-size:16pt; font-weight:600;">Undo and Redo</span></h2>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;">Full support for undo and redo operations is built into QTextEdit and the underlying rich text framework. Operations on a document can be packaged together to make editing a more comfortable experience for the user.</p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><span style=" font-style:italic;">Try making changes to this document and press Ctrl+Z to undo them. You can always recover the original contents of the document.</span> </p></body></html>
diff --git a/examples/widgets/richtext/textedit/textedit.cpp b/examples/widgets/richtext/textedit/textedit.cpp
index fe4ee4f499..0d51ce61ad 100644
--- a/examples/widgets/richtext/textedit/textedit.cpp
+++ b/examples/widgets/richtext/textedit/textedit.cpp
@@ -141,13 +141,23 @@ TextEdit::TextEdit(QWidget *parent)
#ifndef QT_NO_CLIPBOARD
actionCut->setEnabled(false);
+ connect(textEdit, &QTextEdit::copyAvailable, actionCut, &QAction::setEnabled);
actionCopy->setEnabled(false);
+ connect(textEdit, &QTextEdit::copyAvailable, actionCopy, &QAction::setEnabled);
connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &TextEdit::clipboardDataChanged);
#endif
textEdit->setFocus();
setCurrentFileName(QString());
+
+#ifdef Q_OS_MACOS
+ // Use dark text on light background on macOS, also in dark mode.
+ QPalette pal = textEdit->palette();
+ pal.setColor(QPalette::Base, QColor(Qt::white));
+ pal.setColor(QPalette::Text, QColor(Qt::black));
+ textEdit->setPalette(pal);
+#endif
}
void TextEdit::closeEvent(QCloseEvent *e)
@@ -348,6 +358,12 @@ void TextEdit::setupTextActions()
comboStyle->addItem("Ordered List (Alpha upper)");
comboStyle->addItem("Ordered List (Roman lower)");
comboStyle->addItem("Ordered List (Roman upper)");
+ comboStyle->addItem("Heading 1");
+ comboStyle->addItem("Heading 2");
+ comboStyle->addItem("Heading 3");
+ comboStyle->addItem("Heading 4");
+ comboStyle->addItem("Heading 5");
+ comboStyle->addItem("Heading 6");
connect(comboStyle, QOverload<int>::of(&QComboBox::activated), this, &TextEdit::textStyle);
@@ -575,44 +591,56 @@ void TextEdit::textSize(const QString &p)
void TextEdit::textStyle(int styleIndex)
{
QTextCursor cursor = textEdit->textCursor();
+ QTextListFormat::Style style = QTextListFormat::ListStyleUndefined;
+
+ switch (styleIndex) {
+ case 1:
+ style = QTextListFormat::ListDisc;
+ break;
+ case 2:
+ style = QTextListFormat::ListCircle;
+ break;
+ case 3:
+ style = QTextListFormat::ListSquare;
+ break;
+ case 4:
+ style = QTextListFormat::ListDecimal;
+ break;
+ case 5:
+ style = QTextListFormat::ListLowerAlpha;
+ break;
+ case 6:
+ style = QTextListFormat::ListUpperAlpha;
+ break;
+ case 7:
+ style = QTextListFormat::ListLowerRoman;
+ break;
+ case 8:
+ style = QTextListFormat::ListUpperRoman;
+ break;
+ default:
+ break;
+ }
- if (styleIndex != 0) {
- QTextListFormat::Style style = QTextListFormat::ListDisc;
-
- switch (styleIndex) {
- default:
- case 1:
- style = QTextListFormat::ListDisc;
- break;
- case 2:
- style = QTextListFormat::ListCircle;
- break;
- case 3:
- style = QTextListFormat::ListSquare;
- break;
- case 4:
- style = QTextListFormat::ListDecimal;
- break;
- case 5:
- style = QTextListFormat::ListLowerAlpha;
- break;
- case 6:
- style = QTextListFormat::ListUpperAlpha;
- break;
- case 7:
- style = QTextListFormat::ListLowerRoman;
- break;
- case 8:
- style = QTextListFormat::ListUpperRoman;
- break;
- }
+ cursor.beginEditBlock();
- cursor.beginEditBlock();
+ QTextBlockFormat blockFmt = cursor.blockFormat();
- QTextBlockFormat blockFmt = cursor.blockFormat();
+ if (style == QTextListFormat::ListStyleUndefined) {
+ blockFmt.setObjectIndex(-1);
+ int headingLevel = styleIndex >= 9 ? styleIndex - 9 + 1 : 0; // H1 to H6, or Standard
+ blockFmt.setHeadingLevel(headingLevel);
+ cursor.setBlockFormat(blockFmt);
+ int sizeAdjustment = headingLevel ? 4 - headingLevel : 0; // H1 to H6: +3 to -2
+ QTextCharFormat fmt;
+ fmt.setFontWeight(headingLevel ? QFont::Bold : QFont::Normal);
+ fmt.setProperty(QTextFormat::FontSizeAdjustment, sizeAdjustment);
+ cursor.select(QTextCursor::LineUnderCursor);
+ cursor.mergeCharFormat(fmt);
+ textEdit->mergeCurrentCharFormat(fmt);
+ } else {
QTextListFormat listFmt;
-
if (cursor.currentList()) {
listFmt = cursor.currentList()->format();
} else {
@@ -620,18 +648,11 @@ void TextEdit::textStyle(int styleIndex)
blockFmt.setIndent(0);
cursor.setBlockFormat(blockFmt);
}
-
listFmt.setStyle(style);
-
cursor.createList(listFmt);
-
- cursor.endEditBlock();
- } else {
- // ####
- QTextBlockFormat bfmt;
- bfmt.setObjectIndex(-1);
- cursor.mergeBlockFormat(bfmt);
}
+
+ cursor.endEditBlock();
}
void TextEdit::textColor()
@@ -666,6 +687,41 @@ void TextEdit::currentCharFormatChanged(const QTextCharFormat &format)
void TextEdit::cursorPositionChanged()
{
alignmentChanged(textEdit->alignment());
+ QTextList *list = textEdit->textCursor().currentList();
+ if (list) {
+ switch (list->format().style()) {
+ case QTextListFormat::ListDisc:
+ comboStyle->setCurrentIndex(1);
+ break;
+ case QTextListFormat::ListCircle:
+ comboStyle->setCurrentIndex(2);
+ break;
+ case QTextListFormat::ListSquare:
+ comboStyle->setCurrentIndex(3);
+ break;
+ case QTextListFormat::ListDecimal:
+ comboStyle->setCurrentIndex(4);
+ break;
+ case QTextListFormat::ListLowerAlpha:
+ comboStyle->setCurrentIndex(5);
+ break;
+ case QTextListFormat::ListUpperAlpha:
+ comboStyle->setCurrentIndex(6);
+ break;
+ case QTextListFormat::ListLowerRoman:
+ comboStyle->setCurrentIndex(7);
+ break;
+ case QTextListFormat::ListUpperRoman:
+ comboStyle->setCurrentIndex(8);
+ break;
+ default:
+ comboStyle->setCurrentIndex(-1);
+ break;
+ }
+ } else {
+ int headingLevel = textEdit->textCursor().blockFormat().headingLevel();
+ comboStyle->setCurrentIndex(headingLevel ? headingLevel + 8 : 0);
+ }
}
void TextEdit::clipboardDataChanged()
diff --git a/examples/widgets/tools/undo/main.cpp b/examples/widgets/tools/undo/main.cpp
index eff44ca3e8..a5ec1b1b83 100644
--- a/examples/widgets/tools/undo/main.cpp
+++ b/examples/widgets/tools/undo/main.cpp
@@ -62,4 +62,4 @@ int main(int argc, char **argv)
win.show();
return app.exec();
-};
+}
diff --git a/examples/touch/dials/dials.pro b/examples/widgets/touch/dials/dials.pro
index 2c522a46f0..0e823551cc 100644
--- a/examples/touch/dials/dials.pro
+++ b/examples/widgets/touch/dials/dials.pro
@@ -4,5 +4,5 @@ SOURCES += main.cpp
FORMS += dials.ui
# install
-target.path = $$[QT_INSTALL_EXAMPLES]/touch/dials
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/dials
INSTALLS += target
diff --git a/examples/touch/dials/dials.ui b/examples/widgets/touch/dials/dials.ui
index 8ca7ae9475..8ca7ae9475 100644
--- a/examples/touch/dials/dials.ui
+++ b/examples/widgets/touch/dials/dials.ui
diff --git a/examples/touch/dials/doc/images/touch-dials-example.png b/examples/widgets/touch/dials/doc/images/touch-dials-example.png
index 60e1776fc3..60e1776fc3 100644
--- a/examples/touch/dials/doc/images/touch-dials-example.png
+++ b/examples/widgets/touch/dials/doc/images/touch-dials-example.png
Binary files differ
diff --git a/examples/touch/dials/doc/src/touch-dials.qdoc b/examples/widgets/touch/dials/doc/src/touch-dials.qdoc
index 10784c8c0d..dec8248efb 100644
--- a/examples/touch/dials/doc/src/touch-dials.qdoc
+++ b/examples/widgets/touch/dials/doc/src/touch-dials.qdoc
@@ -29,7 +29,7 @@
\example touch/dials
\title Touch Dials Example
\ingroup touchinputexamples
- \brief Shows how to apply touch to a set of standard Qt widgets
+ \brief Shows how to apply touch to a set of standard Qt widgets.
The Touch Dials example shows how to apply touch to a set of
standard Qt widgets.
diff --git a/examples/touch/dials/main.cpp b/examples/widgets/touch/dials/main.cpp
index 071f485de3..071f485de3 100644
--- a/examples/touch/dials/main.cpp
+++ b/examples/widgets/touch/dials/main.cpp
diff --git a/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc b/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc
new file mode 100644
index 0000000000..6f8f636f86
--- /dev/null
+++ b/examples/widgets/touch/fingerpaint/doc/src/fingerpaint.qdoc
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example touch/fingerpaint
+ \title Finger Paint Example
+ \ingroup touchinputexamples
+ \brief Shows the use of a touchscreen to make a simple painting application.
+
+ The Finger Paint example shows the use of a touchscreen with a custom widget
+ to create a simple painting application.
+
+ \image touch-fingerpaint-example.png
+
+ This example was specifically designed to work with a touchscreen, using
+ QTouchEvent instead of QMouseEvent to handle user input over the custom
+ widget. As a result, it is not possible to draw with the mouse cursor.
+*/
diff --git a/examples/touch/fingerpaint/fingerpaint.pro b/examples/widgets/touch/fingerpaint/fingerpaint.pro
index f196f7eed4..6370da6607 100644
--- a/examples/touch/fingerpaint/fingerpaint.pro
+++ b/examples/widgets/touch/fingerpaint/fingerpaint.pro
@@ -9,5 +9,5 @@ SOURCES = main.cpp \
scribblearea.cpp
# install
-target.path = $$[QT_INSTALL_EXAMPLES]/touch/fingerpaint
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/fingerpaint
INSTALLS += target
diff --git a/examples/touch/fingerpaint/main.cpp b/examples/widgets/touch/fingerpaint/main.cpp
index e7ab8856b7..e7ab8856b7 100644
--- a/examples/touch/fingerpaint/main.cpp
+++ b/examples/widgets/touch/fingerpaint/main.cpp
diff --git a/examples/touch/fingerpaint/mainwindow.cpp b/examples/widgets/touch/fingerpaint/mainwindow.cpp
index 0e45eea240..0e45eea240 100644
--- a/examples/touch/fingerpaint/mainwindow.cpp
+++ b/examples/widgets/touch/fingerpaint/mainwindow.cpp
diff --git a/examples/touch/fingerpaint/mainwindow.h b/examples/widgets/touch/fingerpaint/mainwindow.h
index 47c339050e..47c339050e 100644
--- a/examples/touch/fingerpaint/mainwindow.h
+++ b/examples/widgets/touch/fingerpaint/mainwindow.h
diff --git a/examples/touch/fingerpaint/scribblearea.cpp b/examples/widgets/touch/fingerpaint/scribblearea.cpp
index aa4e60c934..aa4e60c934 100644
--- a/examples/touch/fingerpaint/scribblearea.cpp
+++ b/examples/widgets/touch/fingerpaint/scribblearea.cpp
diff --git a/examples/touch/fingerpaint/scribblearea.h b/examples/widgets/touch/fingerpaint/scribblearea.h
index 5138e3a1ab..5138e3a1ab 100644
--- a/examples/touch/fingerpaint/scribblearea.h
+++ b/examples/widgets/touch/fingerpaint/scribblearea.h
diff --git a/examples/touch/knobs/doc/images/touch-knobs-example.png b/examples/widgets/touch/knobs/doc/images/touch-knobs-example.png
index 1cbd90d101..1cbd90d101 100644
--- a/examples/touch/knobs/doc/images/touch-knobs-example.png
+++ b/examples/widgets/touch/knobs/doc/images/touch-knobs-example.png
Binary files differ
diff --git a/examples/touch/knobs/doc/src/touch-knobs.qdoc b/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc
index d39dd564b2..6da5992195 100644
--- a/examples/touch/knobs/doc/src/touch-knobs.qdoc
+++ b/examples/widgets/touch/knobs/doc/src/touch-knobs.qdoc
@@ -29,7 +29,7 @@
\example touch/knobs
\title Touch Knobs Example
\ingroup touchinputexamples
- \brief Shows how to create custom controls that accept touch input
+ \brief Shows how to create custom controls that accept touch input.
The Touch Knobs example shows how to create custom controls that
accept touch input.
diff --git a/examples/touch/knobs/knob.cpp b/examples/widgets/touch/knobs/knob.cpp
index 12aaa1e948..12aaa1e948 100644
--- a/examples/touch/knobs/knob.cpp
+++ b/examples/widgets/touch/knobs/knob.cpp
diff --git a/examples/touch/knobs/knob.h b/examples/widgets/touch/knobs/knob.h
index c16b61d6f1..c16b61d6f1 100644
--- a/examples/touch/knobs/knob.h
+++ b/examples/widgets/touch/knobs/knob.h
diff --git a/examples/touch/knobs/knobs.pro b/examples/widgets/touch/knobs/knobs.pro
index 267ba26167..0915b0665a 100644
--- a/examples/touch/knobs/knobs.pro
+++ b/examples/widgets/touch/knobs/knobs.pro
@@ -4,5 +4,5 @@ HEADERS = knob.h
SOURCES = main.cpp knob.cpp
# install
-target.path = $$[QT_INSTALL_EXAMPLES]/touch/knobs
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/knobs
INSTALLS += target
diff --git a/examples/touch/knobs/main.cpp b/examples/widgets/touch/knobs/main.cpp
index ea0e857e7b..ea0e857e7b 100644
--- a/examples/touch/knobs/main.cpp
+++ b/examples/widgets/touch/knobs/main.cpp
diff --git a/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png b/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png
new file mode 100644
index 0000000000..7db51fbf55
--- /dev/null
+++ b/examples/widgets/touch/pinchzoom/doc/images/pinch-zoom-example.png
Binary files differ
diff --git a/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc b/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc
new file mode 100644
index 0000000000..61db2f96c5
--- /dev/null
+++ b/examples/widgets/touch/pinchzoom/doc/src/pinchzoom.qdoc
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example touch/pinchzoom
+ \title Pinch Zoom Example
+ \ingroup touchinputexamples
+ \brief Shows how to recognize a gesture.
+
+ The Pinch Zoom example shows how to use low-level touch information
+ to recognize a gesture.
+
+ \image touch-pinchzoom-example.png
+*/
diff --git a/examples/touch/pinchzoom/graphicsview.cpp b/examples/widgets/touch/pinchzoom/graphicsview.cpp
index 54e134aea2..54e134aea2 100644
--- a/examples/touch/pinchzoom/graphicsview.cpp
+++ b/examples/widgets/touch/pinchzoom/graphicsview.cpp
diff --git a/examples/touch/pinchzoom/graphicsview.h b/examples/widgets/touch/pinchzoom/graphicsview.h
index d4e2e32d36..d4e2e32d36 100644
--- a/examples/touch/pinchzoom/graphicsview.h
+++ b/examples/widgets/touch/pinchzoom/graphicsview.h
diff --git a/examples/touch/pinchzoom/images/cheese.jpg b/examples/widgets/touch/pinchzoom/images/cheese.jpg
index dea5795fd0..dea5795fd0 100644
--- a/examples/touch/pinchzoom/images/cheese.jpg
+++ b/examples/widgets/touch/pinchzoom/images/cheese.jpg
Binary files differ
diff --git a/examples/touch/pinchzoom/main.cpp b/examples/widgets/touch/pinchzoom/main.cpp
index 938432600f..938432600f 100644
--- a/examples/touch/pinchzoom/main.cpp
+++ b/examples/widgets/touch/pinchzoom/main.cpp
diff --git a/examples/touch/pinchzoom/mice.qrc b/examples/widgets/touch/pinchzoom/mice.qrc
index accdb4d0a6..accdb4d0a6 100644
--- a/examples/touch/pinchzoom/mice.qrc
+++ b/examples/widgets/touch/pinchzoom/mice.qrc
diff --git a/examples/touch/pinchzoom/mouse.cpp b/examples/widgets/touch/pinchzoom/mouse.cpp
index 1e6814be13..1e6814be13 100644
--- a/examples/touch/pinchzoom/mouse.cpp
+++ b/examples/widgets/touch/pinchzoom/mouse.cpp
diff --git a/examples/touch/pinchzoom/mouse.h b/examples/widgets/touch/pinchzoom/mouse.h
index 870bfcd6c0..870bfcd6c0 100644
--- a/examples/touch/pinchzoom/mouse.h
+++ b/examples/widgets/touch/pinchzoom/mouse.h
diff --git a/examples/touch/pinchzoom/pinchzoom.pro b/examples/widgets/touch/pinchzoom/pinchzoom.pro
index 9441cc1d92..ebbc7ddf1f 100644
--- a/examples/touch/pinchzoom/pinchzoom.pro
+++ b/examples/widgets/touch/pinchzoom/pinchzoom.pro
@@ -12,5 +12,5 @@ RESOURCES += \
mice.qrc
# install
-target.path = $$[QT_INSTALL_EXAMPLES]/touch/pinchzoom
+target.path = $$[QT_INSTALL_EXAMPLES]/widgets/touch/pinchzoom
INSTALLS += target
diff --git a/examples/touch/touch.pro b/examples/widgets/touch/touch.pro
index 018ec134eb..018ec134eb 100644
--- a/examples/touch/touch.pro
+++ b/examples/widgets/touch/touch.pro
diff --git a/examples/widgets/widgets.pro b/examples/widgets/widgets.pro
index 7e52125125..d975ea8307 100644
--- a/examples/widgets/widgets.pro
+++ b/examples/widgets/widgets.pro
@@ -19,6 +19,7 @@ SUBDIRS = \
scroller \
statemachine \
tools \
+ touch \
tutorials \
widgets
diff --git a/examples/widgets/widgets/tablet/mainwindow.cpp b/examples/widgets/widgets/tablet/mainwindow.cpp
index 994666d4de..a048119533 100644
--- a/examples/widgets/widgets/tablet/mainwindow.cpp
+++ b/examples/widgets/widgets/tablet/mainwindow.cpp
@@ -104,15 +104,16 @@ void MainWindow::setEventCompression(bool compress)
}
//! [5]
-void MainWindow::save()
+bool MainWindow::save()
{
QString path = QDir::currentPath() + "/untitled.png";
QString fileName = QFileDialog::getSaveFileName(this, tr("Save Picture"),
path);
-
- if (!m_canvas->saveImage(fileName))
+ bool success = m_canvas->saveImage(fileName);
+ if (!success)
QMessageBox::information(this, "Error Saving Picture",
"Could not save the image");
+ return success;
}
//! [5]
@@ -128,6 +129,14 @@ void MainWindow::load()
}
//! [6]
+void MainWindow::clear()
+{
+ if (QMessageBox::question(this, tr("Save changes"), tr("Do you want to save your changes?"),
+ QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel,
+ QMessageBox::Save) != QMessageBox::Save || save())
+ m_canvas->clear();
+}
+
//! [7]
void MainWindow::about()
{
@@ -142,6 +151,7 @@ void MainWindow::createMenus()
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(tr("&Open..."), this, &MainWindow::load, QKeySequence::Open);
fileMenu->addAction(tr("&Save As..."), this, &MainWindow::save, QKeySequence::SaveAs);
+ fileMenu->addAction(tr("&New"), this, &MainWindow::clear, QKeySequence::New);
fileMenu->addAction(tr("E&xit"), this, &MainWindow::close, QKeySequence::Quit);
QMenu *brushMenu = menuBar()->addMenu(tr("&Brush"));
diff --git a/examples/widgets/widgets/tablet/mainwindow.h b/examples/widgets/widgets/tablet/mainwindow.h
index 4b99324f04..4be28784b5 100644
--- a/examples/widgets/widgets/tablet/mainwindow.h
+++ b/examples/widgets/widgets/tablet/mainwindow.h
@@ -72,8 +72,9 @@ private slots:
void setLineWidthValuator(QAction *action);
void setSaturationValuator(QAction *action);
void setEventCompression(bool compress);
- void save();
+ bool save();
void load();
+ void clear();
void about();
private:
diff --git a/examples/widgets/widgets/tablet/tablet.pro b/examples/widgets/widgets/tablet/tablet.pro
index 5772cd0385..647dc2c2f7 100644
--- a/examples/widgets/widgets/tablet/tablet.pro
+++ b/examples/widgets/widgets/tablet/tablet.pro
@@ -10,6 +10,11 @@ SOURCES = mainwindow.cpp \
tabletapplication.cpp
RESOURCES += images.qrc
+# Avoid naming the target "tablet", as it would create an executable
+# named "tablet.exe" on Windows and trigger a bug (in the Wacom drivers, apparently)
+# preventing tablet messages from being received.
+TARGET = qttablet
+
# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/tablet
INSTALLS += target
diff --git a/examples/widgets/widgets/tablet/tabletcanvas.cpp b/examples/widgets/widgets/tablet/tabletcanvas.cpp
index 73678ab754..bfcc84e182 100644
--- a/examples/widgets/widgets/tablet/tabletcanvas.cpp
+++ b/examples/widgets/widgets/tablet/tabletcanvas.cpp
@@ -90,6 +90,12 @@ bool TabletCanvas::loadImage(const QString &file)
}
//! [2]
+void TabletCanvas::clear()
+{
+ m_pixmap.fill(Qt::white);
+ update();
+}
+
//! [3]
void TabletCanvas::tabletEvent(QTabletEvent *event)
{
@@ -142,12 +148,14 @@ void TabletCanvas::initPixmap()
m_pixmap = newPixmap;
}
-void TabletCanvas::paintEvent(QPaintEvent *)
+void TabletCanvas::paintEvent(QPaintEvent *event)
{
if (m_pixmap.isNull())
initPixmap();
QPainter painter(this);
- painter.drawPixmap(0, 0, m_pixmap);
+ QRect pixmapPortion = QRect(event->rect().topLeft() * devicePixelRatioF(),
+ event->rect().size() * devicePixelRatioF());
+ painter.drawPixmap(event->rect().topLeft(), m_pixmap, pixmapPortion);
}
//! [4]
diff --git a/examples/widgets/widgets/tablet/tabletcanvas.h b/examples/widgets/widgets/tablet/tabletcanvas.h
index 671d5376f8..c63ef76893 100644
--- a/examples/widgets/widgets/tablet/tabletcanvas.h
+++ b/examples/widgets/widgets/tablet/tabletcanvas.h
@@ -79,6 +79,7 @@ public:
bool saveImage(const QString &file);
bool loadImage(const QString &file);
+ void clear();
void setAlphaChannelValuator(Valuator type)
{ m_alphaChannelValuator = type; }
void setColorSaturationValuator(Valuator type)