encodingdialog.cpp Example File | Qt Widgets 5.12.5

Qt 5.12 | Qt Widgets | Text Codecs Example | encodingdialog.cpp Example File

Qt 5.12.5 Reference Documentation

# 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"