encodingdialog.cpp Example File

tools/codecs/encodingdialog.cpp
 /****************************************************************************
 **
 ** 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 <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", "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<Encoding>(i)));
     }
 }

 #include "encodingdialog.moc"