blob: 207356f28dc3c79619e3947b6a38b2ce0356878c [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "uic.h"
#include "ui4.h"
#include "driver.h"
#include "option.h"
#include "treewalker.h"
#include "validator.h"
#include "cppwriteincludes.h"
#include "cppwritedeclaration.h"
#include <pythonwritedeclaration.h>
#include <pythonwriteimports.h>
#include <language.h>
#include <qxmlstream.h>
#include <qfileinfo.h>
#include <qscopedpointer.h>
#include <qtextstream.h>
QT_BEGIN_NAMESPACE
Uic::Uic(Driver *d)
: drv(d),
out(d->output()),
opt(d->option())
{
}
Uic::~Uic() = default;
bool Uic::printDependencies()
{
QString fileName = opt.inputFile;
QFile f;
if (fileName.isEmpty())
f.open(stdin, QIODevice::ReadOnly);
else {
f.setFileName(fileName);
if (!f.open(QIODevice::ReadOnly))
return false;
}
DomUI *ui = nullptr;
{
QXmlStreamReader reader;
reader.setDevice(&f);
ui = parseUiFile(reader);
if (!ui)
return false;
}
if (DomIncludes *includes = ui->elementIncludes()) {
const auto incls = includes->elementInclude();
for (DomInclude *incl : incls) {
QString file = incl->text();
if (file.isEmpty())
continue;
fprintf(stdout, "%s\n", file.toLocal8Bit().constData());
}
}
if (DomCustomWidgets *customWidgets = ui->elementCustomWidgets()) {
const auto elementCustomWidget = customWidgets->elementCustomWidget();
for (DomCustomWidget *customWidget : elementCustomWidget) {
if (DomHeader *header = customWidget->elementHeader()) {
QString file = header->text();
if (file.isEmpty())
continue;
fprintf(stdout, "%s\n", file.toLocal8Bit().constData());
}
}
}
delete ui;
return true;
}
void Uic::writeCopyrightHeaderCpp(const DomUI *ui) const
{
QString comment = ui->elementComment();
if (!comment.isEmpty())
out << "/*\n" << comment << "\n*/\n\n";
out << "/********************************************************************************\n";
out << "** Form generated from reading UI file '" << QFileInfo(opt.inputFile).fileName() << "'\n";
out << "**\n";
out << "** Created by: Qt User Interface Compiler version " << QT_VERSION_STR << "\n";
out << "**\n";
out << "** WARNING! All changes made in this file will be lost when recompiling UI file!\n";
out << "********************************************************************************/\n\n";
}
// Format existing UI file comments for Python with some smartness : Replace all
// leading C++ comment characters by '#' or prepend '#' if needed.
static inline bool isCppCommentChar(QChar c)
{
return c == QLatin1Char('/') || c == QLatin1Char('*');
}
static int leadingCppCommentCharCount(const QStringRef &s)
{
int i = 0;
for (const int size = s.size(); i < size && isCppCommentChar(s.at(i)); ++i) {
}
return i;
}
void Uic::writeCopyrightHeaderPython(const DomUI *ui) const
{
QString comment = ui->elementComment();
if (!comment.isEmpty()) {
const auto lines = comment.splitRef(QLatin1Char('\n'));
for (const auto &line : lines) {
if (const int leadingCommentChars = leadingCppCommentCharCount(line)) {
out << language::repeat(leadingCommentChars, '#')
<< line.right(line.size() - leadingCommentChars);
} else {
if (!line.startsWith(QLatin1Char('#')))
out << "# ";
out << line;
}
out << '\n';
}
out << '\n';
}
out << language::repeat(80, '#') << "\n## Form generated from reading UI file '"
<< QFileInfo(opt.inputFile).fileName()
<< "'\n##\n## Created by: Qt User Interface Compiler version " << QT_VERSION_STR
<< "\n##\n## WARNING! All changes made in this file will be lost when recompiling UI file!\n"
<< language::repeat(80, '#') << "\n\n";
}
// Check the version with a stream reader at the <ui> element.
static double versionFromUiAttribute(QXmlStreamReader &reader)
{
const QXmlStreamAttributes attributes = reader.attributes();
const QString versionAttribute = QLatin1String("version");
if (!attributes.hasAttribute(versionAttribute))
return 4.0;
const QStringRef version = attributes.value(versionAttribute);
return version.toDouble();
}
DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
{
DomUI *ui = nullptr;
const QString uiElement = QLatin1String("ui");
while (!reader.atEnd()) {
if (reader.readNext() == QXmlStreamReader::StartElement) {
if (reader.name().compare(uiElement, Qt::CaseInsensitive) == 0
&& !ui) {
const double version = versionFromUiAttribute(reader);
if (version < 4.0) {
const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Designer (%1)").arg(version);
fprintf(stderr, "%s\n", qPrintable(msg));
return nullptr;
}
ui = new DomUI();
ui->read(reader);
} else {
reader.raiseError(QLatin1String("Unexpected element ") + reader.name().toString());
}
}
}
if (reader.hasError()) {
delete ui;
ui = nullptr;
fprintf(stderr, "%s\n", qPrintable(QString::fromLatin1("uic: Error in line %1, column %2 : %3")
.arg(reader.lineNumber()).arg(reader.columnNumber())
.arg(reader.errorString())));
}
return ui;
}
bool Uic::write(QIODevice *in)
{
QScopedPointer<DomUI> ui;
{
QXmlStreamReader reader;
reader.setDevice(in);
ui.reset(parseUiFile(reader));
}
if (ui.isNull())
return false;
double version = ui->attributeVersion().toDouble();
if (version < 4.0) {
fprintf(stderr, "uic: File generated with too old version of Qt Designer\n");
return false;
}
const QString &language = ui->attributeLanguage();
driver()->setUseIdBasedTranslations(ui->attributeIdbasedtr());
if (!language.isEmpty() && language.compare(QLatin1String("c++"), Qt::CaseInsensitive) != 0) {
fprintf(stderr, "uic: File is not a \"c++\" ui file, language=%s\n", qPrintable(language));
return false;
}
return write(ui.data());
}
bool Uic::write(DomUI *ui)
{
if (!ui || !ui->elementWidget())
return false;
const auto lang = language::language();
if (lang == Language::Python)
out << "# -*- coding: utf-8 -*-\n\n";
if (opt.copyrightHeader) {
switch (language::language()) {
case Language::Cpp:
writeCopyrightHeaderCpp(ui);
break;
case Language::Python:
writeCopyrightHeaderPython(ui);
break;
}
}
if (opt.headerProtection && lang == Language::Cpp) {
writeHeaderProtectionStart();
out << "\n";
}
pixFunction = ui->elementPixmapFunction();
if (pixFunction == QLatin1String("QPixmap::fromMimeSource")
|| pixFunction == QLatin1String("qPixmapFromMimeSource")) {
fprintf(stderr, "%s: Warning: Obsolete pixmap function '%s' specified in the UI file.\n",
qPrintable(opt.messagePrefix()), qPrintable(pixFunction));
pixFunction.clear();
}
info.acceptUI(ui);
cWidgetsInfo.acceptUI(ui);
switch (language::language()) {
case Language::Cpp: {
CPP::WriteIncludes writeIncludes(this);
writeIncludes.acceptUI(ui);
Validator(this).acceptUI(ui);
CPP::WriteDeclaration(this).acceptUI(ui);
}
break;
case Language::Python: {
Python::WriteImports writeImports(this);
writeImports.acceptUI(ui);
Validator(this).acceptUI(ui);
Python::WriteDeclaration(this).acceptUI(ui);
}
break;
}
if (opt.headerProtection && lang == Language::Cpp)
writeHeaderProtectionEnd();
return true;
}
void Uic::writeHeaderProtectionStart()
{
QString h = drv->headerFileName();
out << "#ifndef " << h << "\n"
<< "#define " << h << "\n";
}
void Uic::writeHeaderProtectionEnd()
{
QString h = drv->headerFileName();
out << "#endif // " << h << "\n";
}
bool Uic::isButton(const QString &className) const
{
static const QStringList buttons = {
QLatin1String("QRadioButton"), QLatin1String("QToolButton"),
QLatin1String("QCheckBox"), QLatin1String("QPushButton"),
QLatin1String("QCommandLinkButton")
};
return customWidgetsInfo()->extendsOneOf(className, buttons);
}
bool Uic::isContainer(const QString &className) const
{
static const QStringList containers = {
QLatin1String("QStackedWidget"), QLatin1String("QToolBox"),
QLatin1String("QTabWidget"), QLatin1String("QScrollArea"),
QLatin1String("QMdiArea"), QLatin1String("QWizard"),
QLatin1String("QDockWidget")
};
return customWidgetsInfo()->extendsOneOf(className, containers);
}
bool Uic::isMenu(const QString &className) const
{
static const QStringList menus = {
QLatin1String("QMenu"), QLatin1String("QPopupMenu")
};
return customWidgetsInfo()->extendsOneOf(className, menus);
}
QT_END_NAMESPACE