1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "encodingdialog.h"
52
53#if QT_CONFIG(action)
54# include <QAction>
55#endif
56#include <QDialogButtonBox>
57#include <QFormLayout>
58#include <QLabel>
59#include <QLineEdit>
60#include <QVBoxLayout>
61
62#if QT_CONFIG(clipboard)
63# include <QGuiApplication>
64# include <QClipboard>
65#endif
66
67#include <QTextStream>
68
69// Helpers for formatting character sequences
70
71// Format a special character like '\x0a'
72template <class Int>
73static void formatEscapedNumber(QTextStream &str, Int value, int base,
74 int width = 0,char prefix = 0)
75{
76 str << '\\';
77 if (prefix)
78 str << prefix;
79 const auto oldPadChar = str.padChar();
80 const auto oldFieldWidth = str.fieldWidth();
81 const auto oldFieldAlignment = str.fieldAlignment();
82 const auto oldIntegerBase = str.integerBase();
83 str.setPadChar(QLatin1Char('0'));
84 str.setFieldWidth(width);
85 str.setFieldAlignment(QTextStream::AlignRight);
86 str.setIntegerBase(base);
87 str << value;
88 str.setIntegerBase(oldIntegerBase);
89 str.setFieldAlignment(oldFieldAlignment);
90 str.setFieldWidth(oldFieldWidth);
91 str.setPadChar(oldPadChar);
92}
93
94template <class Int>
95static bool formatSpecialCharacter(QTextStream &str, Int value)
96{
97 bool result = true;
98 switch (value) {
99 case '\\':
100 str << "\\\\";
101 break;
102 case '\"':
103 str << "\\\"";
104 break;
105 case '\n':
106 str << "\\n";
107 break;
108 default:
109 result = false;
110 break;
111 }
112 return result;
113}
114
115// Format a sequence of characters (QChar, ushort (UTF-16), uint (UTF-32)
116// or just char (Latin1, Utf-8)) with the help of traits specifying
117// how to obtain the code for checking the printable-ness and how to
118// stream out the plain ASCII values.
119
120template <EncodingDialog::Encoding>
121struct FormattingTraits
122{
123};
124
125template <>
126struct FormattingTraits<EncodingDialog::Unicode>
127{
128 static ushort code(QChar c) { return c.unicode(); }
129 static char toAscii(QChar c) { return c.toLatin1(); }
130};
131
132template <>
133struct FormattingTraits<EncodingDialog::Utf8>
134{
135 static ushort code(char c) { return uchar(c); }
136 static char toAscii(char c) { return c; }
137};
138
139template <>
140struct FormattingTraits<EncodingDialog::Utf16>
141{
142 static ushort code(ushort c) { return c; }
143 static char toAscii(ushort c) { return char(c); }
144};
145
146template <>
147struct FormattingTraits<EncodingDialog::Utf32>
148{
149 static uint code(uint c) { return c; }
150 static char toAscii(uint c) { return char(c); }
151};
152
153template <>
154struct FormattingTraits<EncodingDialog::Latin1>
155{
156 static uchar code(char c) { return uchar(c); }
157 static char toAscii(char c) { return c; }
158};
159
160static bool isHexDigit(char c)
161{
162 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
163 || (c >= 'A' && c <= 'F');
164}
165
166template <EncodingDialog::Encoding encoding, class Iterator>
167static void formatStringSequence(QTextStream &str, Iterator i1, Iterator i2,
168 int escapeIntegerBase, int escapeWidth,
169 char escapePrefix = 0)
170{
171 str << '"';
172 bool separateHexEscape = false;
173 for (; i1 != i2; ++i1) {
174 const auto code = FormattingTraits<encoding>::code(*i1);
175 if (code >= 0x80) {
176 formatEscapedNumber(str, code, escapeIntegerBase, escapeWidth, escapePrefix);
177 separateHexEscape = escapeIntegerBase == 16 && escapeWidth == 0;
178 } else {
179 if (!formatSpecialCharacter(str, code)) {
180 const char c = FormattingTraits<encoding>::toAscii(*i1);
181 // For variable width/hex: Terminate the literal to stop digit parsing
182 // ("\x12" "34...").
183 if (separateHexEscape && isHexDigit(c))
184 str << "\" \"";
185 str << c;
186 }
187 separateHexEscape = false;
188 }
189 }
190 str << '"';
191}
192
193static QString encodedString(const QString &value, EncodingDialog::Encoding e)
194{
195 QString result;
196 QTextStream str(&result);
197 switch (e) {
198 case EncodingDialog::Unicode:
199 formatStringSequence<EncodingDialog::Unicode>(str, i1: value.cbegin(), i2: value.cend(),
200 escapeIntegerBase: 16, escapeWidth: 4, escapePrefix: 'u');
201 break;
202 case EncodingDialog::Utf8: {
203 const QByteArray utf8 = value.toUtf8();
204 str << "u8";
205 formatStringSequence<EncodingDialog::Utf8>(str, i1: utf8.cbegin(), i2: utf8.cend(),
206 escapeIntegerBase: 8, escapeWidth: 3);
207 }
208 break;
209 case EncodingDialog::Utf16: {
210 auto utf16 = value.utf16();
211 auto utf16End = utf16 + value.size();
212 str << 'u';
213 formatStringSequence<EncodingDialog::Utf16>(str, i1: utf16, i2: utf16End,
214 escapeIntegerBase: 16, escapeWidth: 0, escapePrefix: 'x');
215 }
216 break;
217 case EncodingDialog::Utf32: {
218 auto utf32 = value.toUcs4();
219 str << 'U';
220 formatStringSequence<EncodingDialog::Utf32>(str, i1: utf32.cbegin(), i2: utf32.cend(),
221 escapeIntegerBase: 16, escapeWidth: 0, escapePrefix: 'x');
222 }
223 break;
224 case EncodingDialog::Latin1: {
225 const QByteArray latin1 = value.toLatin1();
226 formatStringSequence<EncodingDialog::Latin1>(str, i1: latin1.cbegin(), i2: latin1.cend(),
227 escapeIntegerBase: 16, escapeWidth: 0, escapePrefix: 'x');
228 }
229 break;
230 case EncodingDialog::EncodingCount:
231 break;
232 }
233 return result;
234}
235
236// Dialog helpers
237
238static const char *encodingLabels[]
239{
240 QT_TRANSLATE_NOOP("EncodingDialog", "Unicode:"),
241 QT_TRANSLATE_NOOP("EncodingDialog", "UTF-8:"),
242 QT_TRANSLATE_NOOP("EncodingDialog", "UTF-16:"),
243 QT_TRANSLATE_NOOP("EncodingDialog", "UTF-32:"),
244 QT_TRANSLATE_NOOP("EncodingDialog", "Latin1:")
245};
246
247static const char *encodingToolTips[]
248{
249 QT_TRANSLATE_NOOP("EncodingDialog", "Unicode points for use with any encoding (C++, Python)"),
250 QT_TRANSLATE_NOOP("EncodingDialog", "QString::fromUtf8()"),
251 QT_TRANSLATE_NOOP("EncodingDialog", "wchar_t on Windows, char16_t everywhere"),
252 QT_TRANSLATE_NOOP("EncodingDialog", "wchar_t on Unix (Ucs4)"),
253 QT_TRANSLATE_NOOP("EncodingDialog", "QLatin1String")
254};
255
256// A read-only line edit with a tool button to copy the contents
257class DisplayLineEdit : public QLineEdit
258{
259 Q_OBJECT
260public:
261 explicit DisplayLineEdit(const QIcon &icon, QWidget *parent = nullptr);
262
263public slots:
264 void copyAll();
265};
266
267DisplayLineEdit::DisplayLineEdit(const QIcon &icon, QWidget *parent) :
268 QLineEdit(parent)
269{
270 setReadOnly(true);
271#if QT_CONFIG(clipboard) && QT_CONFIG(action)
272 auto copyAction = addAction(icon, position: QLineEdit::TrailingPosition);
273 connect(sender: copyAction, signal: &QAction::triggered, receiver: this, slot: &DisplayLineEdit::copyAll);
274#endif
275}
276
277void DisplayLineEdit::copyAll()
278{
279#if QT_CONFIG(clipboard)
280 QGuiApplication::clipboard()->setText(text());
281#endif
282}
283
284static void addFormLayoutRow(QFormLayout *formLayout, const QString &text,
285 QWidget *w, const QString &toolTip)
286{
287 auto label = new QLabel(text);
288 label->setToolTip(toolTip);
289 w->setToolTip(toolTip);
290 label->setBuddy(w);
291 formLayout->addRow(label, field: w);
292}
293
294EncodingDialog::EncodingDialog(QWidget *parent) :
295 QDialog(parent)
296{
297 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
298 setWindowTitle(tr(s: "Encodings"));
299
300 auto formLayout = new QFormLayout;
301 auto sourceLineEdit = new QLineEdit(this);
302 sourceLineEdit->setClearButtonEnabled(true);
303 connect(sender: sourceLineEdit, signal: &QLineEdit::textChanged, receiver: this, slot: &EncodingDialog::textChanged);
304
305 addFormLayoutRow(formLayout, text: tr(s: "&Source:"), w: sourceLineEdit, toolTip: tr(s: "Enter text"));
306
307 const auto copyIcon = QIcon::fromTheme(name: QLatin1String("edit-copy"),
308 fallback: QIcon(QLatin1String(":/images/editcopy")));
309 for (int i = 0; i < EncodingCount; ++i) {
310 m_lineEdits[i] = new DisplayLineEdit(copyIcon, this);
311 addFormLayoutRow(formLayout, text: tr(s: encodingLabels[i]),
312 w: m_lineEdits[i], toolTip: tr(s: encodingToolTips[i]));
313 }
314
315 auto mainLayout = new QVBoxLayout(this);
316 mainLayout->addLayout(layout: formLayout);
317 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
318 connect(sender: buttonBox, signal: &QDialogButtonBox::rejected, receiver: this, slot: &QDialog::reject);
319 mainLayout->addWidget(buttonBox);
320}
321
322void EncodingDialog::textChanged(const QString &t)
323{
324 if (t.isEmpty()) {
325 for (auto lineEdit : m_lineEdits)
326 lineEdit->clear();
327 } else {
328 for (int i = 0; i < EncodingCount; ++i)
329 m_lineEdits[i]->setText(encodedString(value: t, e: static_cast<Encoding>(i)));
330 }
331}
332
333#include "encodingdialog.moc"
334

source code of qtbase/examples/widgets/tools/codecs/encodingdialog.cpp