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 | |
33 | namespace language { |
34 | |
35 | static Encoding encoding = Encoding::Utf8; |
36 | static Language _language = Language::Cpp; |
37 | |
38 | Language language() { return _language; } |
39 | |
40 | void 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 | |
69 | QString derefPointer; |
70 | QString nullPtr; |
71 | QString operatorNew; |
72 | QString qtQualifier; |
73 | QString qualifier; |
74 | QString self; |
75 | QString eol; |
76 | QString emptyString; |
77 | |
78 | QString cppQualifier = QLatin1String("::" ); |
79 | QString cppTrue = QLatin1String("true" ); |
80 | QString cppFalse = QLatin1String("false" ); |
81 | |
82 | QTextStream &operator<<(QTextStream &str, const qtConfig &c) |
83 | { |
84 | str << "QT_CONFIG(" << c.parameter() << ')'; |
85 | return str; |
86 | } |
87 | |
88 | QTextStream &operator<<(QTextStream &str, const openQtConfig &c) |
89 | { |
90 | str << "#if " << qtConfig(c.parameter()) << '\n'; |
91 | return str; |
92 | } |
93 | |
94 | QTextStream &operator<<(QTextStream &str, const closeQtConfig &c) |
95 | { |
96 | str << "#endif // " << qtConfig(c.parameter()) << '\n'; |
97 | return str; |
98 | } |
99 | |
100 | struct EnumLookup |
101 | { |
102 | int value; |
103 | const char *valueString; |
104 | }; |
105 | |
106 | template <int N> |
107 | const 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 | |
119 | QString fixClassName(QString className) |
120 | { |
121 | if (language() == Language::Python) |
122 | className.replace(cppQualifier, QLatin1String("_" )); |
123 | return className; |
124 | } |
125 | |
126 | const 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 | |
140 | const 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 | |
155 | const 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 | |
169 | const 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' |
201 | static 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 | |
226 | static 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 | |
253 | enum : int { maxSegmentSize = 1024 }; |
254 | |
255 | template <Encoding e> |
256 | struct FormattingTraits |
257 | { |
258 | }; |
259 | |
260 | template <> |
261 | struct FormattingTraits<Encoding::Utf8> |
262 | { |
263 | static ushort code(char c) { return uchar(c); } |
264 | }; |
265 | |
266 | template <> |
267 | struct FormattingTraits<Encoding::Unicode> |
268 | { |
269 | static ushort code(QChar c) { return c.unicode(); } |
270 | }; |
271 | |
272 | template <Encoding e, class Iterator> |
273 | static 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 | |
299 | void _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 | |
323 | QTextStream &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 | |
330 | startFunctionDefinition1::startFunctionDefinition1(const char *name, const QString ¶meterType, |
331 | const QString ¶meterName, |
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 | |
339 | QTextStream &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 | |
356 | endFunctionDefinition::endFunctionDefinition(const char *name) : m_name(name) |
357 | { |
358 | } |
359 | |
360 | QTextStream &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 | |
373 | void _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 |
391 | static 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 | |
409 | static 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 | |
420 | static 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 | |
428 | void 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 | |
452 | QString 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 | |
463 | static inline QString dot() { return QStringLiteral("." ); } |
464 | |
465 | QString 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 | |