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 tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
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** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "language.h"
30
31#include <QtCore/qtextstream.h>
32
33namespace language {
34
35static Encoding encoding = Encoding::Utf8;
36static Language _language = Language::Cpp;
37
38Language language() { return _language; }
39
40void setLanguage(Language l)
41{
42 _language = l;
43 switch (_language) {
44 case Language::Cpp:
45 derefPointer = QLatin1String("->");
46 nullPtr = QLatin1String("nullptr");
47 operatorNew = QLatin1String("new ");
48 qtQualifier = QLatin1String("Qt::");
49 qualifier = QLatin1String("::");
50 self = QLatin1String(""); // for testing: change to "this->";
51 eol = QLatin1String(";\n");
52 emptyString = QLatin1String("QString()");
53 encoding = Encoding::Utf8;
54 break;
55 case Language::Python:
56 derefPointer = QLatin1String(".");
57 nullPtr = QLatin1String("None");
58 operatorNew = QLatin1String("");
59 qtQualifier = QLatin1String("Qt.");
60 qualifier = QLatin1String(".");
61 self = QLatin1String("self.");
62 eol = QLatin1String("\n");
63 emptyString = QLatin1String("\"\"");
64 encoding = Encoding::Unicode;
65 break;
66 }
67}
68
69QString derefPointer;
70QString nullPtr;
71QString operatorNew;
72QString qtQualifier;
73QString qualifier;
74QString self;
75QString eol;
76QString emptyString;
77
78QString cppQualifier = QLatin1String("::");
79QString cppTrue = QLatin1String("true");
80QString cppFalse = QLatin1String("false");
81
82QTextStream &operator<<(QTextStream &str, const qtConfig &c)
83{
84 str << "QT_CONFIG(" << c.parameter() << ')';
85 return str;
86}
87
88QTextStream &operator<<(QTextStream &str, const openQtConfig &c)
89{
90 str << "#if " << qtConfig(c.parameter()) << '\n';
91 return str;
92}
93
94QTextStream &operator<<(QTextStream &str, const closeQtConfig &c)
95{
96 str << "#endif // " << qtConfig(c.parameter()) << '\n';
97 return str;
98}
99
100struct EnumLookup
101{
102 int value;
103 const char *valueString;
104};
105
106template <int N>
107const char *lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex = 0)
108{
109 for (int i = 0; i < N; ++i) {
110 if (value == array[i].value)
111 return array[i].valueString;
112 }
113 const char *defaultValue = array[defaultIndex].valueString;
114 qWarning("uic: Warning: Invalid enumeration value %d, defaulting to %s",
115 value, defaultValue);
116 return defaultValue;
117}
118
119QString fixClassName(QString className)
120{
121 if (language() == Language::Python)
122 className.replace(cppQualifier, QLatin1String("_"));
123 return className;
124}
125
126const char *toolbarArea(int v)
127{
128 static const EnumLookup toolBarAreas[] =
129 {
130 {0, "NoToolBarArea"},
131 {0x1, "LeftToolBarArea"},
132 {0x2, "RightToolBarArea"},
133 {0x4, "TopToolBarArea"},
134 {0x8, "BottomToolBarArea"},
135 {0xf, "AllToolBarAreas"}
136 };
137 return lookupEnum(toolBarAreas, v);
138}
139
140const char *sizePolicy(int v)
141{
142 static const EnumLookup sizePolicies[] =
143 {
144 {0, "Fixed"},
145 {0x1, "Minimum"},
146 {0x4, "Maximum"},
147 {0x5, "Preferred"},
148 {0x3, "MinimumExpanding"},
149 {0x7, "Expanding"},
150 {0xD, "Ignored"}
151 };
152 return lookupEnum(sizePolicies, v, 3);
153}
154
155const char *dockWidgetArea(int v)
156{
157 static const EnumLookup dockWidgetAreas[] =
158 {
159 {0, "NoDockWidgetArea"},
160 {0x1, "LeftDockWidgetArea"},
161 {0x2, "RightDockWidgetArea"},
162 {0x4, "TopDockWidgetArea"},
163 {0x8, "BottomDockWidgetArea"},
164 {0xf, "AllDockWidgetAreas"}
165 };
166 return lookupEnum(dockWidgetAreas, v);
167}
168
169const char *paletteColorRole(int v)
170{
171 static const EnumLookup colorRoles[] =
172 {
173 {0, "WindowText"},
174 {1, "Button"},
175 {2, "Light"},
176 {3, "Midlight"},
177 {4, "Dark"},
178 {5, "Mid"},
179 {6, "Text"},
180 {7, "BrightText"},
181 {8, "ButtonText"},
182 {9, "Base"},
183 {10, "Window"},
184 {11, "Shadow"},
185 {12, "Highlight"},
186 {13, "HighlightedText"},
187 {14, "Link"},
188 {15, "LinkVisited"},
189 {16, "AlternateBase"},
190 {17, "NoRole"},
191 {18, "ToolTipBase"},
192 {19, "ToolTipText"},
193 {20, "PlaceholderText"},
194 };
195 return lookupEnum(colorRoles, v);
196}
197
198// Helpers for formatting a character sequences
199
200// Format a special character like '\x0a'
201static int formatEscapedNumber(QTextStream &str, ushort value, int base, int width,
202 char prefix = 0)
203{
204 int length = 1 + width;
205 str << '\\';
206 if (prefix) {
207 str << prefix;
208 ++length;
209 }
210 const auto oldPadChar = str.padChar();
211 const auto oldFieldWidth = str.fieldWidth();
212 const auto oldFieldAlignment = str.fieldAlignment();
213 const auto oldIntegerBase = str.integerBase();
214 str.setPadChar(QLatin1Char('0'));
215 str.setFieldWidth(width);
216 str.setFieldAlignment(QTextStream::AlignRight);
217 str.setIntegerBase(base);
218 str << value;
219 str.setIntegerBase(oldIntegerBase);
220 str.setFieldAlignment(oldFieldAlignment);
221 str.setFieldWidth(oldFieldWidth);
222 str.setPadChar(oldPadChar);
223 return length;
224}
225
226static int formatSpecialCharacter(QTextStream &str, ushort value)
227{
228 int length = 0;
229 switch (value) {
230 case '\\':
231 str << "\\\\";
232 length += 2;
233 break;
234 case '\"':
235 str << "\\\"";
236 length += 2;
237 break;
238 case '\n':
239 str << "\\n\"\n\"";
240 length += 5;
241 break;
242 default:
243 break;
244 }
245 return length;
246}
247
248// Format a sequence of characters for C++ with special characters numerically
249// escaped (non-raw string literals), wrappped at maxSegmentSize. FormattingTraits
250// are used to transform characters into (unsigned) codes, which can be used
251// for either normal escapes or Unicode code points as used in Unicode literals.
252
253enum : int { maxSegmentSize = 1024 };
254
255template <Encoding e>
256struct FormattingTraits
257{
258};
259
260template <>
261struct FormattingTraits<Encoding::Utf8>
262{
263 static ushort code(char c) { return uchar(c); }
264};
265
266template <>
267struct FormattingTraits<Encoding::Unicode>
268{
269 static ushort code(QChar c) { return c.unicode(); }
270};
271
272template <Encoding e, class Iterator>
273static void formatStringSequence(QTextStream &str, Iterator it, Iterator end,
274 const QString &indent,
275 int escapeIntegerBase, int escapeWidth,
276 char escapePrefix = 0)
277{
278 str << '"';
279 int length = 0;
280 while (it != end) {
281 const auto code = FormattingTraits<e>::code(*it);
282 if (code >= 0x80) {
283 length += formatEscapedNumber(str, code, escapeIntegerBase, escapeWidth, escapePrefix);
284 } else if (const int l = formatSpecialCharacter(str, code)) {
285 length += l;
286 } else if (code != '\r') {
287 str << *it;
288 ++length;
289 }
290 ++it;
291 if (it != end && length > maxSegmentSize) {
292 str << "\"\n" << indent << indent << '"';
293 length = 0;
294 }
295 }
296 str << '"';
297}
298
299void _formatString(QTextStream &str, const QString &value, const QString &indent,
300 bool qString)
301{
302 switch (encoding) {
303 // Special characters as 3 digit octal escapes (u8"\303\234mlaut")
304 case Encoding::Utf8: {
305 if (qString && _language == Language::Cpp)
306 str << "QString::fromUtf8(";
307 const QByteArray utf8 = value.toUtf8();
308 formatStringSequence<Encoding::Utf8>(str, utf8.cbegin(), utf8.cend(), indent,
309 8, 3);
310 if (qString && _language == Language::Cpp)
311 str << ')';
312 }
313 break;
314 // Special characters as 4 digit hex Unicode points (u8"\u00dcmlaut")
315 case Encoding::Unicode:
316 str << 'u'; // Python Unicode literal (would be UTF-16 in C++)
317 formatStringSequence<Encoding::Unicode>(str, value.cbegin(), value.cend(), indent,
318 16, 4, 'u');
319 break;
320 }
321}
322
323QTextStream &operator<<(QTextStream &str, const repeat &r)
324{
325 for (int i = 0; i < r.m_count; ++i)
326 str << r.m_char;
327 return str;
328}
329
330startFunctionDefinition1::startFunctionDefinition1(const char *name, const QString &parameterType,
331 const QString &parameterName,
332 const QString &indent,
333 const char *returnType) :
334 m_name(name), m_parameterType(parameterType), m_parameterName(parameterName),
335 m_indent(indent), m_return(returnType)
336{
337}
338
339QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f)
340{
341 switch (language()) {
342 case Language::Cpp:
343 str << (f.m_return ? f.m_return : "void") << ' ' << f.m_name << '('
344 << f.m_parameterType;
345 if (f.m_parameterType.cend()->isLetter())
346 str << ' ';
347 str << f.m_parameterName << ')' << '\n' << f.m_indent << "{\n";
348 break;
349 case Language::Python:
350 str << "def " << f.m_name << "(self, " << f.m_parameterName << "):\n";
351 break;
352 }
353 return str;
354}
355
356endFunctionDefinition::endFunctionDefinition(const char *name) : m_name(name)
357{
358}
359
360QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f)
361{
362 switch (language()) {
363 case Language::Cpp:
364 str << "} // " << f.m_name << "\n\n";
365 break;
366 case Language::Python:
367 str << "# " << f.m_name << "\n\n";
368 break;
369 }
370 return str;
371}
372
373void _formatStackVariable(QTextStream &str, const char *className, QStringView varName,
374 bool withInitParameters)
375{
376 switch (language()) {
377 case Language::Cpp:
378 str << className << ' ' << varName;
379 if (withInitParameters)
380 str << '(';
381 break;
382 case Language::Python:
383 str << varName << " = " << className << '(';
384 if (!withInitParameters)
385 str << ')';
386 break;
387 }
388}
389
390// Format a member function for a signal slot connection
391static void formatMemberFnPtr(QTextStream &str, const SignalSlot &s,
392 bool useQOverload = false)
393{
394 const int parenPos = s.signature.indexOf(QLatin1Char('('));
395 Q_ASSERT(parenPos >= 0);
396 if (useQOverload) {
397 const auto parameters = QStringView{s.signature}.mid(parenPos + 1,
398 s.signature.size() - parenPos - 2);
399 str << "qOverload<" << parameters << ">(";
400 }
401
402 const auto functionName = QStringView{s.signature}.left(parenPos);
403 str << '&' << s.className << "::" << functionName;
404
405 if (useQOverload)
406 str << ')';
407}
408
409static void formatMemberFnPtrConnection(QTextStream &str,
410 const SignalSlot &sender,
411 const SignalSlot &receiver)
412{
413 str << "QObject::connect(" << sender.name << ", ";
414 formatMemberFnPtr(str, sender);
415 str << ", " << receiver.name << ", ";
416 formatMemberFnPtr(str, receiver);
417 str << ')';
418}
419
420static void formatStringBasedConnection(QTextStream &str,
421 const SignalSlot &sender,
422 const SignalSlot &receiver)
423{
424 str << "QObject::connect(" << sender.name << ", SIGNAL("<< sender.signature
425 << "), " << receiver.name << ", SLOT(" << receiver.signature << "))";
426}
427
428void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSlot &receiver,
429 ConnectionSyntax connectionSyntax)
430{
431 switch (language()) {
432 case Language::Cpp:
433 switch (connectionSyntax) {
434 case ConnectionSyntax::MemberFunctionPtr:
435 formatMemberFnPtrConnection(str, sender, receiver);
436 break;
437 case ConnectionSyntax::StringBased:
438 formatStringBasedConnection(str, sender, receiver);
439 break;
440 }
441 break;
442 case Language::Python:
443 str << sender.name << '.'
444 << QStringView{sender.signature}.left(sender.signature.indexOf(QLatin1Char('(')))
445 << ".connect(" << receiver.name << '.'
446 << QStringView{receiver.signature}.left(receiver.signature.indexOf(QLatin1Char('(')))
447 << ')';
448 break;
449 }
450}
451
452QString boolValue(bool v)
453{
454 switch (language()) {
455 case Language::Cpp:
456 return v ? cppTrue : cppFalse;
457 case Language::Python:
458 return v ? QStringLiteral("True") : QStringLiteral("False");
459 }
460 Q_UNREACHABLE();
461}
462
463static inline QString dot() { return QStringLiteral("."); }
464
465QString enumValue(const QString &value)
466{
467 if (language() == Language::Cpp || !value.contains(cppQualifier))
468 return value;
469 QString fixed = value;
470 fixed.replace(cppQualifier, dot());
471 return fixed;
472}
473
474} // namespace language
475