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

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