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

source code of qtbase/src/tools/uic/shared/language.cpp