1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "uic.h"
30 #include "ui4.h"
31 #include "driver.h"
32 #include "option.h"
33 #include "treewalker.h"
34 #include "validator.h"
35
36 #include "cppwriteincludes.h"
37 #include "cppwritedeclaration.h"
38 #include <pythonwritedeclaration.h>
39 #include <pythonwriteimports.h>
40
41 #include <language.h>
42
43 #include <qxmlstream.h>
44 #include <qfileinfo.h>
45 #include <qscopedpointer.h>
46 #include <qtextstream.h>
47
48 QT_BEGIN_NAMESPACE
49
Uic(Driver * d)50 Uic::Uic(Driver *d)
51 : drv(d),
52 out(d->output()),
53 opt(d->option())
54 {
55 }
56
57 Uic::~Uic() = default;
58
printDependencies()59 bool Uic::printDependencies()
60 {
61 QString fileName = opt.inputFile;
62
63 QFile f;
64 if (fileName.isEmpty())
65 f.open(stdin, QIODevice::ReadOnly);
66 else {
67 f.setFileName(fileName);
68 if (!f.open(QIODevice::ReadOnly))
69 return false;
70 }
71
72 DomUI *ui = nullptr;
73 {
74 QXmlStreamReader reader;
75 reader.setDevice(&f);
76 ui = parseUiFile(reader);
77 if (!ui)
78 return false;
79 }
80
81 if (DomIncludes *includes = ui->elementIncludes()) {
82 const auto incls = includes->elementInclude();
83 for (DomInclude *incl : incls) {
84 QString file = incl->text();
85 if (file.isEmpty())
86 continue;
87
88 fprintf(stdout, "%s\n", file.toLocal8Bit().constData());
89 }
90 }
91
92 if (DomCustomWidgets *customWidgets = ui->elementCustomWidgets()) {
93 const auto elementCustomWidget = customWidgets->elementCustomWidget();
94 for (DomCustomWidget *customWidget : elementCustomWidget) {
95 if (DomHeader *header = customWidget->elementHeader()) {
96 QString file = header->text();
97 if (file.isEmpty())
98 continue;
99
100 fprintf(stdout, "%s\n", file.toLocal8Bit().constData());
101 }
102 }
103 }
104
105 delete ui;
106
107 return true;
108 }
109
writeCopyrightHeaderCpp(const DomUI * ui) const110 void Uic::writeCopyrightHeaderCpp(const DomUI *ui) const
111 {
112 QString comment = ui->elementComment();
113 if (!comment.isEmpty())
114 out << "/*\n" << comment << "\n*/\n\n";
115
116 out << "/********************************************************************************\n";
117 out << "** Form generated from reading UI file '" << QFileInfo(opt.inputFile).fileName() << "'\n";
118 out << "**\n";
119 out << "** Created by: Qt User Interface Compiler version " << QT_VERSION_STR << "\n";
120 out << "**\n";
121 out << "** WARNING! All changes made in this file will be lost when recompiling UI file!\n";
122 out << "********************************************************************************/\n\n";
123 }
124
125 // Format existing UI file comments for Python with some smartness : Replace all
126 // leading C++ comment characters by '#' or prepend '#' if needed.
127
isCppCommentChar(QChar c)128 static inline bool isCppCommentChar(QChar c)
129 {
130 return c == QLatin1Char('/') || c == QLatin1Char('*');
131 }
132
leadingCppCommentCharCount(const QStringRef & s)133 static int leadingCppCommentCharCount(const QStringRef &s)
134 {
135 int i = 0;
136 for (const int size = s.size(); i < size && isCppCommentChar(s.at(i)); ++i) {
137 }
138 return i;
139 }
140
writeCopyrightHeaderPython(const DomUI * ui) const141 void Uic::writeCopyrightHeaderPython(const DomUI *ui) const
142 {
143 QString comment = ui->elementComment();
144 if (!comment.isEmpty()) {
145 const auto lines = comment.splitRef(QLatin1Char('\n'));
146 for (const auto &line : lines) {
147 if (const int leadingCommentChars = leadingCppCommentCharCount(line)) {
148 out << language::repeat(leadingCommentChars, '#')
149 << line.right(line.size() - leadingCommentChars);
150 } else {
151 if (!line.startsWith(QLatin1Char('#')))
152 out << "# ";
153 out << line;
154 }
155 out << '\n';
156 }
157 out << '\n';
158 }
159
160 out << language::repeat(80, '#') << "\n## Form generated from reading UI file '"
161 << QFileInfo(opt.inputFile).fileName()
162 << "'\n##\n## Created by: Qt User Interface Compiler version " << QT_VERSION_STR
163 << "\n##\n## WARNING! All changes made in this file will be lost when recompiling UI file!\n"
164 << language::repeat(80, '#') << "\n\n";
165 }
166
167 // Check the version with a stream reader at the <ui> element.
168
versionFromUiAttribute(QXmlStreamReader & reader)169 static double versionFromUiAttribute(QXmlStreamReader &reader)
170 {
171 const QXmlStreamAttributes attributes = reader.attributes();
172 const QString versionAttribute = QLatin1String("version");
173 if (!attributes.hasAttribute(versionAttribute))
174 return 4.0;
175 const QStringRef version = attributes.value(versionAttribute);
176 return version.toDouble();
177 }
178
parseUiFile(QXmlStreamReader & reader)179 DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
180 {
181 DomUI *ui = nullptr;
182
183 const QString uiElement = QLatin1String("ui");
184 while (!reader.atEnd()) {
185 if (reader.readNext() == QXmlStreamReader::StartElement) {
186 if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0
187 && !ui) {
188 const double version = versionFromUiAttribute(reader);
189 if (version < 4.0) {
190 const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Designer (%1)").arg(version);
191 fprintf(stderr, "%s\n", qPrintable(msg));
192 return nullptr;
193 }
194
195 ui = new DomUI();
196 ui->read(reader);
197 } else {
198 reader.raiseError(QLatin1String("Unexpected element ") + reader.name().toString());
199 }
200 }
201 }
202 if (reader.hasError()) {
203 delete ui;
204 ui = nullptr;
205 fprintf(stderr, "%s\n", qPrintable(QString::fromLatin1("uic: Error in line %1, column %2 : %3")
206 .arg(reader.lineNumber()).arg(reader.columnNumber())
207 .arg(reader.errorString())));
208 }
209
210 return ui;
211 }
212
write(QIODevice * in)213 bool Uic::write(QIODevice *in)
214 {
215 QScopedPointer<DomUI> ui;
216 {
217 QXmlStreamReader reader;
218 reader.setDevice(in);
219 ui.reset(parseUiFile(reader));
220 }
221
222 if (ui.isNull())
223 return false;
224
225 double version = ui->attributeVersion().toDouble();
226 if (version < 4.0) {
227 fprintf(stderr, "uic: File generated with too old version of Qt Designer\n");
228 return false;
229 }
230
231 const QString &language = ui->attributeLanguage();
232 driver()->setUseIdBasedTranslations(ui->attributeIdbasedtr());
233
234 if (!language.isEmpty() && language.compare(QLatin1String("c++"), Qt::CaseInsensitive) != 0) {
235 fprintf(stderr, "uic: File is not a \"c++\" ui file, language=%s\n", qPrintable(language));
236 return false;
237 }
238
239 return write(ui.data());
240 }
241
write(DomUI * ui)242 bool Uic::write(DomUI *ui)
243 {
244 if (!ui || !ui->elementWidget())
245 return false;
246
247 const auto lang = language::language();
248
249 if (lang == Language::Python)
250 out << "# -*- coding: utf-8 -*-\n\n";
251
252 if (opt.copyrightHeader) {
253 switch (language::language()) {
254 case Language::Cpp:
255 writeCopyrightHeaderCpp(ui);
256 break;
257 case Language::Python:
258 writeCopyrightHeaderPython(ui);
259 break;
260 }
261 }
262
263 if (opt.headerProtection && lang == Language::Cpp) {
264 writeHeaderProtectionStart();
265 out << "\n";
266 }
267
268 pixFunction = ui->elementPixmapFunction();
269 if (pixFunction == QLatin1String("QPixmap::fromMimeSource")
270 || pixFunction == QLatin1String("qPixmapFromMimeSource")) {
271 fprintf(stderr, "%s: Warning: Obsolete pixmap function '%s' specified in the UI file.\n",
272 qPrintable(opt.messagePrefix()), qPrintable(pixFunction));
273 pixFunction.clear();
274 }
275
276 info.acceptUI(ui);
277 cWidgetsInfo.acceptUI(ui);
278
279 switch (language::language()) {
280 case Language::Cpp: {
281 CPP::WriteIncludes writeIncludes(this);
282 writeIncludes.acceptUI(ui);
283 Validator(this).acceptUI(ui);
284 CPP::WriteDeclaration(this).acceptUI(ui);
285 }
286 break;
287 case Language::Python: {
288 Python::WriteImports writeImports(this);
289 writeImports.acceptUI(ui);
290 Validator(this).acceptUI(ui);
291 Python::WriteDeclaration(this).acceptUI(ui);
292 }
293 break;
294 }
295
296 if (opt.headerProtection && lang == Language::Cpp)
297 writeHeaderProtectionEnd();
298
299 return true;
300 }
301
writeHeaderProtectionStart()302 void Uic::writeHeaderProtectionStart()
303 {
304 QString h = drv->headerFileName();
305 out << "#ifndef " << h << "\n"
306 << "#define " << h << "\n";
307 }
308
writeHeaderProtectionEnd()309 void Uic::writeHeaderProtectionEnd()
310 {
311 QString h = drv->headerFileName();
312 out << "#endif // " << h << "\n";
313 }
314
isButton(const QString & className) const315 bool Uic::isButton(const QString &className) const
316 {
317 static const QStringList buttons = {
318 QLatin1String("QRadioButton"), QLatin1String("QToolButton"),
319 QLatin1String("QCheckBox"), QLatin1String("QPushButton"),
320 QLatin1String("QCommandLinkButton")
321 };
322 return customWidgetsInfo()->extendsOneOf(className, buttons);
323 }
324
isContainer(const QString & className) const325 bool Uic::isContainer(const QString &className) const
326 {
327 static const QStringList containers = {
328 QLatin1String("QStackedWidget"), QLatin1String("QToolBox"),
329 QLatin1String("QTabWidget"), QLatin1String("QScrollArea"),
330 QLatin1String("QMdiArea"), QLatin1String("QWizard"),
331 QLatin1String("QDockWidget")
332 };
333
334 return customWidgetsInfo()->extendsOneOf(className, containers);
335 }
336
isMenu(const QString & className) const337 bool Uic::isMenu(const QString &className) const
338 {
339 static const QStringList menus = {
340 QLatin1String("QMenu"), QLatin1String("QPopupMenu")
341 };
342 return customWidgetsInfo()->extendsOneOf(className, menus);
343 }
344
345 QT_END_NAMESPACE
346