From 7aaf54f5725c65bf830088a9cfd104ef4817f256 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 12 Nov 2018 15:18:12 +0100 Subject: Codecs example: Add a dialog for showing common C++ encodings Add a dialog where the user can enter a line of text, which is then displayed in several encodings with special characters converted suitable for C++/Python string literals. Task-number: QTBUG-60635 Change-Id: Ibd436f9f76e128c93cbb581235c730d636641d8a Reviewed-by: Andy Shaw --- examples/widgets/tools/codecs/codecs.pro | 9 +- examples/widgets/tools/codecs/codecs.qrc | 5 + examples/widgets/tools/codecs/encodingdialog.cpp | 333 ++++++++++++++++++++++ examples/widgets/tools/codecs/encodingdialog.h | 73 +++++ examples/widgets/tools/codecs/images/editcopy.png | Bin 0 -> 1325 bytes examples/widgets/tools/codecs/mainwindow.cpp | 19 ++ examples/widgets/tools/codecs/mainwindow.h | 4 + 7 files changed, 441 insertions(+), 2 deletions(-) create mode 100644 examples/widgets/tools/codecs/codecs.qrc create mode 100644 examples/widgets/tools/codecs/encodingdialog.cpp create mode 100644 examples/widgets/tools/codecs/encodingdialog.h create mode 100644 examples/widgets/tools/codecs/images/editcopy.png (limited to 'examples/widgets/tools') diff --git a/examples/widgets/tools/codecs/codecs.pro b/examples/widgets/tools/codecs/codecs.pro index 13daa79237..6f4b0742a9 100644 --- a/examples/widgets/tools/codecs/codecs.pro +++ b/examples/widgets/tools/codecs/codecs.pro @@ -2,10 +2,15 @@ QT += widgets requires(qtConfig(filedialog)) HEADERS += mainwindow.h \ - previewform.h + previewform.h \ + encodingdialog.h + SOURCES += main.cpp \ mainwindow.cpp \ - previewform.cpp + previewform.cpp \ + encodingdialog.cpp + +RESOURCES += codecs.qrc EXAMPLE_FILES = encodedfiles diff --git a/examples/widgets/tools/codecs/codecs.qrc b/examples/widgets/tools/codecs/codecs.qrc new file mode 100644 index 0000000000..65fa1aa64d --- /dev/null +++ b/examples/widgets/tools/codecs/codecs.qrc @@ -0,0 +1,5 @@ + + + images/editcopy.png + + diff --git a/examples/widgets/tools/codecs/encodingdialog.cpp b/examples/widgets/tools/codecs/encodingdialog.cpp new file mode 100644 index 0000000000..ca4b56db9e --- /dev/null +++ b/examples/widgets/tools/codecs/encodingdialog.cpp @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** 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 "encodingdialog.h" + +#if QT_CONFIG(action) +# include +#endif +#include +#include +#include +#include +#include + +#if QT_CONFIG(clipboard) +# include +# include +#endif + +#include + +// Helpers for formatting character sequences + +// Format a special character like '\x0a' +template +static void formatEscapedNumber(QTextStream &str, Int value, int base, + int width = 0,char prefix = 0) +{ + str << '\\'; + if (prefix) + str << prefix; + const auto oldPadChar = str.padChar(); + const auto oldFieldWidth = str.fieldWidth(); + const auto oldFieldAlignment = str.fieldAlignment(); + const auto oldIntegerBase = str.integerBase(); + str.setPadChar(QLatin1Char('0')); + str.setFieldWidth(width); + str.setFieldAlignment(QTextStream::AlignRight); + str.setIntegerBase(base); + str << value; + str.setIntegerBase(oldIntegerBase); + str.setFieldAlignment(oldFieldAlignment); + str.setFieldWidth(oldFieldWidth); + str.setPadChar(oldPadChar); +} + +template +static bool formatSpecialCharacter(QTextStream &str, Int value) +{ + bool result = true; + switch (value) { + case '\\': + str << "\\\\"; + break; + case '\"': + str << "\\\""; + break; + case '\n': + str << "\\n"; + break; + default: + result = false; + break; + } + return result; +} + +// Format a sequence of characters (QChar, ushort (UTF-16), uint (UTF-32) +// or just char (Latin1, Utf-8)) with the help of traits specifying +// how to obtain the code for checking the printable-ness and how to +// stream out the plain ASCII values. + +template +struct FormattingTraits +{ +}; + +template <> +struct FormattingTraits +{ + static ushort code(QChar c) { return c.unicode(); } + static char toAscii(QChar c) { return c.toLatin1(); } +}; + +template <> +struct FormattingTraits +{ + static ushort code(char c) { return uchar(c); } + static char toAscii(char c) { return c; } +}; + +template <> +struct FormattingTraits +{ + static ushort code(ushort c) { return c; } + static char toAscii(ushort c) { return char(c); } +}; + +template <> +struct FormattingTraits +{ + static uint code(uint c) { return c; } + static char toAscii(uint c) { return char(c); } +}; + +template <> +struct FormattingTraits +{ + static uchar code(char c) { return uchar(c); } + static char toAscii(char c) { return c; } +}; + +static bool isHexDigit(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +template +static void formatStringSequence(QTextStream &str, Iterator i1, Iterator i2, + int escapeIntegerBase, int escapeWidth, + char escapePrefix = 0) +{ + str << '"'; + bool separateHexEscape = false; + for (; i1 != i2; ++i1) { + const auto code = FormattingTraits::code(*i1); + if (code >= 0x80) { + formatEscapedNumber(str, code, escapeIntegerBase, escapeWidth, escapePrefix); + separateHexEscape = escapeIntegerBase == 16 && escapeWidth == 0; + } else { + if (!formatSpecialCharacter(str, code)) { + const char c = FormattingTraits::toAscii(*i1); + // For variable width/hex: Terminate the literal to stop digit parsing + // ("\x12" "34..."). + if (separateHexEscape && isHexDigit(c)) + str << "\" \""; + str << c; + } + separateHexEscape = false; + } + } + str << '"'; +} + +static QString encodedString(const QString &value, EncodingDialog::Encoding e) +{ + QString result; + QTextStream str(&result); + switch (e) { + case EncodingDialog::Unicode: + formatStringSequence(str, value.cbegin(), value.cend(), + 16, 4, 'u'); + break; + case EncodingDialog::Utf8: { + const QByteArray utf8 = value.toUtf8(); + str << "u8"; + formatStringSequence(str, utf8.cbegin(), utf8.cend(), + 8, 3); + } + break; + case EncodingDialog::Utf16: { + auto utf16 = value.utf16(); + auto utf16End = utf16 + value.size(); + str << 'u'; + formatStringSequence(str, utf16, utf16End, + 16, 0, 'x'); + } + break; + case EncodingDialog::Utf32: { + auto utf32 = value.toUcs4(); + str << 'U'; + formatStringSequence(str, utf32.cbegin(), utf32.cend(), + 16, 0, 'x'); + } + break; + case EncodingDialog::Latin1: { + const QByteArray latin1 = value.toLatin1(); + formatStringSequence(str, latin1.cbegin(), latin1.cend(), + 16, 0, 'x'); + } + break; + case EncodingDialog::EncodingCount: + break; + } + return result; +} + +// Dialog helpers + +static const char *encodingLabels[] +{ + QT_TRANSLATE_NOOP("EncodingDialog", "Unicode:"), + QT_TRANSLATE_NOOP("EncodingDialog", "UTF-8:"), + QT_TRANSLATE_NOOP("EncodingDialog", "UTF-16:"), + QT_TRANSLATE_NOOP("EncodingDialog", "UTF-32:"), + QT_TRANSLATE_NOOP("EncodingDialog", "Latin1:") +}; + +static const char *encodingToolTips[] +{ + QT_TRANSLATE_NOOP("EncodingDialog", "Unicode points for use with any encoding (C++, Python)"), + QT_TRANSLATE_NOOP("EncodingDialog", "QString::fromUtf8()"), + QT_TRANSLATE_NOOP("EncodingDialog", "QStringViewLiteral(), wchar_t on Windows"), + QT_TRANSLATE_NOOP("EncodingDialog", "wchar_t on Unix (Ucs4)"), + QT_TRANSLATE_NOOP("EncodingDialog", "QLatin1String") +}; + +// A read-only line edit with a tool button to copy the contents +class DisplayLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit DisplayLineEdit(const QIcon &icon, QWidget *parent = nullptr); + +public slots: + void copyAll(); +}; + +DisplayLineEdit::DisplayLineEdit(const QIcon &icon, QWidget *parent) : + QLineEdit(parent) +{ + setReadOnly(true); +#if QT_CONFIG(clipboard) && QT_CONFIG(action) + auto copyAction = addAction(icon, QLineEdit::TrailingPosition); + connect(copyAction, &QAction::triggered, this, &DisplayLineEdit::copyAll); +#endif +} + +void DisplayLineEdit::copyAll() +{ +#if QT_CONFIG(clipboard) + QGuiApplication::clipboard()->setText(text()); +#endif +} + +static void addFormLayoutRow(QFormLayout *formLayout, const QString &text, + QWidget *w, const QString &toolTip) +{ + auto label = new QLabel(text); + label->setToolTip(toolTip); + w->setToolTip(toolTip); + label->setBuddy(w); + formLayout->addRow(label, w); +} + +EncodingDialog::EncodingDialog(QWidget *parent) : + QDialog(parent) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Encodings")); + + auto formLayout = new QFormLayout; + auto sourceLineEdit = new QLineEdit(this); + sourceLineEdit->setClearButtonEnabled(true); + connect(sourceLineEdit, &QLineEdit::textChanged, this, &EncodingDialog::textChanged); + + addFormLayoutRow(formLayout, tr("&Source:"), sourceLineEdit, tr("Enter text")); + + const auto copyIcon = QIcon::fromTheme(QLatin1String("edit-copy"), + QIcon(QLatin1String(":/images/editcopy"))); + for (int i = 0; i < EncodingCount; ++i) { + m_lineEdits[i] = new DisplayLineEdit(copyIcon, this); + addFormLayoutRow(formLayout, tr(encodingLabels[i]), + m_lineEdits[i], tr(encodingToolTips[i])); + } + + auto mainLayout = new QVBoxLayout(this); + mainLayout->addLayout(formLayout); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + mainLayout->addWidget(buttonBox); +} + +void EncodingDialog::textChanged(const QString &t) +{ + if (t.isEmpty()) { + for (auto lineEdit : m_lineEdits) + lineEdit->clear(); + } else { + for (int i = 0; i < EncodingCount; ++i) + m_lineEdits[i]->setText(encodedString(t, static_cast(i))); + } +} + +#include "encodingdialog.moc" diff --git a/examples/widgets/tools/codecs/encodingdialog.h b/examples/widgets/tools/codecs/encodingdialog.h new file mode 100644 index 0000000000..3f82da84af --- /dev/null +++ b/examples/widgets/tools/codecs/encodingdialog.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 ENCODINGDIALOG_H +#define ENCODINGDIALOG_H + +#include + +QT_FORWARD_DECLARE_CLASS(QLineEdit) + +class EncodingDialog : public QDialog +{ + Q_OBJECT +public: + explicit EncodingDialog(QWidget *parent = nullptr); + + enum Encoding { Unicode, Utf8, Utf16, Utf32, Latin1, EncodingCount }; + +private slots: + void textChanged(const QString &t); + +private: + QLineEdit *m_lineEdits[EncodingCount]; +}; + +#endif // ENCODINGDIALOG_H diff --git a/examples/widgets/tools/codecs/images/editcopy.png b/examples/widgets/tools/codecs/images/editcopy.png new file mode 100644 index 0000000000..1121b47d8b Binary files /dev/null and b/examples/widgets/tools/codecs/images/editcopy.png differ diff --git a/examples/widgets/tools/codecs/mainwindow.cpp b/examples/widgets/tools/codecs/mainwindow.cpp index 28f904d1a7..229c2ccfd4 100644 --- a/examples/widgets/tools/codecs/mainwindow.cpp +++ b/examples/widgets/tools/codecs/mainwindow.cpp @@ -51,6 +51,7 @@ #include #include "mainwindow.h" +#include "encodingdialog.h" #include "previewform.h" MainWindow::MainWindow() @@ -188,9 +189,27 @@ void MainWindow::createMenus() QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close); exitAct->setShortcuts(QKeySequence::Quit); + auto toolMenu = menuBar()->addMenu(tr("&Tools")); + auto encodingAction = toolMenu->addAction(tr("Encodings"), this, &MainWindow::encodingDialog); + encodingAction->setShortcut(Qt::CTRL + Qt::Key_E); + encodingAction->setToolTip(tr("Shows a dialog allowing to convert to common encoding in programming languages.")); + + menuBar()->addSeparator(); QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(tr("&About"), this, &MainWindow::about); helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); } + +void MainWindow::encodingDialog() +{ + if (!m_encodingDialog) { + m_encodingDialog = new EncodingDialog(this); + const QRect screenGeometry = QApplication::desktop()->screenGeometry(this); + m_encodingDialog->setMinimumWidth(screenGeometry.width() / 4); + } + m_encodingDialog->show(); + m_encodingDialog->raise(); + +} diff --git a/examples/widgets/tools/codecs/mainwindow.h b/examples/widgets/tools/codecs/mainwindow.h index 7121c0b122..64494d1960 100644 --- a/examples/widgets/tools/codecs/mainwindow.h +++ b/examples/widgets/tools/codecs/mainwindow.h @@ -59,6 +59,8 @@ class QAction; class QTextCodec; class QPlainTextEdit; QT_END_NAMESPACE + +class EncodingDialog; class PreviewForm; class MainWindow : public QMainWindow @@ -73,6 +75,7 @@ private slots: void save(); void about(); void aboutToShowSaveAsMenu(); + void encodingDialog(); private: void findCodecs(); @@ -82,6 +85,7 @@ private: QPlainTextEdit *textEdit; PreviewForm *previewForm; QList codecs; + EncodingDialog *m_encodingDialog = nullptr; }; #endif -- cgit v1.2.3