Revision control

++ // Copyright (C) 2018 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ #include "encodingdialog.h"
++
++ #if QT_CONFIG(action)
++ # include <QAction>
++ #endif
++ #include <QDialogButtonBox>
++ #include <QFormLayout>
++ #include <QLabel>
++ #include <QLineEdit>
++ #include <QVBoxLayout>
++
++ #if QT_CONFIG(clipboard)
++ # include <QGuiApplication>
++ # include <QClipboard>
++ #endif
++
++ #include <QTextStream>
++
++ // Helpers for formatting character sequences
++
++ // Format a special character like '\x0a'
++ template <class Int>
++ 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 <class Int>
++ 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 <EncodingDialog::Encoding>
++ struct FormattingTraits
++ {
++ };
++
++ template <>
++ struct FormattingTraits<EncodingDialog::Unicode>
++ {
++ static ushort code(QChar c) { return c.unicode(); }
++ static char toAscii(QChar c) { return c.toLatin1(); }
++ };
++
++ template <>
++ struct FormattingTraits<EncodingDialog::Utf8>
++ {
++ static ushort code(char c) { return uchar(c); }
++ static char toAscii(char c) { return c; }
++ };
++
++ template <>
++ struct FormattingTraits<EncodingDialog::Utf16>
++ {
++ static ushort code(ushort c) { return c; }
++ static char toAscii(ushort c) { return char(c); }
++ };
++
++ template <>
++ struct FormattingTraits<EncodingDialog::Utf32>
++ {
++ static uint code(uint c) { return c; }
++ static char toAscii(uint c) { return char(c); }
++ };
++
++ template <>
++ struct FormattingTraits<EncodingDialog::Latin1>
++ {
++ 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 <EncodingDialog::Encoding encoding, class Iterator>
++ 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<encoding>::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<encoding>::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<EncodingDialog::Unicode>(str, value.cbegin(), value.cend(),
++ 16, 4, 'u');
++ break;
++ case EncodingDialog::Utf8: {
++ const QByteArray utf8 = value.toUtf8();
++ str << "u8";
++ formatStringSequence<EncodingDialog::Utf8>(str, utf8.cbegin(), utf8.cend(),
++ 8, 3);
++ }
++ break;
++ case EncodingDialog::Utf16: {
++ auto utf16 = value.utf16();
++ auto utf16End = utf16 + value.size();
++ str << 'u';
++ formatStringSequence<EncodingDialog::Utf16>(str, utf16, utf16End,
++ 16, 0, 'x');
++ }
++ break;
++ case EncodingDialog::Utf32: {
++ auto utf32 = value.toUcs4();
++ str << 'U';
++ formatStringSequence<EncodingDialog::Utf32>(str, utf32.cbegin(), utf32.cend(),
++ 16, 0, 'x');
++ }
++ break;
++ case EncodingDialog::Latin1: {
++ const QByteArray latin1 = value.toLatin1();
++ formatStringSequence<EncodingDialog::Latin1>(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", "wchar_t on Windows, char16_t everywhere"),
++ 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<Encoding>(i)));
++ }
++ }
++
++ #include "encodingdialog.moc"