diff options
Diffstat (limited to 'src/tools')
52 files changed, 2587 insertions, 1139 deletions
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp index 3b78d2487f..3af5237f3d 100644 --- a/src/tools/androiddeployqt/main.cpp +++ b/src/tools/androiddeployqt/main.cpp @@ -43,6 +43,15 @@ #include <QRegExp> #include <algorithm> + +#ifdef Q_CC_MSVC +#define popen _popen +#define QT_POPEN_READ "rb" +#define pclose _pclose +#else +#define QT_POPEN_READ "r" +#endif + static const bool mustReadOutputAnyway = true; // pclose seems to return the wrong error code unless we read the output void deleteRecursively(const QString &dirName) @@ -70,7 +79,7 @@ FILE *openProcess(const QString &command) QString processedCommand = command; #endif - return popen(processedCommand.toLocal8Bit().constData(), "r"); + return popen(processedCommand.toLocal8Bit().constData(), QT_POPEN_READ); } struct QtDependency @@ -128,6 +137,7 @@ struct Options bool build; bool gradle; bool auxMode; + bool stripLibraries = true; QTime timer; // External tools @@ -147,6 +157,10 @@ struct Options QString rootPath; QStringList qmlImportPaths; + // Versioning + QString versionName; + QString versionCode; + // lib c++ path QString stdCppPath; QString stdCppName = QStringLiteral("gnustl_shared"); @@ -157,6 +171,7 @@ struct Options QString toolchainVersion; QString toolchainPrefix; QString toolPrefix; + bool useLLVM = false; QString ndkHost; // Package information @@ -436,6 +451,8 @@ Options parseOptions() options.generateAssetsFileList = false; } else if (argument.compare(QLatin1String("--aux-mode"), Qt::CaseInsensitive) == 0) { options.auxMode = true; + } else if (argument.compare(QLatin1String("--no-strip"), Qt::CaseInsensitive) == 0) { + options.stripLibraries = false; } } @@ -524,6 +541,7 @@ void printHelp() " --aux-mode: Operate in auxiliary mode. This will only copy the\n" " dependencies into the build directory and update the XML templates.\n" " The project will not be built or installed.\n" + " --no-strip: Do not strip debug symbols from libraries.\n" " --help: Displays this information.\n\n", qPrintable(QCoreApplication::arguments().at(0)) ); @@ -707,7 +725,7 @@ bool readInputFile(Options *options) return false; } - options->sdkPath = sdkPath.toString(); + options->sdkPath = QDir::fromNativeSeparators(sdkPath.toString()); if (options->androidPlatform.isEmpty()) { options->androidPlatform = detectLatestAndroidPlatform(options->sdkPath); @@ -752,6 +770,22 @@ bool readInputFile(Options *options) } { + const QJsonValue androidVersionName = jsonObject.value(QStringLiteral("android-version-name")); + if (!androidVersionName.isUndefined()) + options->versionName = androidVersionName.toString(); + else + options->versionName = QStringLiteral("1.0"); + } + + { + const QJsonValue androidVersionCode = jsonObject.value(QStringLiteral("android-version-code")); + if (!androidVersionCode.isUndefined()) + options->versionCode = androidVersionCode.toString(); + else + options->versionCode = QStringLiteral("1"); + } + + { const QJsonValue applicationBinary = jsonObject.value(QStringLiteral("application-binary")); if (applicationBinary.isUndefined()) { fprintf(stderr, "No application binary defined in json file.\n"); @@ -809,6 +843,11 @@ bool readInputFile(Options *options) } { + const QJsonValue value = jsonObject.value(QStringLiteral("useLLVM")); + options->useLLVM = value.toBool(false); + } + + { const QJsonValue toolchainPrefix = jsonObject.value(QStringLiteral("toolchain-prefix")); if (toolchainPrefix.isUndefined()) { fprintf(stderr, "No toolchain prefix defined in json file.\n"); @@ -827,7 +866,7 @@ bool readInputFile(Options *options) } } - { + if (!options->useLLVM) { const QJsonValue toolchainVersion = jsonObject.value(QStringLiteral("toolchain-version")); if (toolchainVersion.isUndefined()) { fprintf(stderr, "No toolchain version defined in json file.\n"); @@ -861,17 +900,19 @@ bool readInputFile(Options *options) options->extraPlugins = extraPlugins.toString().split(QLatin1Char(',')); } - { + if (!options->auxMode) { const QJsonValue stdcppPath = jsonObject.value(QStringLiteral("stdcpp-path")); - if (!stdcppPath.isUndefined()) { - options->stdCppPath = stdcppPath.toString(); - auto name = QFileInfo(options->stdCppPath).baseName(); - if (!name.startsWith(QLatin1String("lib"))) { - fprintf(stderr, "Invalid STD C++ library name.\n"); - return false; - } - options->stdCppName = name.mid(3); + if (stdcppPath.isUndefined()) { + fprintf(stderr, "No stdcpp-path defined in json file.\n"); + return false; + } + options->stdCppPath = stdcppPath.toString(); + auto name = QFileInfo(options->stdCppPath).baseName(); + if (!name.startsWith(QLatin1String("lib"))) { + fprintf(stderr, "Invalid STD C++ library name.\n"); + return false; } + options->stdCppName = name.mid(3); } { @@ -1220,7 +1261,7 @@ bool updateStringsXml(const Options &options) fprintf(stderr, "Can't open %s for writing.\n", qPrintable(fileName)); return false; } - file.write(QByteArray("<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\">") + file.write(QByteArray("<?xml version='1.0' encoding='utf-8'?><resources><string name=\"app_name\" translatable=\"false\">") .append(QFileInfo(options.applicationBinary).baseName().mid(sizeof("lib") - 1).toLatin1()) .append("</string></resources>\n")); return true; @@ -1312,6 +1353,8 @@ bool updateAndroidManifest(Options &options) replacements[QLatin1String("-- %%INSERT_LOCAL_LIBS%% --")] = localLibs.join(QLatin1Char(':')); replacements[QLatin1String("-- %%INSERT_LOCAL_JARS%% --")] = options.localJars.join(QLatin1Char(':')); replacements[QLatin1String("-- %%INSERT_INIT_CLASSES%% --")] = options.initClasses.join(QLatin1Char(':')); + replacements[QLatin1String("-- %%INSERT_VERSION_NAME%% --")] = options.versionName; + replacements[QLatin1String("-- %%INSERT_VERSION_CODE%% --")] = options.versionCode; replacements[QLatin1String("package=\"org.qtproject.example\"")] = QString::fromLatin1("package=\"%1\"").arg(options.packageName); replacements[QLatin1String("-- %%BUNDLE_LOCAL_QT_LIBS%% --")] = (options.deploymentMechanism == Options::Bundled) ? QString::fromLatin1("1") : QString::fromLatin1("0"); @@ -1357,8 +1400,8 @@ bool updateAndroidManifest(Options &options) options.packageName = reader.attributes().value(QLatin1String("package")).toString(); } else if (reader.name() == QLatin1String("uses-sdk")) { if (reader.attributes().hasAttribute(QLatin1String("android:minSdkVersion"))) - if (reader.attributes().value(QLatin1String("android:minSdkVersion")).toInt() < 16) { - fprintf(stderr, "Invalid minSdkVersion version, minSdkVersion must be >= 16\n"); + if (reader.attributes().value(QLatin1String("android:minSdkVersion")).toInt() < 21) { + fprintf(stderr, "Invalid minSdkVersion version, minSdkVersion must be >= 21\n"); return false; } } else if ((reader.name() == QLatin1String("application") || @@ -1546,14 +1589,16 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) { QString readElf = options.ndkPath + QLatin1String("/toolchains/") - + options.toolchainPrefix - + QLatin1Char('-') - + options.toolchainVersion - + QLatin1String("/prebuilt/") + + options.toolchainPrefix; + + if (!options.useLLVM) + readElf += QLatin1Char('-') + options.toolchainVersion; + + readElf += QLatin1String("/prebuilt/") + options.ndkHost + QLatin1String("/bin/") - + options.toolPrefix - + QLatin1String("-readelf"); + + options.toolPrefix + + (options.useLLVM ? QLatin1String("-readobj") : QLatin1String("-readelf")); #if defined(Q_OS_WIN32) readElf += QLatin1String(".exe"); #endif @@ -1563,27 +1608,40 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName) return QStringList(); } - readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf)).arg(shellQuote(fileName)); + if (options.useLLVM) + readElf = QString::fromLatin1("%1 -needed-libs %2").arg(shellQuote(readElf), shellQuote(fileName)); + else + readElf = QString::fromLatin1("%1 -d -W %2").arg(shellQuote(readElf), shellQuote(fileName)); FILE *readElfCommand = openProcess(readElf); - if (readElfCommand == 0) { + if (!readElfCommand) { fprintf(stderr, "Cannot execute command %s", qPrintable(readElf)); return QStringList(); } QStringList ret; + bool readLibs = false; char buffer[512]; while (fgets(buffer, sizeof(buffer), readElfCommand) != 0) { QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer)); - if (line.contains("(NEEDED)") && line.contains("Shared library:") ) { - const int pos = line.lastIndexOf('[') + 1; - QString libraryName = QLatin1String("lib/") + QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); - if (QFile::exists(absoluteFilePath(&options, libraryName))) { - ret += libraryName; + QString library; + if (options.useLLVM) { + line = line.trimmed(); + if (!readLibs) { + readLibs = line.startsWith("NeededLibraries"); + continue; } - + if (!line.startsWith("lib")) + continue; + library = QString::fromLatin1(line); + } else if (line.contains("(NEEDED)") && line.contains("Shared library:")) { + const int pos = line.lastIndexOf('[') + 1; + library = QString::fromLatin1(line.mid(pos, line.length() - pos - 2)); } + QString libraryName = QLatin1String("lib/") + library; + if (QFile::exists(absoluteFilePath(&options, libraryName))) + ret += libraryName; } pclose(readElfCommand); @@ -1664,7 +1722,7 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) QStringList importPaths; importPaths += shellQuote(options->qtInstallDirectory + QLatin1String("/qml")); - importPaths += rootPath; + importPaths += shellQuote(rootPath); for (const QString &qmlImportPath : qAsConst(options->qmlImportPaths)) importPaths += shellQuote(qmlImportPath); @@ -1672,7 +1730,7 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies) .arg(shellQuote(rootPath)) .arg(importPaths.join(QLatin1Char(' '))); - FILE *qmlImportScannerCommand = popen(qmlImportScanner.toLocal8Bit().constData(), "r"); + FILE *qmlImportScannerCommand = popen(qmlImportScanner.toLocal8Bit().constData(), QT_POPEN_READ); if (qmlImportScannerCommand == 0) { fprintf(stderr, "Couldn't run qmlimportscanner.\n"); return false; @@ -1837,10 +1895,12 @@ bool stripFile(const Options &options, const QString &fileName) { QString strip = options.ndkPath + QLatin1String("/toolchains/") - + options.toolchainPrefix - + QLatin1Char('-') - + options.toolchainVersion - + QLatin1String("/prebuilt/") + + options.toolchainPrefix; + + if (!options.useLLVM) + strip += QLatin1Char('-') + options.toolchainVersion; + + strip += QLatin1String("/prebuilt/") + options.ndkHost + QLatin1String("/bin/") + options.toolPrefix @@ -1854,7 +1914,10 @@ bool stripFile(const Options &options, const QString &fileName) return false; } - strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip)).arg(shellQuote(fileName)); + if (options.useLLVM) + strip = QString::fromLatin1("%1 -strip-all %2").arg(shellQuote(strip), shellQuote(fileName)); + else + strip = QString::fromLatin1("%1 %2").arg(shellQuote(strip), shellQuote(fileName)); FILE *stripCommand = openProcess(strip); if (stripCommand == 0) { @@ -1869,6 +1932,8 @@ bool stripFile(const Options &options, const QString &fileName) bool stripLibraries(const Options &options) { + if (!options.stripLibraries) + return true; if (options.verbose) fprintf(stdout, "Stripping libraries to minimize size.\n"); @@ -2104,7 +2169,7 @@ bool createAndroidProject(const Options &options) if (options.verbose) fprintf(stdout, " -- Command: %s\n", qPrintable(androidTool)); - FILE *androidToolCommand = popen(androidTool.toLocal8Bit().constData(), "r"); + FILE *androidToolCommand = popen(androidTool.toLocal8Bit().constData(), QT_POPEN_READ); if (androidToolCommand == 0) { fprintf(stderr, "Cannot run command '%s'\n", qPrintable(androidTool)); return false; @@ -2423,22 +2488,15 @@ bool copyStdCpp(Options *options) if (options->verbose) fprintf(stdout, "Copying STL library\n"); - QString filePath = !options->stdCppPath.isEmpty() ? options->stdCppPath - : options->ndkPath - + QLatin1String("/sources/cxx-stl/gnu-libstdc++/") - + options->toolchainVersion - + QLatin1String("/libs/") - + options->architecture - + QLatin1String("/libgnustl_shared.so"); - if (!QFile::exists(filePath)) { - fprintf(stderr, "STL library does not exist at %s\n", qPrintable(filePath)); + if (!QFile::exists(options->stdCppPath)) { + fprintf(stderr, "STL library does not exist at %s\n", qPrintable(options->stdCppPath)); return false; } const QString destinationDirectory = options->outputDirectory + QLatin1String("/libs/") + options->architecture; - if (!copyFileIfNewer(filePath, destinationDirectory + QLatin1String("/lib") + if (!copyFileIfNewer(options->stdCppPath, destinationDirectory + QLatin1String("/lib") + options->stdCppName + QLatin1String(".so"), options->verbose)) { return false; diff --git a/src/tools/bootstrap/CMakeLists.txt b/src/tools/bootstrap/CMakeLists.txt index 69e334c492..d171c1bcb5 100644 --- a/src/tools/bootstrap/CMakeLists.txt +++ b/src/tools/bootstrap/CMakeLists.txt @@ -79,17 +79,18 @@ extend_target(Bootstrap ../../corelib/tools/qstring_compat.cpp ../../corelib/tools/qstringbuilder.cpp ../../corelib/tools/qstringlist.cpp + ../../corelib/tools/qstringview.cpp ../../corelib/tools/qversionnumber.cpp ../../corelib/tools/qvsnprintf.cpp ../../xml/dom/qdom.cpp ../../xml/sax/qxml.cpp - PUBLIC_DEFINES # special case: - QT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} - QT_VERSION_MINOR=${PROJECT_VERSION_MINOR} - QT_VERSION_PATCH=${PROJECT_VERSION_PATCH} - QT_VERSION_STR="${PROJECT_VERSION}" + PUBLIC_DEFINES # special case + QT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} # special case + QT_VERSION_MINOR=${PROJECT_VERSION_MINOR} # special case + QT_VERSION_PATCH=${PROJECT_VERSION_PATCH} # special case + QT_VERSION_STR="${PROJECT_VERSION}" # special case QT_BOOTSTRAPPED - QT_NO_CAST_ASCII + QT_NO_CAST_TO_ASCII QT_NO_FOREACH QT_NO_CAST_FROM_ASCII PUBLIC_INCLUDE_DIRECTORIES # special case @@ -98,17 +99,16 @@ extend_target(Bootstrap PUBLIC_LIBRARIES # special case ZLIB::ZLIB Qt::Platform - # CONFIG = "minimal_syncqt" "internal_module" "force_bootstrap" "gc_binaries" - # INSTALLS = "lib" - # MODULE_CONFIG = "gc_binaries" - # MODULE_DEFINES = "QT_VERSION_STR=$$shell_quote\"$$QT_VERSION\"" "QT_VERSION_MAJOR=$$QT_MAJOR_VERSION" "QT_VERSION_MINOR=$$QT_MINOR_VERSION" "QT_VERSION_PATCH=$$QT_PATCH_VERSION" "QT_BOOTSTRAPPED" "QT_NO_CAST_TO_ASCII" - # MODULE_INCNAME = "QtCore" "QtXml" - # _LOADED = "qt_module" - # _OPTION = "host_build" - # lib.CONFIG = "dummy_install" ) -qt_internal_add_target_aliases(Bootstrap) # special case +#### Keys ignored in scope 1:.:.:bootstrap.pro:<TRUE>: +# CONFIG = "minimal_syncqt" "internal_module" "force_bootstrap" "gc_binaries" +# INSTALLS = "lib" +# MODULE_CONFIG = "gc_binaries" +# MODULE_INCNAME = "QtCore" "QtXml" +# _LOADED = "qt_module" +# _OPTION = "host_build" +# lib.CONFIG = "dummy_install" ## Scopes: ##################################################################### @@ -130,12 +130,12 @@ extend_target(Bootstrap CONDITION WIN32 ../../corelib/io/qstandardpaths_win.cpp ../../corelib/kernel/qcoreapplication_win.cpp ../../corelib/plugin/qsystemlibrary.cpp - LIBRARIES - user32 - ole32 + PUBLIC_LIBRARIES advapi32 - shell32 netapi32 + ole32 + shell32 + user32 ) extend_target(Bootstrap CONDITION APPLE_OSX @@ -148,6 +148,7 @@ extend_target(Bootstrap CONDITION APPLE_OSX ../../corelib/kernel/qcoreapplication_mac.cpp LIBRARIES ${FWCoreServices} + PUBLIC_LIBRARIES ${FWFoundation} ) @@ -165,11 +166,15 @@ extend_target(Bootstrap CONDITION UNIX AND NOT APPLE_OSX # extend_target(Bootstrap CONDITION cross_compile [...]) # extend_target(Bootstrap CONDITION NOT cross_compile [...]) +#### Keys ignored in scope 12:.:.:bootstrap.pro:else: +# CONFIG = "no_core_dep" + extend_target(Bootstrap CONDITION WIN32 AND mingw - LIBRARIES + PUBLIC_LIBRARIES uuid ) # special case: qt_internal_add_link_flags_gc_sections(Bootstrap PUBLIC) set_target_properties(Bootstrap PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF) +qt_internal_add_target_aliases(Bootstrap) diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 83e44ff9a4..1662e99674 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -19,8 +19,6 @@ DEFINES += \ QT_NO_FOREACH \ QT_NO_CAST_FROM_ASCII -DEFINES -= QT_EVAL - SOURCES += \ ../../corelib/codecs/qlatincodec.cpp \ ../../corelib/codecs/qtextcodec.cpp \ @@ -93,6 +91,7 @@ SOURCES += \ ../../corelib/tools/qstringbuilder.cpp \ ../../corelib/tools/qstring_compat.cpp \ ../../corelib/tools/qstringlist.cpp \ + ../../corelib/tools/qstringview.cpp \ ../../corelib/tools/qversionnumber.cpp \ ../../corelib/tools/qvsnprintf.cpp \ ../../xml/dom/qdom.cpp \ diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index c8882343f4..02c1fbd394 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -619,7 +619,7 @@ void Generator::generateCode() fprintf(out, "// a) You are using a NOTIFY signal that does not exist. Fix it.\n"); fprintf(out, "// b) You are using a NOTIFY signal that does exist (in a parent class) but has a non-empty parameter list. This is a moc limitation.\n"); fprintf(out, "Q_DECL_UNUSED static void checkNotifySignalValidity_%s(%s *t) {\n", qualifiedClassNameIdentifier.constData(), cdef->qualified.constData()); - for (const QByteArray &nonClassSignal : cdef->nonClassSignalList) + for (const QByteArray &nonClassSignal : qAsConst(cdef->nonClassSignalList)) fprintf(out, " t->%s();\n", nonClassSignal.constData()); fprintf(out, "}\n"); } @@ -1194,9 +1194,9 @@ void Generator::generateStaticMetacall() #ifndef QT_NO_DEBUG fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData()); } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData()); } fprintf(out, " Q_UNUSED(_t)\n"); fprintf(out, " switch (_id) {\n"); @@ -1359,9 +1359,9 @@ void Generator::generateStaticMetacall() #ifndef QT_NO_DEBUG fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData()); } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData()); } fprintf(out, " Q_UNUSED(_t)\n"); if (needTempVarForGet) @@ -1405,9 +1405,9 @@ void Generator::generateStaticMetacall() #ifndef QT_NO_DEBUG fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n"); #endif - fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData()); } else { - fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData()); + fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData()); } fprintf(out, " Q_UNUSED(_t)\n"); fprintf(out, " void *_v = _a[0];\n"); diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 134166580b..eae0353199 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -39,7 +39,7 @@ class Generator ClassDef *cdef; QVector<uint> meta_data; public: - Generator(ClassDef *classDef, const QVector<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = 0); + Generator(ClassDef *classDef, const QVector<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = nullptr); void generateCode(); private: bool registerableMetaType(const QByteArray &propertyType); diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 76816960ff..2f52dd6ca0 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -159,6 +159,7 @@ Type Moc::parseType() bool isVoid = false; type.firstToken = lookup(); for (;;) { + skipCxxAttributes(); switch (next()) { case SIGNED: case UNSIGNED: @@ -188,8 +189,11 @@ Type Moc::parseType() } break; } + + skipCxxAttributes(); test(ENUM) || test(CLASS) || test(STRUCT); for(;;) { + skipCxxAttributes(); switch (next()) { case IDENTIFIER: // void mySlot(unsigned myArg) @@ -260,7 +264,7 @@ bool Moc::parseEnum(EnumDef *def) { bool isTypdefEnum = false; // typedef enum { ... } Foo; - if (test(CLASS)) + if (test(CLASS) || test(STRUCT)) def->isEnumClass = true; if (test(IDENTIFIER)) { @@ -281,6 +285,7 @@ bool Moc::parseEnum(EnumDef *def) break; next(IDENTIFIER); def->values += lexem(); + skipCxxAttributes(); } while (test(EQ) ? until(COMMA) : test(COMMA)); next(RBRACE); if (isTypdefEnum) { @@ -356,6 +361,15 @@ bool Moc::testFunctionAttribute(Token tok, FunctionDef *def) return false; } +bool Moc::skipCxxAttributes() +{ + auto rewind = index; + if (test(LBRACK) && test(LBRACK) && until(RBRACK) && test(RBRACK)) + return true; + index = rewind; + return false; +} + bool Moc::testFunctionRevision(FunctionDef *def) { if (test(Q_REVISION_TOKEN)) { @@ -381,7 +395,7 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro) //skip modifiers and attributes while (test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) || (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual - || testFunctionAttribute(def) || testFunctionRevision(def)) {} + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} bool templateFunction = (lookup() == TEMPLATE); def->type = parseType(); if (def->type.name.isEmpty()) { @@ -454,10 +468,11 @@ bool Moc::parseFunction(FunctionDef *def, bool inMacro) until(RBRACE); else if ((def->isAbstract = test(EQ))) until(SEMIC); + else if (skipCxxAttributes()) + until(SEMIC); else error(); } - if (scopedFunctionName) { const QByteArray msg = "Function declaration " + def->name + " contains extra qualification. Ignoring as signal or slot."; @@ -475,7 +490,7 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def) //skip modifiers and attributes while (test(EXPLICIT) || test(INLINE) || (test(STATIC) && (def->isStatic = true) == true) || (test(VIRTUAL) && (def->isVirtual = true) == true) //mark as virtual - || testFunctionAttribute(def) || testFunctionRevision(def)) {} + || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {} bool tilde = test(TILDE); def->type = parseType(); if (def->type.name.isEmpty()) @@ -565,6 +580,7 @@ void Moc::parse() } else if (!test(SEMIC)) { NamespaceDef def; def.classname = nsName; + def.doGenerate = currentFilenames.size() <= 1; next(LBRACE); def.begin = index - 1; @@ -572,25 +588,22 @@ void Moc::parse() def.end = index; index = def.begin + 1; - const bool parseNamespace = currentFilenames.size() <= 1; - if (parseNamespace) { - for (int i = namespaceList.size() - 1; i >= 0; --i) { - if (inNamespace(&namespaceList.at(i))) { - def.qualified.prepend(namespaceList.at(i).classname + "::"); - } - } - for (const QByteArray &ns : nested) { - NamespaceDef parentNs; - parentNs.classname = ns; - parentNs.qualified = def.qualified; - def.qualified += ns + "::"; - parentNs.begin = def.begin; - parentNs.end = def.end; - namespaceList += parentNs; + for (int i = namespaceList.size() - 1; i >= 0; --i) { + if (inNamespace(&namespaceList.at(i))) { + def.qualified.prepend(namespaceList.at(i).classname + "::"); } } + for (const QByteArray &ns : nested) { + NamespaceDef parentNs; + parentNs.classname = ns; + parentNs.qualified = def.qualified; + def.qualified += ns + "::"; + parentNs.begin = def.begin; + parentNs.end = def.end; + namespaceList += parentNs; + } - while (parseNamespace && inNamespace(&def) && hasNext()) { + while (inNamespace(&def) && hasNext()) { switch (next()) { case NAMESPACE: if (test(IDENTIFIER)) { @@ -915,7 +928,8 @@ void Moc::parse() } else { knownGadgets.insert(def.classname, def.qualified); knownGadgets.insert(def.qualified, def.qualified); - classList += def; + if (n.doGenerate) + classList += def; } } } diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index d98c73e1a0..2bba8a5bb9 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -83,57 +83,53 @@ Q_DECLARE_TYPEINFO(ArgumentDef, Q_MOVABLE_TYPE); struct FunctionDef { - FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), isStatic(false), - inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false), - isScriptable(false), isSlot(false), isSignal(false), isPrivateSignal(false), - isConstructor(false), isDestructor(false), isAbstract(false), revision(0) {} Type type; + QVector<ArgumentDef> arguments; QByteArray normalizedType; QByteArray tag; QByteArray name; - bool returnTypeIsVolatile; - - QVector<ArgumentDef> arguments; + QByteArray inPrivateClass; enum Access { Private, Protected, Public }; - Access access; - bool isConst; - bool isVirtual; - bool isStatic; - bool inlineCode; - bool wasCloned; - - QByteArray inPrivateClass; - bool isCompat; - bool isInvokable; - bool isScriptable; - bool isSlot; - bool isSignal; - bool isPrivateSignal; - bool isConstructor; - bool isDestructor; - bool isAbstract; - - int revision; + Access access = Private; + int revision = 0; + + bool isConst = false; + bool isVirtual = false; + bool isStatic = false; + bool inlineCode = false; + bool wasCloned = false; + + bool returnTypeIsVolatile = false; + + bool isCompat = false; + bool isInvokable = false; + bool isScriptable = false; + bool isSlot = false; + bool isSignal = false; + bool isPrivateSignal = false; + bool isConstructor = false; + bool isDestructor = false; + bool isAbstract = false; }; Q_DECLARE_TYPEINFO(FunctionDef, Q_MOVABLE_TYPE); struct PropertyDef { - PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){} - QByteArray name, type, member, read, write, reset, designable, scriptable, editable, stored, user, notify, inPrivateClass; - int notifyId; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class - bool constant; - bool final; - enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; - Specification gspec; bool stdCppSet() const { QByteArray s("set"); s += toupper(name[0]); s += name.mid(1); return (s == write); } - int revision; + + QByteArray name, type, member, read, write, reset, designable, scriptable, editable, stored, user, notify, inPrivateClass; + int notifyId = -1; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class + enum Specification { ValueSpec, ReferenceSpec, PointerSpec }; + Specification gspec = ValueSpec; + int revision = 0; + bool constant = false; + bool final = false; }; Q_DECLARE_TYPEINFO(PropertyDef, Q_MOVABLE_TYPE); @@ -169,9 +165,6 @@ struct ClassDef : BaseDef { }; QVector<QVector<Interface> >interfaceList; - bool hasQObject = false; - bool hasQGadget = false; - struct PluginData { QByteArray iid; QMap<QString, QJsonArray> metaArgs; @@ -181,17 +174,21 @@ struct ClassDef : BaseDef { QVector<FunctionDef> constructorList; QVector<FunctionDef> signalList, slotList, methodList, publicList; QVector<QByteArray> nonClassSignalList; - int notifyableProperties = 0; QVector<PropertyDef> propertyList; + int notifyableProperties = 0; int revisionedMethods = 0; int revisionedProperties = 0; + bool hasQObject = false; + bool hasQGadget = false; + }; Q_DECLARE_TYPEINFO(ClassDef, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(ClassDef::Interface, Q_MOVABLE_TYPE); struct NamespaceDef : BaseDef { bool hasQNamespace = false; + bool doGenerate = false; }; Q_DECLARE_TYPEINFO(NamespaceDef, Q_MOVABLE_TYPE); @@ -260,6 +257,8 @@ public: bool testFunctionAttribute(Token tok, FunctionDef *def); bool testFunctionRevision(FunctionDef *def); + bool skipCxxAttributes(); + void checkSuperClasses(ClassDef *def); void checkProperties(ClassDef* cdef); }; diff --git a/src/tools/moc/parser.h b/src/tools/moc/parser.h index bedcbbf7e2..63f4cf0d9a 100644 --- a/src/tools/moc/parser.h +++ b/src/tools/moc/parser.h @@ -69,9 +69,9 @@ public: inline const Symbol &symbol() { return symbols.at(index-1);} Q_NORETURN void error(int rollback); - Q_NORETURN void error(const char *msg = 0); - void warning(const char * = 0); - void note(const char * = 0); + Q_NORETURN void error(const char *msg = nullptr); + void warning(const char * = nullptr); + void note(const char * = nullptr); }; diff --git a/src/tools/moc/util/generate.sh b/src/tools/moc/util/generate.sh index 3894be1309..5460d28924 100755 --- a/src/tools/moc/util/generate.sh +++ b/src/tools/moc/util/generate.sh @@ -27,6 +27,8 @@ ## ############################################################################# +set -ex + qmake make cat licenseheader.txt > ../keywords.cpp diff --git a/src/tools/moc/util/generate_keywords.pro b/src/tools/moc/util/generate_keywords.pro index 88e5553f54..2bbc3ced61 100644 --- a/src/tools/moc/util/generate_keywords.pro +++ b/src/tools/moc/util/generate_keywords.pro @@ -1,4 +1,4 @@ CONFIG -= moc -mac:CONFIG -= app_bundle +CONFIG += cmdline SOURCES += generate_keywords.cpp diff --git a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp index 2115a14adf..7c6f0bdeef 100644 --- a/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp +++ b/src/tools/qdbuscpp2xml/qdbuscpp2xml.cpp @@ -62,7 +62,7 @@ static const char docTypeHeader[] = #define PROGRAMNAME "qdbuscpp2xml" #define PROGRAMVERSION "0.2" -#define PROGRAMCOPYRIGHT "Copyright (C) 2018 The Qt Company Ltd." +#define PROGRAMCOPYRIGHT "Copyright (C) 2019 The Qt Company Ltd." static QString outputFile; static int flags; @@ -212,8 +212,11 @@ static QString generateInterfaceXml(const ClassDef *mo) access |= 2; int typeId = QMetaType::type(mp.type.constData()); - if (!typeId) + if (!typeId) { + fprintf(stderr, PROGRAMNAME ": unregistered type: '%s', ignoring\n", + mp.type.constData()); continue; + } const char *signature = QDBusMetaType::typeToSignature(typeId); if (!signature) continue; diff --git a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp index 5b76502c94..ea410cd257 100644 --- a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp +++ b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp @@ -46,7 +46,7 @@ #define PROGRAMNAME "qdbusxml2cpp" #define PROGRAMVERSION "0.8" -#define PROGRAMCOPYRIGHT "Copyright (C) 2018 The Qt Company Ltd." +#define PROGRAMCOPYRIGHT "Copyright (C) 2019 The Qt Company Ltd." #define ANNOTATION_NO_WAIT "org.freedesktop.DBus.Method.NoReply" @@ -326,7 +326,6 @@ static void writeArgList(QTextStream &ts, const QStringList &argNames, // yes, starting from 1 for (int i = 1; i < outputArgs.count(); ++i) { const QDBusIntrospection::Argument &arg = outputArgs.at(i); - QString name = arg.name; if (!first) ts << ", "; @@ -531,8 +530,6 @@ static void writeProxy(const QString &filename, const QDBusIntrospection::Interf // properties: for (const QDBusIntrospection::Property &property : interface->properties) { QByteArray type = qtTypeName(property.type, property.annotations); - QString templateType = templateArg(type); - QString constRefType = constRefArg(type); QString getter = propertyGetter(property); QString setter = propertySetter(property); diff --git a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp index a9bf7e86e4..17fc978039 100644 --- a/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp +++ b/src/tools/qfloat16-tables/gen_qfloat16_tables.cpp @@ -79,6 +79,7 @@ qint32 main(qint32 argc, char **argv) fid.write("#include <QtCore/qfloat16.h>\n\n"); fid.write("QT_BEGIN_NAMESPACE\n\n"); + fid.write("#if !defined(__F16C__) && !defined(__ARM_FP16_FORMAT_IEEE)\n\n"); fid.write("const quint32 qfloat16::mantissatable[2048] = {\n"); fid.write("0,\n"); @@ -155,6 +156,7 @@ qint32 main(qint32 argc, char **argv) fid.write("};\n\n"); + fid.write("#endif // !__F16C__ && !__ARM_FP16_FORMAT_IEEE\n\n"); fid.write("QT_END_NAMESPACE\n"); fid.close(); return 0; diff --git a/src/tools/qlalr/cppgenerator.cpp b/src/tools/qlalr/cppgenerator.cpp index b14c73a8fe..508db696b1 100644 --- a/src/tools/qlalr/cppgenerator.cpp +++ b/src/tools/qlalr/cppgenerator.cpp @@ -460,7 +460,7 @@ void CppGenerator::generateDecl (QTextStream &out) << "public:" << endl << " enum VariousConstants {" << endl; - for (Name t : qAsConst(grammar.terminals)) + for (const Name &t : qAsConst(grammar.terminals)) { QString name = *t; int value = std::distance (grammar.names.begin (), t); diff --git a/src/tools/qlalr/lalr.h b/src/tools/qlalr/lalr.h index 6a2baaa462..8eadee400d 100644 --- a/src/tools/qlalr/lalr.h +++ b/src/tools/qlalr/lalr.h @@ -61,6 +61,8 @@ public: public: const_iterator () {} + const_iterator (const typename _Base::iterator &it): + _M_iterator (typename _Base::const_iterator(it)) {} const_iterator (const typename _Base::const_iterator &it): _M_iterator (it) {} diff --git a/src/tools/qvkgen/qvkgen.cpp b/src/tools/qvkgen/qvkgen.cpp index 059f9413cb..4db3f26161 100644 --- a/src/tools/qvkgen/qvkgen.cpp +++ b/src/tools/qvkgen/qvkgen.cpp @@ -192,22 +192,20 @@ QString VkSpecParser::parseName() QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr) { - QString s; - s.sprintf("%s %s%s%s", qPrintable(c.cmd.type), - (className ? className : ""), (className ? "::" : ""), - qPrintable(c.cmd.name)); + QString s(QString::asprintf("%s %s%s%s", qPrintable(c.cmd.type), + (className ? className : ""), (className ? "::" : ""), + qPrintable(c.cmd.name))); if (!c.args.isEmpty()) { s += QLatin1Char('('); bool first = true; for (const VkSpecParser::TypedName &a : c.args) { - QString argStr; - argStr.sprintf("%s%s%s%s", qPrintable(a.type), (a.type.endsWith(QLatin1Char('*')) ? "" : " "), - qPrintable(a.name), qPrintable(a.typeSuffix)); if (!first) s += QStringLiteral(", "); else first = false; - s += argStr; + s += QString::asprintf("%s%s%s%s", qPrintable(a.type), + (a.type.endsWith(QLatin1Char('*')) ? "" : " "), + qPrintable(a.name), qPrintable(a.typeSuffix)); } s += QLatin1Char(')'); } @@ -216,13 +214,12 @@ QString funcSig(const VkSpecParser::Command &c, const char *className = nullptr) QString funcCall(const VkSpecParser::Command &c, int idx) { - QString s; // template: // [return] reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(d_ptr->m_funcs[0])(instance, pPhysicalDeviceCount, pPhysicalDevices); - s.sprintf("%sreinterpret_cast<PFN_%s>(d_ptr->m_funcs[%d])", - (c.cmd.type == QStringLiteral("void") ? "" : "return "), - qPrintable(c.cmd.name), - idx); + QString s = QString::asprintf("%sreinterpret_cast<PFN_%s>(d_ptr->m_funcs[%d])", + (c.cmd.type == QStringLiteral("void") ? "" : "return "), + qPrintable(c.cmd.name), + idx); if (!c.args.isEmpty()) { s += QLatin1Char('('); bool first = true; @@ -338,10 +335,9 @@ bool genVulkanFunctionsH(const QVector<VkSpecParser::Command> &commands, const Q *dst += QStringLiteral(";\n"); } - QString str; - str.sprintf(s, preamble.get(licHeaderFn).constData(), instCmdStr.toUtf8().constData(), devCmdStr.toUtf8().constData()); - - f.write(str.toUtf8()); + f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(), + instCmdStr.toUtf8().constData(), + devCmdStr.toUtf8().constData()).toUtf8()); return true; } @@ -400,10 +396,7 @@ bool genVulkanFunctionsPH(const QVector<VkSpecParser::Command> &commands, const [](const VkSpecParser::Command &c) { return c.deviceLevel; }); const int instLevelCount = commands.count() - devLevelCount; - QString str; - str.sprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount); - - f.write(str.toUtf8()); + f.write(QString::asprintf(s, preamble.get(licHeaderFn).constData(), instLevelCount, devLevelCount).toUtf8()); return true; } @@ -478,10 +471,12 @@ bool genVulkanFunctionsPC(const QVector<VkSpecParser::Command> &commands, const if (instCmdNamesStr.count() > 2) instCmdNamesStr = instCmdNamesStr.left(instCmdNamesStr.count() - 2); - QString str; - str.sprintf(s, preamble.get(licHeaderFn).constData(), - instCmdWrapperStr.toUtf8().constData(), instCmdNamesStr.toUtf8().constData(), instIdx, - devCmdWrapperStr.toUtf8().constData(), devCmdNamesStr.toUtf8().constData(), commands.count() - instIdx); + const QString str = + QString::asprintf(s, preamble.get(licHeaderFn).constData(), + instCmdWrapperStr.toUtf8().constData(), + instCmdNamesStr.toUtf8().constData(), instIdx, + devCmdWrapperStr.toUtf8().constData(), + devCmdNamesStr.toUtf8().constData(), commands.count() - instIdx); f.write(str.toUtf8()); diff --git a/src/tools/rcc/main.cpp b/src/tools/rcc/main.cpp index 12f986b1e2..0eb6766b5a 100644 --- a/src/tools/rcc/main.cpp +++ b/src/tools/rcc/main.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. @@ -127,10 +128,25 @@ int runRcc(int argc, char *argv[]) QCommandLineOption rootOption(QStringLiteral("root"), QStringLiteral("Prefix resource access path with root path."), QStringLiteral("path")); parser.addOption(rootOption); +#if QT_CONFIG(zstd) && !defined(QT_NO_COMPRESS) +# define ALGOS "[zstd], zlib, none" +#elif QT_CONFIG(zstd) +# define ALGOS "[zstd], none" +#elif !defined(QT_NO_COMPRESS) +# define ALGOS "[zlib], none" +#else +# define ALGOS "[none]" +#endif + const QString &algoDescription = + QStringLiteral("Compress input files using algorithm <algo> (" ALGOS ")."); + QCommandLineOption compressionAlgoOption(QStringLiteral("compress-algo"), algoDescription, QStringLiteral("algo")); + parser.addOption(compressionAlgoOption); +#undef ALGOS + QCommandLineOption compressOption(QStringLiteral("compress"), QStringLiteral("Compress input files by <level>."), QStringLiteral("level")); parser.addOption(compressOption); - QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression.")); + QCommandLineOption nocompressOption(QStringLiteral("no-compress"), QStringLiteral("Disable all compression. Same as --compress-algo=none.")); parser.addOption(nocompressOption); QCommandLineOption thresholdOption(QStringLiteral("threshold"), QStringLiteral("Threshold to consider compressing files."), QStringLiteral("level")); @@ -139,6 +155,11 @@ int runRcc(int argc, char *argv[]) QCommandLineOption binaryOption(QStringLiteral("binary"), QStringLiteral("Output a binary file for use as a dynamic resource.")); parser.addOption(binaryOption); + QCommandLineOption generatorOption(QStringList{QStringLiteral("g"), QStringLiteral("generator")}); + generatorOption.setDescription(QStringLiteral("Select generator.")); + generatorOption.setValueName(QStringLiteral("cpp|python|python2")); + parser.addOption(generatorOption); + QCommandLineOption passOption(QStringLiteral("pass"), QStringLiteral("Pass number for big resources"), QStringLiteral("number")); parser.addOption(passOption); @@ -169,13 +190,13 @@ int runRcc(int argc, char *argv[]) QString errorMsg; - quint8 formatVersion = 2; + quint8 formatVersion = 3; if (parser.isSet(formatVersionOption)) { bool ok = false; formatVersion = parser.value(formatVersionOption).toUInt(&ok); if (!ok) { errorMsg = QLatin1String("Invalid format version specified"); - } else if (formatVersion != 1 && formatVersion != 2) { + } else if (formatVersion < 1 || formatVersion > 3) { errorMsg = QLatin1String("Unsupported format version specified"); } } @@ -189,14 +210,33 @@ int runRcc(int argc, char *argv[]) || library.resourceRoot().at(0) != QLatin1Char('/')) errorMsg = QLatin1String("Root must start with a /"); } - if (parser.isSet(compressOption)) - library.setCompressLevel(parser.value(compressOption).toInt()); + + if (parser.isSet(compressionAlgoOption)) + library.setCompressionAlgorithm(RCCResourceLibrary::parseCompressionAlgorithm(parser.value(compressionAlgoOption), &errorMsg)); + if (formatVersion < 3 && library.compressionAlgorithm() == RCCResourceLibrary::CompressionAlgorithm::Zstd) + errorMsg = QLatin1String("Zstandard compression requires format version 3 or higher"); if (parser.isSet(nocompressOption)) - library.setCompressLevel(-2); + library.setCompressionAlgorithm(RCCResourceLibrary::CompressionAlgorithm::None); + if (parser.isSet(compressOption) && errorMsg.isEmpty()) { + int level = library.parseCompressionLevel(library.compressionAlgorithm(), parser.value(compressOption), &errorMsg); + library.setCompressLevel(level); + } if (parser.isSet(thresholdOption)) library.setCompressThreshold(parser.value(thresholdOption).toInt()); if (parser.isSet(binaryOption)) library.setFormat(RCCResourceLibrary::Binary); + if (parser.isSet(generatorOption)) { + auto value = parser.value(generatorOption); + if (value == QLatin1String("cpp")) + library.setFormat(RCCResourceLibrary::C_Code); + else if (value == QLatin1String("python")) + library.setFormat(RCCResourceLibrary::Python3_Code); + else if (value == QLatin1String("python2")) + library.setFormat(RCCResourceLibrary::Python2_Code); + else + errorMsg = QLatin1String("Invalid generator: ") + value; + } + if (parser.isSet(passOption)) { if (parser.value(passOption) == QLatin1String("1")) library.setFormat(RCCResourceLibrary::Pass1); @@ -257,6 +297,8 @@ int runRcc(int argc, char *argv[]) switch (library.format()) { case RCCResourceLibrary::C_Code: case RCCResourceLibrary::Pass1: + case RCCResourceLibrary::Python3_Code: + case RCCResourceLibrary::Python2_Code: mode = QIODevice::WriteOnly | QIODevice::Text; break; case RCCResourceLibrary::Pass2: diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp index 1a7cab01df..bb4e6e3615 100644 --- a/src/tools/rcc/rcc.cpp +++ b/src/tools/rcc/rcc.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. @@ -41,6 +42,10 @@ #include <algorithm> +#if QT_CONFIG(zstd) +# include <zstd.h> +#endif + // Note: A copy of this file is used in Qt Designer (qttools/src/designer/src/lib/shared/rcc.cpp) QT_BEGIN_NAMESPACE @@ -48,9 +53,18 @@ QT_BEGIN_NAMESPACE enum { CONSTANT_USENAMESPACE = 1, CONSTANT_COMPRESSLEVEL_DEFAULT = -1, + CONSTANT_ZSTDCOMPRESSLEVEL_CHECK = 1, // Zstd level to check if compressing is a good idea + CONSTANT_ZSTDCOMPRESSLEVEL_STORE = 14, // Zstd level to actually store the data CONSTANT_COMPRESSTHRESHOLD_DEFAULT = 70 }; +#if QT_CONFIG(zstd) +# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zstd +#elif !defined(QT_NO_COMPRESS) +# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::Zlib +#else +# define CONSTANT_COMPRESSALGO_DEFAULT RCCResourceLibrary::CompressionAlgorithm::None +#endif #define writeString(s) write(s, sizeof(s)) @@ -88,15 +102,18 @@ class RCCFileInfo public: enum Flags { + // must match qresource.cpp NoFlags = 0x00, Compressed = 0x01, - Directory = 0x02 + Directory = 0x02, + CompressedZstd = 0x04 }; RCCFileInfo(const QString &name = QString(), const QFileInfo &fileInfo = QFileInfo(), QLocale::Language language = QLocale::C, QLocale::Country country = QLocale::AnyCountry, uint flags = NoFlags, + RCCResourceLibrary::CompressionAlgorithm compressAlgo = CONSTANT_COMPRESSALGO_DEFAULT, int compressLevel = CONSTANT_COMPRESSLEVEL_DEFAULT, int compressThreshold = CONSTANT_COMPRESSTHRESHOLD_DEFAULT); ~RCCFileInfo(); @@ -115,6 +132,7 @@ public: QFileInfo m_fileInfo; RCCFileInfo *m_parent; QHash<QString, RCCFileInfo*> m_children; + RCCResourceLibrary::CompressionAlgorithm m_compressAlgo; int m_compressLevel; int m_compressThreshold; @@ -125,7 +143,7 @@ public: RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo, QLocale::Language language, QLocale::Country country, uint flags, - int compressLevel, int compressThreshold) + RCCResourceLibrary::CompressionAlgorithm compressAlgo, int compressLevel, int compressThreshold) { m_name = name; m_fileInfo = fileInfo; @@ -136,6 +154,7 @@ RCCFileInfo::RCCFileInfo(const QString &name, const QFileInfo &fileInfo, m_nameOffset = 0; m_dataOffset = 0; m_childOffset = 0; + m_compressAlgo = compressAlgo; m_compressLevel = compressLevel; m_compressThreshold = compressThreshold; } @@ -157,6 +176,8 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) { const bool text = lib.m_format == RCCResourceLibrary::C_Code; const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; //some info if (text || pass1) { if (m_language != QLocale::C) { @@ -203,6 +224,8 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) } if (text || pass1) lib.writeChar('\n'); + else if (python) + lib.writeString("\\\n"); if (lib.formatVersion() >= 2) { // last modified time stamp @@ -211,9 +234,14 @@ void RCCFileInfo::writeDataInfo(RCCResourceLibrary &lib) static const quint64 sourceDate = 1000 * qgetenv("QT_RCC_SOURCE_DATE_OVERRIDE").toULongLong(); if (sourceDate != 0) lastmod = sourceDate; + static const quint64 sourceDate2 = 1000 * qgetenv("SOURCE_DATE_EPOCH").toULongLong(); + if (sourceDate2 != 0) + lastmod = sourceDate2; lib.writeNumber8(lastmod); if (text || pass1) lib.writeChar('\n'); + else if (python) + lib.writeString("\\\n"); } } @@ -224,6 +252,8 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; const bool pass2 = lib.m_format == RCCResourceLibrary::Pass2; const bool binary = lib.m_format == RCCResourceLibrary::Binary; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; //capture the offset m_dataOffset = offset; @@ -236,19 +266,82 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, } QByteArray data = file.readAll(); -#ifndef QT_NO_COMPRESS // Check if compression is useful for this file - if (m_compressLevel != 0 && data.size() != 0) { - QByteArray compressed = - qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel); - - int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size()); - if (compressRatio >= m_compressThreshold) { - data = compressed; - m_flags |= Compressed; + if (data.size() != 0) { +#if QT_CONFIG(zstd) + if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best) { + m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zstd; + m_compressLevel = 19; // not ZSTD_maxCLevel(), as 20+ are experimental + } + if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zstd) { + if (lib.m_zstdCCtx == nullptr) + lib.m_zstdCCtx = ZSTD_createCCtx(); + qsizetype size = data.size(); + size = ZSTD_COMPRESSBOUND(size); + + int compressLevel = m_compressLevel; + if (compressLevel < 0) + compressLevel = CONSTANT_ZSTDCOMPRESSLEVEL_CHECK; + + QByteArray compressed(size, Qt::Uninitialized); + char *dst = const_cast<char *>(compressed.constData()); + size_t n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size, + data.constData(), data.size(), + compressLevel); + if (n * 100.0 < data.size() * 1.0 * (100 - m_compressThreshold) ) { + // compressing is worth it + if (m_compressLevel < 0) { + // heuristic compression, so recompress + n = ZSTD_compressCCtx(lib.m_zstdCCtx, dst, size, + data.constData(), data.size(), + CONSTANT_ZSTDCOMPRESSLEVEL_STORE); + } + if (ZSTD_isError(n)) { + QString msg = QString::fromLatin1("%1: error: compression with zstd failed: %2\n") + .arg(m_name, QString::fromUtf8(ZSTD_getErrorName(n))); + lib.m_errorDevice->write(msg.toUtf8()); + } else if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: compressed using zstd (%2 -> %3)\n") + .arg(m_name).arg(data.size()).arg(n); + lib.m_errorDevice->write(msg.toUtf8()); + } + + lib.m_overallFlags |= CompressedZstd; + m_flags |= CompressedZstd; + data = std::move(compressed); + data.truncate(n); + } else if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name); + lib.m_errorDevice->write(msg.toUtf8()); + } + } +#endif +#ifndef QT_NO_COMPRESS + if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Best) { + m_compressAlgo = RCCResourceLibrary::CompressionAlgorithm::Zlib; + m_compressLevel = 9; + } + if (m_compressAlgo == RCCResourceLibrary::CompressionAlgorithm::Zlib) { + QByteArray compressed = + qCompress(reinterpret_cast<uchar *>(data.data()), data.size(), m_compressLevel); + + int compressRatio = int(100.0 * (data.size() - compressed.size()) / data.size()); + if (compressRatio >= m_compressThreshold) { + if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: compressed using zlib (%2 -> %3)\n") + .arg(m_name).arg(data.size()).arg(compressed.size()); + lib.m_errorDevice->write(msg.toUtf8()); + } + data = compressed; + lib.m_overallFlags |= Compressed; + m_flags |= Compressed; + } else if (lib.verbose()) { + QString msg = QString::fromLatin1("%1: note: not compressed\n").arg(m_name); + lib.m_errorDevice->write(msg.toUtf8()); + } } - } #endif // QT_NO_COMPRESS + } // some info if (text || pass1) { @@ -258,20 +351,24 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, } // write the length - - if (text || binary || pass2) + if (text || binary || pass2 || python) lib.writeNumber4(data.size()); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 4; // write the payload const char *p = data.constData(); - if (text) { + if (text || python) { for (int i = data.size(), j = 0; --i >= 0; --j) { lib.writeHex(*p++); if (j == 0) { - lib.writeString("\n "); + if (text) + lib.writeString("\n "); + else + lib.writeString("\\\n"); j = 16; } } @@ -283,6 +380,9 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset, // done if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); + return offset; } @@ -290,6 +390,8 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) { const bool text = lib.m_format == RCCResourceLibrary::C_Code; const bool pass1 = lib.m_format == RCCResourceLibrary::Pass1; + const bool python = lib.m_format == RCCResourceLibrary::Python3_Code + || lib.m_format == RCCResourceLibrary::Python2_Code; // capture the offset m_nameOffset = offset; @@ -305,12 +407,16 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) lib.writeNumber2(m_name.length()); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 2; // write the hash lib.writeNumber4(qt_hash(m_name)); if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); offset += 4; // write the m_name @@ -319,12 +425,17 @@ qint64 RCCFileInfo::writeDataName(RCCResourceLibrary &lib, qint64 offset) lib.writeNumber2(unicode[i].unicode()); if ((text || pass1) && i % 16 == 0) lib.writeString("\n "); + else if (python && i % 16 == 0) + lib.writeString("\\\n"); } offset += m_name.length()*2; // done if (text || pass1) lib.writeString("\n "); + else if (python) + lib.writeString("\\\n"); + return offset; } @@ -343,7 +454,8 @@ RCCResourceLibrary::Strings::Strings() : ATTRIBUTE_PREFIX(QLatin1String("prefix")), ATTRIBUTE_ALIAS(QLatin1String("alias")), ATTRIBUTE_THRESHOLD(QLatin1String("threshold")), - ATTRIBUTE_COMPRESS(QLatin1String("compress")) + ATTRIBUTE_COMPRESS(QLatin1String("compress")), + ATTRIBUTE_COMPRESSALGO(QStringLiteral("compression-algorithm")) { } @@ -351,22 +463,30 @@ RCCResourceLibrary::RCCResourceLibrary(quint8 formatVersion) : m_root(0), m_format(C_Code), m_verbose(false), + m_compressionAlgo(CONSTANT_COMPRESSALGO_DEFAULT), m_compressLevel(CONSTANT_COMPRESSLEVEL_DEFAULT), m_compressThreshold(CONSTANT_COMPRESSTHRESHOLD_DEFAULT), m_treeOffset(0), m_namesOffset(0), m_dataOffset(0), + m_overallFlags(0), m_useNameSpace(CONSTANT_USENAMESPACE), m_errorDevice(0), m_outDevice(0), m_formatVersion(formatVersion) { m_out.reserve(30 * 1000 * 1000); +#if QT_CONFIG(zstd) + m_zstdCCtx = nullptr; +#endif } RCCResourceLibrary::~RCCResourceLibrary() { delete m_root; +#if QT_CONFIG(zstd) + ZSTD_freeCCtx(m_zstdCCtx); +#endif } enum RCCXmlTag { @@ -391,6 +511,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, QLocale::Language language = QLocale::c().language(); QLocale::Country country = QLocale::c().country(); QString alias; + auto compressAlgo = m_compressionAlgo; int compressLevel = m_compressLevel; int compressThreshold = m_compressThreshold; @@ -444,17 +565,27 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, if (attributes.hasAttribute(m_strings.ATTRIBUTE_ALIAS)) alias = attributes.value(m_strings.ATTRIBUTE_ALIAS).toString(); + compressAlgo = m_compressionAlgo; compressLevel = m_compressLevel; - if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) - compressLevel = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString().toInt(); - compressThreshold = m_compressThreshold; + + QString errorString; + if (attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESSALGO)) + compressAlgo = parseCompressionAlgorithm(attributes.value(m_strings.ATTRIBUTE_COMPRESSALGO), &errorString); + if (errorString.isEmpty() && attributes.hasAttribute(m_strings.ATTRIBUTE_COMPRESS)) { + QString value = attributes.value(m_strings.ATTRIBUTE_COMPRESS).toString(); + compressLevel = parseCompressionLevel(compressAlgo, value, &errorString); + } + + // Special case for -no-compress + if (m_compressLevel == -2) + compressAlgo = CompressionAlgorithm::None; + if (attributes.hasAttribute(m_strings.ATTRIBUTE_THRESHOLD)) compressThreshold = attributes.value(m_strings.ATTRIBUTE_THRESHOLD).toString().toInt(); - // Special case for -no-compress. Overrides all other settings. - if (m_compressLevel == -2) - compressLevel = 0; + if (!errorString.isEmpty()) + reader.raiseError(errorString); } } else { reader.raiseError(QString(QLatin1String("unexpected tag: %1")).arg(reader.name().toString())); @@ -520,6 +651,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, language, country, child.isDir() ? RCCFileInfo::Directory : RCCFileInfo::NoFlags, + compressAlgo, compressLevel, compressThreshold) ); @@ -535,6 +667,7 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice, language, country, RCCFileInfo::NoFlags, + compressAlgo, compressLevel, compressThreshold) ); @@ -729,6 +862,55 @@ RCCResourceLibrary::ResourceDataFileMap RCCResourceLibrary::resourceDataFileMap( return rc; } +RCCResourceLibrary::CompressionAlgorithm RCCResourceLibrary::parseCompressionAlgorithm(QStringView value, QString *errorMsg) +{ + if (value == QLatin1String("best")) + return CompressionAlgorithm::Best; + if (value == QLatin1String("zlib")) { +#ifdef QT_NO_COMPRESS + *errorMsg = QLatin1String("zlib support not compiled in"); +#else + return CompressionAlgorithm::Zlib; +#endif + } else if (value == QLatin1String("zstd")) { +#if QT_CONFIG(zstd) + return CompressionAlgorithm::Zstd; +#else + *errorMsg = QLatin1String("Zstandard support not compiled in"); +#endif + } else if (value != QLatin1String("none")) { + *errorMsg = QString::fromLatin1("Unknown compression algorithm '%1'").arg(value); + } + + return CompressionAlgorithm::None; +} + +int RCCResourceLibrary::parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg) +{ + bool ok; + int c = level.toInt(&ok); + if (ok) { + switch (algo) { + case CompressionAlgorithm::None: + case CompressionAlgorithm::Best: + return 0; + case CompressionAlgorithm::Zlib: + if (c >= 1 && c <= 9) + return c; + break; + case CompressionAlgorithm::Zstd: +#if QT_CONFIG(zstd) + if (c >= 0 && c <= ZSTD_maxCLevel()) + return c; +#endif + break; + } + } + + *errorMsg = QString::fromLatin1("invalid compression level '%1'").arg(level); + return 0; +} + bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &tempDevice, QIODevice &errorDevice) { m_errorDevice = &errorDevice; @@ -795,18 +977,45 @@ bool RCCResourceLibrary::output(QIODevice &outDevice, QIODevice &tempDevice, QIO return true; } +void RCCResourceLibrary::writeDecimal(int value) +{ + Q_ASSERT(m_format != RCCResourceLibrary::Binary); + char buf[std::numeric_limits<int>::digits10 + 2]; + int n = snprintf(buf, sizeof(buf), "%d", value); + write(buf, n + 1); // write() takes a size including terminating NUL +} + +static const char hexDigits[] = "0123456789abcdef"; + +inline void RCCResourceLibrary::write2HexDigits(quint8 number) +{ + writeChar(hexDigits[number >> 4]); + writeChar(hexDigits[number & 0xf]); +} + void RCCResourceLibrary::writeHex(quint8 tmp) { - const char digits[] = "0123456789abcdef"; - writeChar('0'); - writeChar('x'); - if (tmp < 16) { - writeChar(digits[tmp]); - } else { - writeChar(digits[tmp >> 4]); - writeChar(digits[tmp & 0xf]); + switch (m_format) { + case RCCResourceLibrary::Python3_Code: + case RCCResourceLibrary::Python2_Code: + if (tmp >= 32 && tmp < 127 && tmp != '"' && tmp != '\\') { + writeChar(char(tmp)); + } else { + writeChar('\\'); + writeChar('x'); + write2HexDigits(tmp); + } + break; + default: + writeChar('0'); + writeChar('x'); + if (tmp < 16) + writeChar(hexDigits[tmp]); + else + write2HexDigits(tmp); + writeChar(','); + break; } - writeChar(','); } void RCCResourceLibrary::writeNumber2(quint16 number) @@ -874,7 +1083,9 @@ void RCCResourceLibrary::writeNumber8(quint64 number) bool RCCResourceLibrary::writeHeader() { - if (m_format == C_Code || m_format == Pass1) { + switch (m_format) { + case C_Code: + case Pass1: writeString("/****************************************************************************\n"); writeString("** Resource object code\n"); writeString("**\n"); @@ -883,12 +1094,30 @@ bool RCCResourceLibrary::writeHeader() writeString("\n**\n"); writeString("** WARNING! All changes made in this file will be lost!\n"); writeString( "*****************************************************************************/\n\n"); - } else if (m_format == Binary) { + break; + case Python3_Code: + case Python2_Code: + writeString("# Resource object code (Python "); + writeChar(m_format == Python3_Code ? '3' : '2'); + writeString(")\n"); + writeString("# Created by: object code\n"); + writeString("# Created by: The Resource Compiler for Qt version "); + writeByteArray(QT_VERSION_STR); + writeString("\n"); + writeString("# WARNING! All changes made in this file will be lost!\n\n"); + writeString("from PySide2 import QtCore\n\n"); + break; + case Binary: writeString("qres"); writeNumber4(0); writeNumber4(0); writeNumber4(0); writeNumber4(0); + if (m_formatVersion >= 3) + writeNumber4(m_overallFlags); + break; + default: + break; } return true; } @@ -896,10 +1125,21 @@ bool RCCResourceLibrary::writeHeader() bool RCCResourceLibrary::writeDataBlobs() { Q_ASSERT(m_errorDevice); - if (m_format == C_Code) { + switch (m_format) { + case C_Code: writeString("static const unsigned char qt_resource_data[] = {\n"); - } else if (m_format == Binary) { + break; + case Python3_Code: + writeString("qt_resource_data = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_data = \"\\\n"); + break; + case Binary: m_dataOffset = m_out.size(); + break; + default: + break; } if (!m_root) @@ -925,24 +1165,46 @@ bool RCCResourceLibrary::writeDataBlobs() } } } - if (m_format == C_Code) + switch (m_format) { + case C_Code: writeString("\n};\n\n"); - else if (m_format == Pass1) { + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + case Pass1: if (offset < 8) offset = 8; writeString("\nstatic const unsigned char qt_resource_data["); writeByteArray(QByteArray::number(offset)); writeString("] = { 'Q', 'R', 'C', '_', 'D', 'A', 'T', 'A' };\n\n"); + break; + default: + break; } return true; } bool RCCResourceLibrary::writeDataNames() { - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("static const unsigned char qt_resource_name[] = {\n"); - else if (m_format == Binary) + break; + case Python3_Code: + writeString("qt_resource_name = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_name = \"\\\n"); + break; + case Binary: m_namesOffset = m_out.size(); + break; + default: + break; + } QHash<QString, int> names; QStack<RCCFileInfo*> pending; @@ -967,8 +1229,18 @@ bool RCCResourceLibrary::writeDataNames() } } } - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("\n};\n\n"); + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + default: + break; + } return true; } @@ -983,10 +1255,24 @@ struct qt_rcc_compare_hash bool RCCResourceLibrary::writeDataStructure() { - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("static const unsigned char qt_resource_struct[] = {\n"); - else if (m_format == Binary) + break; + case Python3_Code: + writeString("qt_resource_struct = b\"\\\n"); + break; + case Python2_Code: + writeString("qt_resource_struct = \"\\\n"); + break; + case Binary: m_treeOffset = m_out.size(); + break; + default: + break; + } + QStack<RCCFileInfo*> pending; if (!m_root) @@ -1030,8 +1316,18 @@ bool RCCResourceLibrary::writeDataStructure() pending.push(child); } } - if (m_format == C_Code || m_format == Pass1) + switch (m_format) { + case C_Code: + case Pass1: writeString("\n};\n\n"); + break; + case Python3_Code: + case Python2_Code: + writeString("\"\n\n"); + break; + default: + break; + } return true; } @@ -1091,10 +1387,35 @@ bool RCCResourceLibrary::writeInitializer() if (m_root) { writeString("bool qRegisterResourceData" "(int, const unsigned char *, " - "const unsigned char *, const unsigned char *);\n\n"); + "const unsigned char *, const unsigned char *);\n"); writeString("bool qUnregisterResourceData" "(int, const unsigned char *, " "const unsigned char *, const unsigned char *);\n\n"); + + if (m_overallFlags & (RCCFileInfo::Compressed | RCCFileInfo::CompressedZstd)) { + // use variable relocations with ELF and Mach-O + writeString("#if defined(__ELF__) || defined(__APPLE__)\n"); + if (m_overallFlags & RCCFileInfo::Compressed) { + writeString("static inline unsigned char qResourceFeatureZlib()\n" + "{\n" + " extern const unsigned char qt_resourceFeatureZlib;\n" + " return qt_resourceFeatureZlib;\n" + "}\n"); + } + if (m_overallFlags & RCCFileInfo::CompressedZstd) { + writeString("static inline unsigned char qResourceFeatureZstd()\n" + "{\n" + " extern const unsigned char qt_resourceFeatureZstd;\n" + " return qt_resourceFeatureZstd;\n" + "}\n"); + } + writeString("#else\n"); + if (m_overallFlags & RCCFileInfo::Compressed) + writeString("unsigned char qResourceFeatureZlib();\n"); + if (m_overallFlags & RCCFileInfo::CompressedZstd) + writeString("unsigned char qResourceFeatureZstd();\n"); + writeString("#endif\n\n"); + } } if (m_useNameSpace) @@ -1113,12 +1434,12 @@ bool RCCResourceLibrary::writeInitializer() writeString("()\n{\n"); if (m_root) { - writeString(" "); + writeString(" int version = "); + writeDecimal(m_formatVersion); + writeString(";\n "); writeAddNamespaceFunction("qRegisterResourceData"); - writeString("\n ("); - writeHex(m_formatVersion); - writeString(" qt_resource_struct, " - "qt_resource_name, qt_resource_data);\n"); + writeString("\n (version, qt_resource_struct, " + "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); writeString("}\n\n"); @@ -1136,11 +1457,24 @@ bool RCCResourceLibrary::writeInitializer() writeMangleNamespaceFunction(cleanResources); writeString("()\n{\n"); if (m_root) { - writeString(" "); + writeString(" int version = "); + writeDecimal(m_formatVersion); + writeString(";\n "); + + // ODR-use certain symbols from QtCore if we require optional features + if (m_overallFlags & RCCFileInfo::Compressed) { + writeString("version += "); + writeAddNamespaceFunction("qResourceFeatureZlib()"); + writeString(";\n "); + } + if (m_overallFlags & RCCFileInfo::CompressedZstd) { + writeString("version += "); + writeAddNamespaceFunction("qResourceFeatureZstd()"); + writeString(";\n "); + } + writeAddNamespaceFunction("qUnregisterResourceData"); - writeString("\n ("); - writeHex(m_formatVersion); - writeString(" qt_resource_struct, " + writeString("\n (version, qt_resource_struct, " "qt_resource_name, qt_resource_data);\n"); } writeString(" return 1;\n"); @@ -1176,6 +1510,23 @@ bool RCCResourceLibrary::writeInitializer() p[i++] = (m_namesOffset >> 16) & 0xff; p[i++] = (m_namesOffset >> 8) & 0xff; p[i++] = (m_namesOffset >> 0) & 0xff; + + if (m_formatVersion >= 3) { + p[i++] = (m_overallFlags >> 24) & 0xff; + p[i++] = (m_overallFlags >> 16) & 0xff; + p[i++] = (m_overallFlags >> 8) & 0xff; + p[i++] = (m_overallFlags >> 0) & 0xff; + } + } else if (m_format == Python3_Code || m_format == Python2_Code) { + writeString("def qInitResources():\n"); + writeString(" QtCore.qRegisterResourceData(0x"); + write2HexDigits(m_formatVersion); + writeString(", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n"); + writeString("def qCleanupResources():\n"); + writeString(" QtCore.qUnregisterResourceData(0x"); + write2HexDigits(m_formatVersion); + writeString(", qt_resource_struct, qt_resource_name, qt_resource_data)\n\n"); + writeString("qInitResources()\n"); } return true; } diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h index 36984cf38a..b301355e4f 100644 --- a/src/tools/rcc/rcc.h +++ b/src/tools/rcc/rcc.h @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. @@ -35,6 +36,8 @@ #include <qhash.h> #include <qstring.h> +typedef struct ZSTD_CCtx_s ZSTD_CCtx; + QT_BEGIN_NAMESPACE class RCCFileInfo; @@ -55,7 +58,7 @@ public: bool readFiles(bool listMode, QIODevice &errorDevice); - enum Format { Binary, C_Code, Pass1, Pass2 }; + enum Format { Binary, C_Code, Pass1, Pass2, Python3_Code, Python2_Code }; void setFormat(Format f) { m_format = f; } Format format() const { return m_format; } @@ -77,6 +80,19 @@ public: void setOutputName(const QString &name) { m_outputName = name; } QString outputName() const { return m_outputName; } + enum class CompressionAlgorithm { + Zlib, + Zstd, + + Best = 99, + None = -1 + }; + + static CompressionAlgorithm parseCompressionAlgorithm(QStringView algo, QString *errorMsg); + void setCompressionAlgorithm(CompressionAlgorithm algo) { m_compressionAlgo = algo; } + CompressionAlgorithm compressionAlgorithm() const { return m_compressionAlgo; } + + static int parseCompressionLevel(CompressionAlgorithm algo, const QString &level, QString *errorMsg); void setCompressLevel(int c) { m_compressLevel = c; } int compressLevel() const { return m_compressLevel; } @@ -104,6 +120,7 @@ private: const QString ATTRIBUTE_ALIAS; const QString ATTRIBUTE_THRESHOLD; const QString ATTRIBUTE_COMPRESS; + const QString ATTRIBUTE_COMPRESSALGO; }; friend class RCCFileInfo; void reset(); @@ -117,7 +134,9 @@ private: bool writeInitializer(); void writeMangleNamespaceFunction(const QByteArray &name); void writeAddNamespaceFunction(const QByteArray &name); + void writeDecimal(int value); void writeHex(quint8 number); + void write2HexDigits(quint8 number); void writeNumber2(quint16 number); void writeNumber4(quint32 number); void writeNumber8(quint64 number); @@ -125,6 +144,10 @@ private: void writeByteArray(const QByteArray &); void write(const char *, int len); +#if QT_CONFIG(zstd) + ZSTD_CCtx *m_zstdCCtx; +#endif + const Strings m_strings; RCCFileInfo *m_root; QStringList m_fileNames; @@ -133,11 +156,13 @@ private: QString m_outputName; Format m_format; bool m_verbose; + CompressionAlgorithm m_compressionAlgo; int m_compressLevel; int m_compressThreshold; int m_treeOffset; int m_namesOffset; int m_dataOffset; + quint32 m_overallFlags; bool m_useNameSpace; QStringList m_failedResources; QIODevice *m_errorDevice; diff --git a/src/tools/rcc/rcc.pro b/src/tools/rcc/rcc.pro index 208ec54a73..ae55b5d8af 100644 --- a/src/tools/rcc/rcc.pro +++ b/src/tools/rcc/rcc.pro @@ -8,3 +8,14 @@ SOURCES += main.cpp QMAKE_TARGET_DESCRIPTION = "Qt Resource Compiler" load(qt_tool) + +# RCC is a bootstrapped tool, so qglobal.h #includes qconfig-bootstrapped.h +# and that has a #define saying zstd isn't present (for qresource.cpp, which is +# part of the bootstrap lib). So we inform the presence of the feature in the +# command-line. +qtConfig(zstd):!cross_compile { + DEFINES += QT_FEATURE_zstd=1 + QMAKE_USE_PRIVATE += zstd +} else { + DEFINES += QT_FEATURE_zstd=-1 +} diff --git a/src/tools/tracegen/CMakeLists.txt b/src/tools/tracegen/CMakeLists.txt index bfb33d77c5..df98910626 100644 --- a/src/tools/tracegen/CMakeLists.txt +++ b/src/tools/tracegen/CMakeLists.txt @@ -14,7 +14,8 @@ add_qt_tool(tracegen provider.cpp provider.h qtheaders.cpp qtheaders.h tracegen.cpp - # CONFIG = "force_bootstrap" - # _LOADED = "qt_tool" - # _OPTION = "host_build" ) + +#### Keys ignored in scope 1:.:.:tracegen.pro:<TRUE>: +# _LOADED = "qt_tool" +# _OPTION = "host_build" diff --git a/src/tools/tracegen/etw.cpp b/src/tools/tracegen/etw.cpp index 8c065f93c9..e839137915 100644 --- a/src/tools/tracegen/etw.cpp +++ b/src/tools/tracegen/etw.cpp @@ -110,19 +110,21 @@ static QString createGuid(const QUuid &uuid) return guid; } -static void writePrologue(QTextStream &stream, const QString &fileName, const QString &providerName) +static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider) { - QUuid uuid = QUuid::createUuidV5(QUuid(), providerName.toLocal8Bit()); + QUuid uuid = QUuid::createUuidV5(QUuid(), provider.name.toLocal8Bit()); - const QString provider = providerVar(providerName); + const QString providerV = providerVar(provider.name); const QString guard = includeGuard(fileName); const QString guid = createGuid(uuid); const QString guidString = uuid.toString(); stream << "#ifndef " << guard << "\n" << "#define " << guard << "\n" + << "\n" << "#include <windows.h>\n" - << "#include <TraceLoggingProvider.h>\n"; + << "#include <TraceLoggingProvider.h>\n" + << "\n"; /* TraceLogging API macros cannot deal with UTF8 * source files, so we work around it like this @@ -132,30 +134,33 @@ static void writePrologue(QTextStream &stream, const QString &fileName, const QS "#define _TlgPragmaUtf8Begin\n" "#define _TlgPragmaUtf8End\n"; + stream << "\n"; stream << qtHeaders(); - stream << "\n"; + if (!provider.prefixText.isEmpty()) + stream << provider.prefixText.join(QLatin1Char('\n')) << "\n\n"; + stream << "#ifdef TRACEPOINT_DEFINE\n" << "/* " << guidString << " */\n" - << "TRACELOGGING_DEFINE_PROVIDER(" << provider << ", \"" - << providerName <<"\", " << guid << ");\n\n"; + << "TRACELOGGING_DEFINE_PROVIDER(" << providerV << ", \"" + << provider.name <<"\", " << guid << ");\n\n"; stream << "static inline void registerProvider()\n" << "{\n" - << " TraceLoggingRegister(" << provider << ");\n" + << " TraceLoggingRegister(" << providerV << ");\n" << "}\n\n"; stream << "static inline void unregisterProvider()\n" << "{\n" - << " TraceLoggingUnregister(" << provider << ");\n" + << " TraceLoggingUnregister(" << providerV << ");\n" << "}\n"; stream << "Q_CONSTRUCTOR_FUNCTION(registerProvider)\n" << "Q_DESTRUCTOR_FUNCTION(unregisterProvider)\n\n"; stream << "#else\n" - << "TRACELOGGING_DECLARE_PROVIDER(" << provider << ");\n" + << "TRACELOGGING_DECLARE_PROVIDER(" << providerV << ");\n" << "#endif // TRACEPOINT_DEFINE\n\n"; } @@ -224,7 +229,7 @@ void writeEtw(QFile &file, const Provider &provider) const QString fileName = QFileInfo(file.fileName()).fileName(); - writePrologue(stream, fileName, provider.name); + writePrologue(stream, fileName, provider); writeTracepoints(stream, provider); writeEpilogue(stream, fileName); } diff --git a/src/tools/tracegen/lttng.cpp b/src/tools/tracegen/lttng.cpp index 5d41bf5f1f..f0fbca9e16 100644 --- a/src/tools/tracegen/lttng.cpp +++ b/src/tools/tracegen/lttng.cpp @@ -104,16 +104,21 @@ static void writeCtfMacro(QTextStream &stream, const Tracepoint::Field &field) } } -static void writePrologue(QTextStream &stream, const QString &fileName, const QString &providerName) +static void writePrologue(QTextStream &stream, const QString &fileName, const Provider &provider) { + const QString guard = includeGuard(fileName); + stream << "#undef TRACEPOINT_PROVIDER\n"; - stream << "#define TRACEPOINT_PROVIDER " << providerName << "\n\n"; + stream << "#define TRACEPOINT_PROVIDER " << provider.name << "\n"; + stream << "\n"; + // include prefix text or qt headers only once + stream << "#if !defined(" << guard << ")\n"; stream << qtHeaders(); - - const QString guard = includeGuard(fileName); - stream << "\n"; + if (!provider.prefixText.isEmpty()) + stream << provider.prefixText.join(QLatin1Char('\n')) << "\n\n"; + stream << "#endif\n\n"; /* the first guard is the usual one, the second is required * by LTTNG to force the re-evaluation of TRACEPOINT_* macros @@ -213,7 +218,7 @@ void writeLttng(QFile &file, const Provider &provider) const QString fileName = QFileInfo(file.fileName()).fileName(); - writePrologue(stream, fileName, provider.name); + writePrologue(stream, fileName, provider); writeTracepoints(stream, provider); writeEpilogue(stream, fileName); } diff --git a/src/tools/tracegen/provider.cpp b/src/tools/tracegen/provider.cpp index a6523a2e3d..39633efe5d 100644 --- a/src/tools/tracegen/provider.cpp +++ b/src/tools/tracegen/provider.cpp @@ -275,9 +275,21 @@ Provider parseProvider(const QString &filename) Provider provider; provider.name = QFileInfo(filename).baseName(); + bool parsingPrefixText = false; for (int lineNumber = 1; !s.atEnd(); ++lineNumber) { QString line = s.readLine().trimmed(); + if (line == QLatin1String("{")) { + parsingPrefixText = true; + continue; + } else if (parsingPrefixText && line == QLatin1String("}")) { + parsingPrefixText = false; + continue; + } else if (parsingPrefixText) { + provider.prefixText.append(line); + continue; + } + if (line.isEmpty() || line.startsWith(QLatin1Char('#'))) continue; @@ -296,7 +308,14 @@ Provider parseProvider(const QString &filename) } } + if (parsingPrefixText) { + panic("Syntax error while processing '%s': " + "no closing brace found for prefix text block", + qPrintable(filename)); + } + #ifdef TRACEGEN_DEBUG + qDebug() << provider.prefixText; for (auto i = provider.tracepoints.constBegin(); i != provider.tracepoints.constEnd(); ++i) dumpTracepoint(*i); #endif diff --git a/src/tools/tracegen/provider.h b/src/tools/tracegen/provider.h index 9771e62f4d..a4baf56815 100644 --- a/src/tools/tracegen/provider.h +++ b/src/tools/tracegen/provider.h @@ -42,6 +42,7 @@ #include <qvector.h> #include <qstring.h> +#include <qstringlist.h> #include <qtypeinfo.h> struct Tracepoint @@ -86,6 +87,7 @@ struct Provider { QString name; QVector<Tracepoint> tracepoints; + QStringList prefixText; }; Provider parseProvider(const QString &filename); diff --git a/src/tools/uic/CMakeLists.txt b/src/tools/uic/CMakeLists.txt index 15ef92a2b4..05de0a71ba 100644 --- a/src/tools/uic/CMakeLists.txt +++ b/src/tools/uic/CMakeLists.txt @@ -12,9 +12,11 @@ add_qt_tool(uic customwidgetsinfo.cpp customwidgetsinfo.h databaseinfo.cpp databaseinfo.h driver.cpp driver.h - globaldefs.h main.cpp option.h + python/pythonwritedeclaration.cpp python/pythonwritedeclaration.h + python/pythonwriteimports.cpp python/pythonwriteimports.h + shared/language.cpp shared/language.h treewalker.cpp treewalker.h ui4.cpp ui4.h uic.cpp uic.h @@ -24,19 +26,24 @@ add_qt_tool(uic QT_UIC QT_NO_CAST_FROM_ASCII QT_NO_FOREACH - QT_UIC_CPP_GENERATOR + QT_UIC_CPP_GENERATOR # special case INCLUDE_DIRECTORIES + # . # special case: remove + shared cpp + # ${CMAKE_CURRENT_BINARY_DIR} # special case: remove + python DISABLE_AUTOGEN_TOOLS # special case: uic rcc - # QMAKE_TARGET_DESCRIPTION = "Qt User Interface Compiler" - # _LOADED = "qt_tool" - # _OPTION = "host_build" ) +#### Keys ignored in scope 1:.:.:uic.pro:<TRUE>: +# QMAKE_TARGET_DESCRIPTION = "Qt User Interface Compiler" +# _LOADED = "qt_tool" +# _OPTION = "host_build" + ## Scopes: ##################################################################### -# -#extend_target(uic CONDITION NOT force_bootstrap AND NOT qtConfig(commandlineparser OR NOT QT_FEATURE_textcodec OR NOT QT_FEATURE_xmlstreamreader OR NOT QT_FEATURE_xmlstreamwriter -# # CONFIG = "force_bootstrap" -#) + +#### Keys ignored in scope 2:.:.:uic.pro:NOT force_bootstrap AND NOT qtConfig(commandlineparser OR NOT QT_FEATURE_textcodec OR NOT QT_FEATURE_xmlstreamreader OR NOT QT_FEATURE_xmlstreamwriter: +# CONFIG = "force_bootstrap" diff --git a/src/tools/uic/cpp/cpp.pri b/src/tools/uic/cpp/cpp.pri index a6b6188117..786b0e97a5 100644 --- a/src/tools/uic/cpp/cpp.pri +++ b/src/tools/uic/cpp/cpp.pri @@ -1,7 +1,5 @@ INCLUDEPATH += $$PWD $$QT_BUILD_TREE/src/tools/uic -DEFINES += QT_UIC_CPP_GENERATOR - # Input HEADERS += $$PWD/cppwritedeclaration.h \ $$PWD/cppwriteincludes.h \ diff --git a/src/tools/uic/cpp/cppwriteincludes.cpp b/src/tools/uic/cpp/cppwriteincludes.cpp index d51fddffea..0ba49627c0 100644 --- a/src/tools/uic/cpp/cppwriteincludes.cpp +++ b/src/tools/uic/cpp/cppwriteincludes.cpp @@ -119,7 +119,7 @@ void WriteIncludes::acceptUI(DomUI *node) writeHeaders(m_globalIncludes, true); writeHeaders(m_localIncludes, false); - m_output << QLatin1Char('\n'); + m_output << '\n'; } void WriteIncludes::acceptWidget(DomWidget *node) @@ -214,14 +214,14 @@ void WriteIncludes::add(const QString &className, bool determineHeader, const QS m_knownClasses.insert(className); const CustomWidgetsInfo *cwi = m_uic->customWidgetsInfo(); - if (cwi->extends(className, QLatin1String("QTreeView")) - || cwi->extends(className, QLatin1String("QTreeWidget")) - || cwi->extends(className, QLatin1String("QTableView")) - || cwi->extends(className, QLatin1String("QTableWidget"))) { + static const QStringList treeViewsWithHeaders = { + QLatin1String("QTreeView"), QLatin1String("QTreeWidget"), + QLatin1String("QTableView"), QLatin1String("QTableWidget") + }; + if (cwi->extendsOneOf(className, treeViewsWithHeaders)) add(QLatin1String("QHeaderView")); - } - if (!m_laidOut && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QToolBox"))) + if (!m_laidOut && cwi->extends(className, QLatin1String("QToolBox"))) add(QLatin1String("QLayout")); // spacing property of QToolBox) if (className == QLatin1String("Line")) { // ### hmm, deprecate me! @@ -314,7 +314,7 @@ void WriteIncludes::writeHeaders(const OrderedSet &headers, bool global) const QString value = m_oldHeaderToNewHeader.value(header, header); const auto trimmed = QStringRef(&value).trimmed(); if (!trimmed.isEmpty()) - m_output << "#include " << openingQuote << trimmed << closingQuote << QLatin1Char('\n'); + m_output << "#include " << openingQuote << trimmed << closingQuote << '\n'; } } diff --git a/src/tools/uic/cpp/cppwriteincludes.h b/src/tools/uic/cpp/cppwriteincludes.h index 7f03849798..79cbd41014 100644 --- a/src/tools/uic/cpp/cppwriteincludes.h +++ b/src/tools/uic/cpp/cppwriteincludes.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE class QTextStream; +class CustomWidgetsInfo; class Driver; class Uic; @@ -58,9 +59,9 @@ struct WriteIncludes : public TreeWalker // // actions // - void acceptActionGroup(DomActionGroup *node) Q_DECL_OVERRIDE; - void acceptAction(DomAction *node) Q_DECL_OVERRIDE; - void acceptActionRef(DomActionRef *node) Q_DECL_OVERRIDE; + void acceptActionGroup(DomActionGroup *node) override; + void acceptAction(DomAction *node) override; + void acceptActionRef(DomActionRef *node) override; // // custom widgets @@ -74,6 +75,9 @@ struct WriteIncludes : public TreeWalker void acceptIncludes(DomIncludes *node) override; void acceptInclude(DomInclude *node) override; +protected: + QTextStream &output() const { return m_output; } + private: void add(const QString &className, bool determineHeader = true, const QString &header = QString(), bool global = false); diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp index 4f6ac1eb97..6ff7323b3d 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.cpp +++ b/src/tools/uic/cpp/cppwriteinitialization.cpp @@ -32,9 +32,11 @@ #include "utils.h" #include "uic.h" #include "databaseinfo.h" -#include "globaldefs.h" + +#include <language.h> #include <qtextstream.h> +#include <qversionnumber.h> #include <qdebug.h> #include <algorithm> @@ -44,41 +46,32 @@ QT_BEGIN_NAMESPACE namespace { - // Fixup an enumeration name from class Qt. - // They are currently stored as "BottomToolBarArea" instead of "Qt::BottomToolBarArea". - // due to MO issues. This might be fixed in the future. - QLatin1String qtEnumerationPrefix(const QString &name) { - static const QLatin1String prefix("Qt::"); - if (name.indexOf(prefix) != 0) - return prefix; - return QLatin1String(); - } // figure out the toolbar area of a DOM attrib list. // By legacy, it is stored as an integer. As of 4.3.0, it is the enumeration value. QString toolBarAreaStringFromDOMAttributes(const CPP::WriteInitialization::DomPropertyMap &attributes) { const DomProperty *pstyle = attributes.value(QLatin1String("toolBarArea")); + QString result; if (!pstyle) - return QString(); - + return result; switch (pstyle->kind()) { - case DomProperty::Number: { - return QLatin1String("static_cast<Qt::ToolBarArea>(") - + QString::number(pstyle->elementNumber()) + QLatin1String("), "); - } - case DomProperty::Enum: { - const QString area = pstyle->elementEnum(); - return qtEnumerationPrefix(area) + area + QLatin1String(", "); - } + case DomProperty::Number: + result = QLatin1String(language::toolbarArea(pstyle->elementNumber())); + break; + case DomProperty::Enum: + result = pstyle->elementEnum(); + break; default: break; } - return QString(); + if (!result.startsWith(QLatin1String("Qt::"))) + result.prepend(QLatin1String("Qt::")); + return result + QLatin1String(", "); } // Write a statement to create a spacer item. void writeSpacerItem(const DomSpacer *node, QTextStream &output) { const QHash<QString, DomProperty *> properties = propertyMap(node->elementProperty()); - output << "new QSpacerItem("; + output << language::operatorNew << "QSpacerItem("; int w = 0; int h = 0; @@ -109,11 +102,9 @@ namespace { if (orientation == QLatin1String("Qt::Vertical") || orientation == QLatin1String("Vertical")) isVspacer = true; } - - if (isVspacer) - output << "QSizePolicy::Minimum, " << sizeType << ')'; - else - output << sizeType << ", QSizePolicy::Minimum)"; + const QString horizType = isVspacer ? QLatin1String("QSizePolicy::Minimum") : sizeType; + const QString vertType = isVspacer ? sizeType : QLatin1String("QSizePolicy::Minimum"); + output << language::enumValue(horizType) << ", " << language::enumValue(vertType) << ')'; } @@ -127,7 +118,8 @@ namespace { // Write object->setFoo(x); template <class Value> void writeSetter(const QString &indent, const QString &varName,const QString &setter, Value v, QTextStream &str) { - str << indent << varName << "->" << setter << '(' << v << ");\n"; + str << indent << varName << language::derefPointer + << setter << '(' << v << ')' << language::eol; } static inline bool iconHasStatePixmaps(const DomResourceIcon *i) { @@ -172,17 +164,16 @@ namespace { } return true; } - - inline void openIfndef(QTextStream &str, const QString &symbol) { if (!symbol.isEmpty()) str << QLatin1String("#ifndef ") << symbol << endl; } - inline void closeIfndef(QTextStream &str, const QString &symbol) { if (!symbol.isEmpty()) str << QLatin1String("#endif // ") << symbol << endl; } - - const char *accessibilityDefineC = "QT_NO_ACCESSIBILITY"; - const char *toolTipDefineC = "QT_NO_TOOLTIP"; - const char *whatsThisDefineC = "QT_NO_WHATSTHIS"; - const char *statusTipDefineC = "QT_NO_STATUSTIP"; - const char *shortcutDefineC = "QT_NO_SHORTCUT"; } +// QtGui +static inline QString accessibilityConfigKey() { return QStringLiteral("accessibility"); } +static inline QString shortcutConfigKey() { return QStringLiteral("shortcut"); } +static inline QString whatsThisConfigKey() { return QStringLiteral("whatsthis"); } +// QtWidgets +static inline QString statusTipConfigKey() { return QStringLiteral("statustip"); } +static inline QString toolTipConfigKey() { return QStringLiteral("tooltip"); } + namespace CPP { FontHandle::FontHandle(const DomFont *domFont) : @@ -503,8 +494,9 @@ void WriteInitialization::acceptUI(DomUI *node) const QString widgetClassName = node->elementWidget()->attributeClass(); - m_output << m_option.indent << "void " << "setupUi(" << widgetClassName << " *" << varName << ")\n" - << m_option.indent << "{\n"; + const QString parameterType = widgetClassName + QLatin1String(" *"); + m_output << m_option.indent + << language::startFunctionDefinition1("setupUi", parameterType, varName, m_option.indent); const QStringList connections = m_uic->databaseInfo()->connections(); for (int i=0; i<connections.size(); ++i) { @@ -514,38 +506,42 @@ void WriteInitialization::acceptUI(DomUI *node) continue; const QString varConn = connection + QLatin1String("Connection"); - m_output << m_indent << varConn << " = QSqlDatabase::database(" << fixString(connection, m_dindent) << ");\n"; + m_output << m_indent << varConn << " = QSqlDatabase::database(" + << language::charliteral(connection, m_dindent) << ");\n"; } acceptWidget(node->elementWidget()); if (!m_buddies.empty()) - openIfndef(m_output, QLatin1String(shortcutDefineC)); + m_output << language::openQtConfig(shortcutConfigKey()); for (const Buddy &b : qAsConst(m_buddies)) { if (!m_registeredWidgets.contains(b.objName)) { fprintf(stderr, "%s: Warning: Buddy assignment: '%s' is not a valid widget.\n", qPrintable(m_option.messagePrefix()), b.objName.toLatin1().data()); continue; - } else if (!m_registeredWidgets.contains(b.buddy)) { + } + if (!m_registeredWidgets.contains(b.buddy)) { fprintf(stderr, "%s: Warning: Buddy assignment: '%s' is not a valid widget.\n", qPrintable(m_option.messagePrefix()), b.buddy.toLatin1().data()); continue; } - m_output << m_indent << b.objName << "->setBuddy(" << b.buddy << ");\n"; + m_output << m_indent << b.objName << language::derefPointer + << "setBuddy(" << b.buddy << ')' << language::eol; } if (!m_buddies.empty()) - closeIfndef(m_output, QLatin1String(shortcutDefineC)); + m_output << language::closeQtConfig(shortcutConfigKey()); if (node->elementTabStops()) acceptTabStops(node->elementTabStops()); - if (m_delayedActionInitialization.size()) + if (!m_delayedActionInitialization.isEmpty()) m_output << "\n" << m_delayedActionInitialization; - m_output << "\n" << m_indent << "retranslateUi(" << varName << ");\n"; + m_output << "\n" << m_indent << language::self + << "retranslateUi(" << varName << ')' << language::eol; if (node->elementConnections()) acceptConnections(node->elementConnections()); @@ -553,10 +549,12 @@ void WriteInitialization::acceptUI(DomUI *node) if (!m_delayedInitialization.isEmpty()) m_output << "\n" << m_delayedInitialization << "\n"; - if (m_option.autoConnection) - m_output << "\n" << m_indent << "QMetaObject::connectSlotsByName(" << varName << ");\n"; + if (m_option.autoConnection) { + m_output << "\n" << m_indent << "QMetaObject" << language::qualifier + << "connectSlotsByName(" << varName << ')' << language::eol; + } - m_output << m_option.indent << "} // setupUi\n\n"; + m_output << m_option.indent << language::endFunctionDefinition("setupUi"); if (!m_mainFormUsedInRetranslateUi) { m_refreshInitialization += m_indent; @@ -565,10 +563,10 @@ void WriteInitialization::acceptUI(DomUI *node) m_refreshInitialization += QLatin1String(");\n"); } - m_output << m_option.indent << "void " << "retranslateUi(" << widgetClassName << " *" << varName << ")\n" - << m_option.indent << "{\n" + m_output << m_option.indent + << language::startFunctionDefinition1("retranslateUi", parameterType, varName, m_option.indent) << m_refreshInitialization - << m_option.indent << "} // retranslateUi\n\n"; + << m_option.indent << language::endFunctionDefinition("retranslateUi"); m_layoutChain.pop(); m_widgetChain.pop(); @@ -591,9 +589,11 @@ void WriteInitialization::addWizardPage(const QString &pageVarName, const DomWid } } if (id.isEmpty()) { - m_output << m_indent << parentWidget << "->addPage(" << pageVarName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << "addPage(" << pageVarName << ')' << language::eol; } else { - m_output << m_indent << parentWidget << "->setPage(" << id << ", " << pageVarName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << "setPage(" << id << ", " << pageVarName << ')' << language::eol; } } @@ -615,18 +615,24 @@ void WriteInitialization::acceptWidget(DomWidget *node) if (m_uic->isContainer(parentClass)) parentWidget.clear(); - if (m_widgetChain.size() != 1) - m_output << m_indent << varName << " = new " << m_uic->customWidgetsInfo()->realClassName(className) << '(' << parentWidget << ");\n"; + const auto *cwi = m_uic->customWidgetsInfo(); + + if (m_widgetChain.size() != 1) { + m_output << m_indent << varName << " = " << language::operatorNew + << language::fixClassName(cwi->realClassName(className)) + << '(' << parentWidget << ')' << language::eol; + } parentWidget = savedParentWidget; - if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QComboBox"))) { + + if (cwi->extends(className, QLatin1String("QComboBox"))) { initializeComboBox(node); - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QListWidget"))) { + } else if (cwi->extends(className, QLatin1String("QListWidget"))) { initializeListWidget(node); - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTreeWidget"))) { + } else if (cwi->extends(className, QLatin1String("QTreeWidget"))) { initializeTreeWidget(node); - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTableWidget"))) { + } else if (cwi->extends(className, QLatin1String("QTableWidget"))) { initializeTableWidget(node); } @@ -635,7 +641,8 @@ void WriteInitialization::acceptWidget(DomWidget *node) writeProperties(varName, className, node->elementProperty()); - if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QMenu")) && parentWidget.size()) { + if (!parentWidget.isEmpty() + && cwi->extends(className, QLatin1String("QMenu"))) { initializeMenu(node, parentWidget); } @@ -647,7 +654,7 @@ void WriteInitialization::acceptWidget(DomWidget *node) if (const DomWidget* parentWidget = m_widgetChain.top()) { const QString parentClass = parentWidget->attributeClass(); if (parentClass != QLatin1String("QMainWindow") - && !m_uic->isCustomWidgetContainer(parentClass) + && !m_uic->customWidgetsInfo()->isCustomWidgetContainer(parentClass) && !m_uic->isContainer(parentClass)) m_layoutWidget = true; } @@ -663,101 +670,100 @@ void WriteInitialization::acceptWidget(DomWidget *node) const QString pageDefaultString = QLatin1String("Page"); - if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QMainWindow"))) { - if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QMenuBar"))) { - m_output << m_indent << parentWidget << "->setMenuBar(" << varName <<");\n"; - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QToolBar"))) { - m_output << m_indent << parentWidget << "->addToolBar(" - << toolBarAreaStringFromDOMAttributes(attributes) << varName << ");\n"; + if (cwi->extends(parentClass, QLatin1String("QMainWindow"))) { + if (cwi->extends(className, QLatin1String("QMenuBar"))) { + m_output << m_indent << parentWidget << language::derefPointer + << "setMenuBar(" << varName << ')' << language::eol; + } else if (cwi->extends(className, QLatin1String("QToolBar"))) { + m_output << m_indent << parentWidget << language::derefPointer << "addToolBar(" + << language::enumValue(toolBarAreaStringFromDOMAttributes(attributes)) << varName + << ')' << language::eol; if (const DomProperty *pbreak = attributes.value(QLatin1String("toolBarBreak"))) { if (pbreak->elementBool() == QLatin1String("true")) { - m_output << m_indent << parentWidget << "->insertToolBarBreak(" << varName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << "insertToolBarBreak(" << varName << ')' << language::eol; } } - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QDockWidget"))) { - QString area; + } else if (cwi->extends(className, QLatin1String("QDockWidget"))) { + m_output << m_indent << parentWidget << language::derefPointer << "addDockWidget("; if (DomProperty *pstyle = attributes.value(QLatin1String("dockWidgetArea"))) { - area += QLatin1String("static_cast<Qt::DockWidgetArea>("); - area += QString::number(pstyle->elementNumber()); - area += QLatin1String("), "); + m_output << "Qt" << language::qualifier + << language::dockWidgetArea(pstyle->elementNumber()) << ", "; } - - m_output << m_indent << parentWidget << "->addDockWidget(" << area << varName << ");\n"; + m_output << varName << ");\n"; } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QStatusBar"))) { - m_output << m_indent << parentWidget << "->setStatusBar(" << varName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << "setStatusBar(" << varName << ')' << language::eol; } else { - m_output << m_indent << parentWidget << "->setCentralWidget(" << varName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << "setCentralWidget(" << varName << ')' << language::eol; } } // Check for addPageMethod of a custom plugin first - const QString addPageMethod = m_uic->customWidgetsInfo()->customWidgetAddPageMethod(parentClass); + QString addPageMethod = cwi->customWidgetAddPageMethod(parentClass); + if (addPageMethod.isEmpty()) + addPageMethod = cwi->simpleContainerAddPageMethod(parentClass); if (!addPageMethod.isEmpty()) { - m_output << m_indent << parentWidget << "->" << addPageMethod << '(' << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QStackedWidget"))) { - m_output << m_indent << parentWidget << "->addWidget(" << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QToolBar"))) { - m_output << m_indent << parentWidget << "->addWidget(" << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QDockWidget"))) { - m_output << m_indent << parentWidget << "->setWidget(" << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QScrollArea"))) { - m_output << m_indent << parentWidget << "->setWidget(" << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QSplitter"))) { - m_output << m_indent << parentWidget << "->addWidget(" << varName << ");\n"; - } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QMdiArea"))) { - m_output << m_indent << parentWidget << "->addSubWindow(" << varName << ");\n"; + m_output << m_indent << parentWidget << language::derefPointer + << addPageMethod << '(' << varName << ')' << language::eol; } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QWizard"))) { addWizardPage(varName, node, parentWidget); } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QToolBox"))) { - QString icon; - if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) { - icon += QLatin1String(", ") ; - icon += iconCall(picon); - } - const DomProperty *plabel = attributes.value(QLatin1String("label")); DomString *plabelString = plabel ? plabel->elementString() : 0; + QString icon; + if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) + icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition + m_output << m_indent << parentWidget << language::derefPointer << "addItem(" + << varName << icon << ", " << noTrCall(plabelString, pageDefaultString) + << ')' << language::eol; - m_output << m_indent << parentWidget << "->addItem(" << varName << icon << ", " << noTrCall(plabelString, pageDefaultString) << ");\n"; - - autoTrOutput(plabelString, pageDefaultString) << m_indent << parentWidget << "->setItemText(" - << parentWidget << "->indexOf(" << varName << "), " << autoTrCall(plabelString, pageDefaultString) << ");\n"; + autoTrOutput(plabelString, pageDefaultString) << m_indent << parentWidget + << language::derefPointer << "setItemText(" << parentWidget + << language::derefPointer << "indexOf(" << varName << "), " + << autoTrCall(plabelString, pageDefaultString) << ')' << language::eol; -#ifndef QT_NO_TOOLTIP if (DomProperty *ptoolTip = attributes.value(QLatin1String("toolTip"))) { - autoTrOutput(ptoolTip->elementString()) << m_indent << parentWidget << "->setItemToolTip(" - << parentWidget << "->indexOf(" << varName << "), " << autoTrCall(ptoolTip->elementString()) << ");\n"; + autoTrOutput(ptoolTip->elementString()) + << language::openQtConfig(toolTipConfigKey()) + << m_indent << parentWidget << language::derefPointer << "setItemToolTip(" << parentWidget + << language::derefPointer << "indexOf(" << varName << "), " + << autoTrCall(ptoolTip->elementString()) << ')' << language::eol + << language::closeQtConfig(toolTipConfigKey()); } -#endif // QT_NO_TOOLTIP } else if (m_uic->customWidgetsInfo()->extends(parentClass, QLatin1String("QTabWidget"))) { - QString icon; - if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) { - icon += QLatin1String(", "); - icon += iconCall(picon); - } - const DomProperty *ptitle = attributes.value(QLatin1String("title")); DomString *ptitleString = ptitle ? ptitle->elementString() : 0; + QString icon; + if (const DomProperty *picon = attributes.value(QLatin1String("icon"))) + icon = QLatin1String(", ") + iconCall(picon); // Side effect: Writes icon definition + m_output << m_indent << parentWidget << language::derefPointer << "addTab(" + << varName << icon << ", " << "QString())" << language::eol; - m_output << m_indent << parentWidget << "->addTab(" << varName << icon << ", " << "QString());\n"; - - autoTrOutput(ptitleString, pageDefaultString) << m_indent << parentWidget << "->setTabText(" - << parentWidget << "->indexOf(" << varName << "), " << autoTrCall(ptitleString, pageDefaultString) << ");\n"; + autoTrOutput(ptitleString, pageDefaultString) << m_indent << parentWidget + << language::derefPointer << "setTabText(" << parentWidget + << language::derefPointer << "indexOf(" << varName << "), " + << autoTrCall(ptitleString, pageDefaultString) << ')' << language::eol; -#ifndef QT_NO_TOOLTIP if (const DomProperty *ptoolTip = attributes.value(QLatin1String("toolTip"))) { - autoTrOutput(ptoolTip->elementString()) << m_indent << parentWidget << "->setTabToolTip(" - << parentWidget << "->indexOf(" << varName << "), " << autoTrCall(ptoolTip->elementString()) << ");\n"; + autoTrOutput(ptoolTip->elementString()) + << language::openQtConfig(toolTipConfigKey()) + << m_indent << parentWidget << language::derefPointer << "setTabToolTip(" + << parentWidget << language::derefPointer << "indexOf(" << varName + << "), " << autoTrCall(ptoolTip->elementString()) << ')' << language::eol + << language::closeQtConfig(toolTipConfigKey()); } -#endif // QT_NO_TOOLTIP -#ifndef QT_NO_WHATSTHIS if (const DomProperty *pwhatsThis = attributes.value(QLatin1String("whatsThis"))) { - autoTrOutput(pwhatsThis->elementString()) << m_indent << parentWidget << "->setTabWhatsThis(" - << parentWidget << "->indexOf(" << varName << "), " << autoTrCall(pwhatsThis->elementString()) << ");\n"; + autoTrOutput(pwhatsThis->elementString()) + << language::openQtConfig(whatsThisConfigKey()) + << m_indent << parentWidget << language::derefPointer << "setTabWhatsThis(" + << parentWidget << language::derefPointer << "indexOf(" << varName + << "), " << autoTrCall(pwhatsThis->elementString()) << ')' << language::eol + << language::closeQtConfig(whatsThisConfigKey()); } -#endif // QT_NO_WHATSTHIS } // @@ -766,15 +772,21 @@ void WriteInitialization::acceptWidget(DomWidget *node) static const QLatin1String realPropertyNames[] = { QLatin1String("visible"), QLatin1String("cascadingSectionResizes"), + QLatin1String("minimumSectionSize"), // before defaultSectionSize QLatin1String("defaultSectionSize"), QLatin1String("highlightSections"), - QLatin1String("minimumSectionSize"), QLatin1String("showSortIndicator"), QLatin1String("stretchLastSection"), }; - if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTreeView")) - || m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTreeWidget"))) { + static const QStringList trees = { + QLatin1String("QTreeView"), QLatin1String("QTreeWidget") + }; + static const QStringList tables = { + QLatin1String("QTableView"), QLatin1String("QTableWidget") + }; + + if (cwi->extendsOneOf(className, trees)) { DomPropertyList headerProperties; for (auto realPropertyName : realPropertyNames) { const QString fakePropertyName = QLatin1String("header") @@ -784,12 +796,11 @@ void WriteInitialization::acceptWidget(DomWidget *node) headerProperties << fakeProperty; } } - writeProperties(varName + QLatin1String("->header()"), QLatin1String("QHeaderView"), - headerProperties, WritePropertyIgnoreObjectName); - - } else if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTableView")) - || m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTableWidget"))) { + writeProperties(varName + language::derefPointer + QLatin1String("header()"), + QLatin1String("QHeaderView"), headerProperties, + WritePropertyIgnoreObjectName); + } else if (cwi->extendsOneOf(className, tables)) { static const QLatin1String headerPrefixes[] = { QLatin1String("horizontalHeader"), QLatin1String("verticalHeader"), @@ -805,8 +816,9 @@ void WriteInitialization::acceptWidget(DomWidget *node) headerProperties << fakeProperty; } } - writeProperties(varName + QLatin1String("->") + headerPrefix + QLatin1String("()"), - QLatin1String("QHeaderView"), + const QString headerVar = varName + language::derefPointer + + headerPrefix + QLatin1String("()"); + writeProperties(headerVar, QLatin1String("QHeaderView"), headerProperties, WritePropertyIgnoreObjectName); } } @@ -823,8 +835,10 @@ void WriteInitialization::acceptWidget(DomWidget *node) continue; } - if (!name.isEmpty()) - m_output << m_indent << name << "->raise();\n"; + if (!name.isEmpty()) { + m_output << m_indent << name << language::derefPointer << "raise()" + << language::eol; + } } } @@ -855,11 +869,13 @@ void WriteInitialization::addButtonGroup(const DomWidget *buttonNode, const QStr m_output << m_indent; if (createGroupOnTheFly) m_output << className << " *"; - m_output << groupName << " = new " << className << '(' << m_mainFormVarName << ");\n"; + m_output << groupName << " = " << language::operatorNew + << className << '(' << m_mainFormVarName << ')' << language::eol; m_buttonGroups.insert(groupName); writeProperties(groupName, className, group->elementProperty()); } - m_output << m_indent << groupName << "->addButton(" << varName << ");\n"; + m_output << m_indent << groupName << language::derefPointer << "addButton(" + << varName << ')' << language::eol; } void WriteInitialization::acceptLayout(DomLayout *node) @@ -872,7 +888,7 @@ void WriteInitialization::acceptLayout(DomLayout *node) bool isGroupBox = false; - m_output << m_indent << varName << " = new " << className << '('; + m_output << m_indent << varName << " = " << language::operatorNew << className << '('; if (!m_layoutChain.top() && !isGroupBox) m_output << m_driver->findOrInsertWidget(m_widgetChain.top()); @@ -964,9 +980,12 @@ void WriteInitialization::writePropertyList(const QString &varName, return; const QStringList list = value.split(QLatin1Char(',')); const int count = list.count(); - for (int i = 0; i < count; i++) - if (list.at(i) != defaultValue) - m_output << m_indent << varName << "->" << setFunction << '(' << i << ", " << list.at(i) << ");\n"; + for (int i = 0; i < count; i++) { + if (list.at(i) != defaultValue) { + m_output << m_indent << varName << language::derefPointer << setFunction + << '(' << i << ", " << list.at(i) << ')' << language::eol; + } + } } void WriteInitialization::acceptSpacer(DomSpacer *node) @@ -983,6 +1002,24 @@ static inline QString formLayoutRole(int column, int colspan) return column == 0 ? QLatin1String("QFormLayout::LabelRole") : QLatin1String("QFormLayout::FieldRole"); } +static QString layoutAddMethod(DomLayoutItem::Kind kind, const QString &layoutClass) +{ + const QString methodPrefix = layoutClass == QLatin1String("QFormLayout") + ? QLatin1String("set") : QLatin1String("add"); + switch (kind) { + case DomLayoutItem::Widget: + return methodPrefix + QLatin1String("Widget"); + case DomLayoutItem::Layout: + return methodPrefix + QLatin1String("Layout"); + case DomLayoutItem::Spacer: + return methodPrefix + QLatin1String("Item"); + case DomLayoutItem::Unknown: + Q_ASSERT( false ); + break; + } + Q_UNREACHABLE(); +} + void WriteInitialization::acceptLayoutItem(DomLayoutItem *node) { TreeWalker::acceptLayoutItem(node); @@ -995,47 +1032,27 @@ void WriteInitialization::acceptLayoutItem(DomLayoutItem *node) const QString layoutName = m_driver->findOrInsertLayout(layout); const QString itemName = m_driver->findOrInsertLayoutItem(node); - QString addArgs; - QString methodPrefix = QLatin1String("add"); //Consistent API-design galore! + m_output << "\n" << m_indent << layoutName << language::derefPointer << "" + << layoutAddMethod(node->kind(), layout->attributeClass()) << '('; + if (layout->attributeClass() == QLatin1String("QGridLayout")) { const int row = node->attributeRow(); const int col = node->attributeColumn(); const int rowSpan = node->hasAttributeRowSpan() ? node->attributeRowSpan() : 1; const int colSpan = node->hasAttributeColSpan() ? node->attributeColSpan() : 1; - - addArgs = QString::fromLatin1("%1, %2, %3, %4, %5").arg(itemName).arg(row).arg(col).arg(rowSpan).arg(colSpan); + m_output << itemName << ", " << row << ", " << col << ", " << rowSpan << ", " << colSpan; if (!node->attributeAlignment().isEmpty()) - addArgs += QLatin1String(", ") + node->attributeAlignment(); + m_output << ", " << language::enumValue(node->attributeAlignment()); + } else if (layout->attributeClass() == QLatin1String("QFormLayout")) { + const int row = node->attributeRow(); + const int colSpan = node->hasAttributeColSpan() ? node->attributeColSpan() : 1; + const QString role = formLayoutRole(node->attributeColumn(), colSpan); + m_output << row << ", " << language::enumValue(role) << ", " << itemName; } else { - if (layout->attributeClass() == QLatin1String("QFormLayout")) { - methodPrefix = QLatin1String("set"); - const int row = node->attributeRow(); - const int colSpan = node->hasAttributeColSpan() ? node->attributeColSpan() : 1; - const QString role = formLayoutRole(node->attributeColumn(), colSpan); - addArgs = QString::fromLatin1("%1, %2, %3").arg(row).arg(role, itemName); - } else { - addArgs = itemName; - if (layout->attributeClass().contains(QLatin1String("Box")) && !node->attributeAlignment().isEmpty()) - addArgs += QLatin1String(", 0, ") + node->attributeAlignment(); - } - } - - // figure out "add" method - m_output << "\n" << m_indent << layoutName << "->"; - switch (node->kind()) { - case DomLayoutItem::Widget: - m_output << methodPrefix << "Widget(" << addArgs; - break; - case DomLayoutItem::Layout: - m_output << methodPrefix << "Layout(" << addArgs; - break; - case DomLayoutItem::Spacer: - m_output << methodPrefix << "Item(" << addArgs; - break; - case DomLayoutItem::Unknown: - Q_ASSERT( 0 ); - break; + m_output << itemName; + if (layout->attributeClass().contains(QLatin1String("Box")) && !node->attributeAlignment().isEmpty()) + m_output << ", 0, " << language::enumValue(node->attributeAlignment()); } m_output << ");\n\n"; } @@ -1048,7 +1065,8 @@ void WriteInitialization::acceptActionGroup(DomActionGroup *node) if (m_actionGroupChain.top()) varName = m_driver->findOrInsertActionGroup(m_actionGroupChain.top()); - m_output << m_indent << actionName << " = new QActionGroup(" << varName << ");\n"; + m_output << m_indent << actionName << " = " << language::operatorNew + << "QActionGroup(" << varName << ");\n"; writeProperties(actionName, QLatin1String("QActionGroup"), node->elementProperty()); m_actionGroupChain.push(node); @@ -1068,41 +1086,46 @@ void WriteInitialization::acceptAction(DomAction *node) if (m_actionGroupChain.top()) varName = m_driver->findOrInsertActionGroup(m_actionGroupChain.top()); - m_output << m_indent << actionName << " = new QAction(" << varName << ");\n"; + m_output << m_indent << actionName << " = " << language::operatorNew + << "QAction(" << varName << ')' << language::eol; writeProperties(actionName, QLatin1String("QAction"), node->elementProperty()); } void WriteInitialization::acceptActionRef(DomActionRef *node) { QString actionName = node->attributeName(); - const bool isSeparator = actionName == QLatin1String("separator"); - bool isMenu = false; + if (actionName.isEmpty() || !m_widgetChain.top() + || m_driver->actionGroupByName(actionName)) { + return; + } - QString varName = m_driver->findOrInsertWidget(m_widgetChain.top()); + const QString varName = m_driver->findOrInsertWidget(m_widgetChain.top()); - if (actionName.isEmpty() || !m_widgetChain.top()) { - return; - } else if (m_driver->actionGroupByName(actionName)) { - return; - } else if (const DomWidget *w = m_driver->widgetByName(actionName)) { - isMenu = m_uic->isMenu(w->attributeClass()); - } else if (!(m_driver->actionByName(actionName) || isSeparator)) { - fprintf(stderr, "%s: Warning: action `%s' not declared\n", - qPrintable(m_option.messagePrefix()), - actionName.toLatin1().data()); + if (m_widgetChain.top() && actionName == QLatin1String("separator")) { + // separator is always reserved! + m_actionOut << m_indent << varName << language::derefPointer + << "addSeparator()" << language::eol; return; } - if (m_widgetChain.top() && isSeparator) { - // separator is always reserved! - m_actionOut << m_indent << varName << "->addSeparator();\n"; + const DomWidget *domWidget = m_driver->widgetByName(actionName); + if (domWidget && m_uic->isMenu(domWidget->attributeClass())) { + m_actionOut << m_indent << varName << language::derefPointer + << "addAction(" << m_driver->findOrInsertWidget(domWidget) + << language::derefPointer << "menuAction())" << language::eol; return; } - if (isMenu) - actionName += QLatin1String("->menuAction()"); + const DomAction *domAction = m_driver->actionByName(actionName); + if (!domAction) { + fprintf(stderr, "%s: Warning: action `%s' not declared\n", + qPrintable(m_option.messagePrefix()), qPrintable(actionName)); + return; + } - m_actionOut << m_indent << varName << "->addAction(" << actionName << ");\n"; + m_actionOut << m_indent << varName << language::derefPointer + << "addAction(" << m_driver->findOrInsertAction(domAction) + << ')' << language::eol; } QString WriteInitialization::writeStringListProperty(const DomStringList *list) const @@ -1119,11 +1142,28 @@ QString WriteInitialization::writeStringListProperty(const DomStringList *list) str << '\n' << m_indent << " << " << trCall(values.at(i), comment); } else { for (int i = 0; i < values.size(); ++i) - str << " << QString::fromUtf8(" << fixString(values.at(i), m_dindent) << ')'; + str << " << " << language::qstring(values.at(i), m_dindent); } return propertyValue; } +static QString configKeyForProperty(const QString &propertyName) +{ + if (propertyName == QLatin1String("toolTip")) + return toolTipConfigKey(); + if (propertyName == QLatin1String("whatsThis")) + return whatsThisConfigKey(); + if (propertyName == QLatin1String("statusTip")) + return statusTipConfigKey(); + if (propertyName == QLatin1String("shortcut")) + return shortcutConfigKey(); + if (propertyName == QLatin1String("accessibleName") + || propertyName == QLatin1String("accessibleDescription")) { + return accessibilityConfigKey(); + } + return QString(); +} + void WriteInitialization::writeProperties(const QString &varName, const QString &className, const DomPropertyList &lst, @@ -1134,19 +1174,32 @@ void WriteInitialization::writeProperties(const QString &varName, if (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QAxWidget"))) { DomPropertyMap properties = propertyMap(lst); if (DomProperty *p = properties.value(QLatin1String("control"))) { - m_output << m_indent << varName << "->setControl(QString::fromUtf8(" - << fixString(toString(p->elementString()), m_dindent) << "));\n"; + m_output << m_indent << varName << language::derefPointer << "setControl(" + << language::qstring(toString(p->elementString()), m_dindent) + << ')' << language::eol; } } QString indent; if (!m_widgetChain.top()) { indent = m_option.indent; - m_output << m_indent << "if (" << varName << "->objectName().isEmpty())\n"; + switch (language::language()) { + case Language::Cpp: + m_output << m_indent << "if (" << varName << "->objectName().isEmpty())\n"; + break; + case Language::Python: + m_output << m_indent << "if " << varName << ".objectName():\n"; + break; + } + } + if (!(flags & WritePropertyIgnoreObjectName)) { + QString objectName = varName; + if (!language::self.isEmpty() && objectName.startsWith(language::self)) + objectName.remove(0, language::self.size()); + m_output << m_indent << indent + << varName << language::derefPointer << "setObjectName(" + << language::qstring(objectName, m_dindent) << ')' << language::eol; } - if (!(flags & WritePropertyIgnoreObjectName)) - m_output << m_indent << indent << varName - << "->setObjectName(QString::fromUtf8(" << fixString(varName, m_dindent) << "));\n"; int leftMargin, topMargin, rightMargin, bottomMargin; leftMargin = topMargin = rightMargin = bottomMargin = -1; @@ -1162,28 +1215,31 @@ void WriteInitialization::writeProperties(const QString &varName, // special case for the property `geometry': Do not use position if (isTopLevel && propertyName == QLatin1String("geometry") && p->elementRect()) { const DomRect *r = p->elementRect(); - m_output << m_indent << varName << "->resize(" << r->elementWidth() << ", " << r->elementHeight() << ");\n"; + m_output << m_indent << varName << language::derefPointer << "resize(" + << r->elementWidth() << ", " << r->elementHeight() << ')' << language::eol; continue; } if (propertyName == QLatin1String("currentRow") // QListWidget::currentRow && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QListWidget"))) { - m_delayedOut << m_indent << varName << "->setCurrentRow(" - << p->elementNumber() << ");\n"; + m_delayedOut << m_indent << varName << language::derefPointer + << "setCurrentRow(" << p->elementNumber() << ')' << language::eol; continue; } + static const QStringList currentIndexWidgets = { + QLatin1String("QComboBox"), QLatin1String("QStackedWidget"), + QLatin1String("QTabWidget"), QLatin1String("QToolBox") + }; if (propertyName == QLatin1String("currentIndex") // set currentIndex later - && (m_uic->customWidgetsInfo()->extends(className, QLatin1String("QComboBox")) - || m_uic->customWidgetsInfo()->extends(className, QLatin1String("QStackedWidget")) - || m_uic->customWidgetsInfo()->extends(className, QLatin1String("QTabWidget")) - || m_uic->customWidgetsInfo()->extends(className, QLatin1String("QToolBox")))) { - m_delayedOut << m_indent << varName << "->setCurrentIndex(" - << p->elementNumber() << ");\n"; + && (m_uic->customWidgetsInfo()->extendsOneOf(className, currentIndexWidgets))) { + m_delayedOut << m_indent << varName << language::derefPointer + << "setCurrentIndex(" << p->elementNumber() << ')' << language::eol; continue; } if (propertyName == QLatin1String("tabSpacing") && m_uic->customWidgetsInfo()->extends(className, QLatin1String("QToolBox"))) { - m_delayedOut << m_indent << varName << "->layout()->setSpacing(" - << p->elementNumber() << ");\n"; + m_delayedOut << m_indent << varName << language::derefPointer + << "layout()" << language::derefPointer << "setSpacing(" + << p->elementNumber() << ')' << language::eol; continue; } if (propertyName == QLatin1String("control") // ActiveQt support @@ -1210,10 +1266,15 @@ void WriteInitialization::writeProperties(const QString &varName, if (p->elementEnum() == QLatin1String("Qt::Vertical")) shape = QLatin1String("QFrame::VLine"); - m_output << m_indent << varName << "->setFrameShape(" << shape << ");\n"; + m_output << m_indent << varName << language::derefPointer << "setFrameShape(" + << language::enumValue(shape) << ')' << language::eol; // QFrame Default is 'Plain'. Make the line 'Sunken' unless otherwise specified - if (!frameShadowEncountered) - m_output << m_indent << varName << "->setFrameShadow(QFrame::Sunken);\n"; + if (!frameShadowEncountered) { + m_output << m_indent << varName << language::derefPointer + << "setFrameShadow(" + << language::enumValue(QLatin1String("QFrame::Sunken")) + << ')' << language::eol; + } continue; } else if ((flags & WritePropertyIgnoreMargin) && propertyName == QLatin1String("margin")) { continue; @@ -1245,25 +1306,28 @@ void WriteInitialization::writeProperties(const QString &varName, QString setFunction; - if (stdset) { - setFunction = QLatin1String("->set") - + propertyName.at(0).toUpper() - + propertyName.midRef(1) - + QLatin1Char('('); - } else { - setFunction += QLatin1String("->setProperty(\"") - + propertyName - + QLatin1String("\", QVariant"); - if (p->kind() == DomProperty::Enum) - setFunction += QLatin1String("::fromValue"); - setFunction += QLatin1Char('('); - } + { + QTextStream str(&setFunction); + if (stdset) { + str << language::derefPointer <<"set" << propertyName.at(0).toUpper() + << propertyName.midRef(1) << '('; + } else { + str << language::derefPointer << QLatin1String("setProperty(\"") + << propertyName << "\", "; + if (language::language() == Language::Cpp) { + str << "QVariant"; + if (p->kind() == DomProperty::Enum) + str << "::fromValue"; + str << '('; + } + } + } // QTextStream QString varNewName = varName; switch (p->kind()) { case DomProperty::Bool: { - propertyValue = p->elementBool(); + propertyValue = language::boolValue(p->elementBool() == language::cppTrue); break; } case DomProperty::Color: @@ -1274,13 +1338,12 @@ void WriteInitialization::writeProperties(const QString &varName, Buddy buddy = { varName, p->elementCstring() }; m_buddies.append(std::move(buddy)); } else { - if (stdset) - propertyValue = fixString(p->elementCstring(), m_dindent); - else { - propertyValue = QLatin1String("QByteArray(") - + fixString(p->elementCstring(), m_dindent) - + QLatin1Char(')'); - } + QTextStream str(&propertyValue); + if (!stdset) + str << "QByteArray("; + str << language::charliteral(p->elementCstring(), m_dindent); + if (!stdset) + str << ')'; } break; case DomProperty::Cursor: @@ -1289,17 +1352,19 @@ void WriteInitialization::writeProperties(const QString &varName, break; case DomProperty::CursorShape: if (p->hasAttributeStdset() && !p->attributeStdset()) - varNewName += QLatin1String("->viewport()"); - propertyValue = QString::fromLatin1("QCursor(Qt::%1)") - .arg(p->elementCursorShape()); + varNewName += language::derefPointer + QLatin1String("viewport()"); + propertyValue = QLatin1String("QCursor(Qt") + language::qualifier + + p->elementCursorShape() + QLatin1Char(')'); break; case DomProperty::Enum: propertyValue = p->elementEnum(); - if (!propertyValue.contains(QLatin1String("::"))) - propertyValue = className + QLatin1String("::") + propertyValue; + if (propertyValue.contains(language::cppQualifier)) + propertyValue = language::enumValue(propertyValue); + else + propertyValue.prepend(className + language::qualifier); break; case DomProperty::Set: - propertyValue = p->elementSet(); + propertyValue = language::enumValue(p->elementSet()); break; case DomProperty::Font: propertyValue = writeFontProperties(p->elementFont()); @@ -1313,8 +1378,8 @@ void WriteInitialization::writeProperties(const QString &varName, case DomProperty::Palette: { const DomPalette *pal = p->elementPalette(); const QString paletteName = m_driver->unique(QLatin1String("palette")); - m_output << m_indent << "QPalette " << paletteName << ";\n"; - + m_output << m_indent << language::stackVariable("QPalette", paletteName) + << language::eol; writeColorGroup(pal->elementActive(), QLatin1String("QPalette::Active"), paletteName); writeColorGroup(pal->elementInactive(), QLatin1String("QPalette::Inactive"), paletteName); writeColorGroup(pal->elementDisabled(), QLatin1String("QPalette::Disabled"), paletteName); @@ -1350,15 +1415,16 @@ void WriteInitialization::writeProperties(const QString &varName, } case DomProperty::Locale: { const DomLocale *locale = p->elementLocale(); - propertyValue = QString::fromLatin1("QLocale(QLocale::%1, QLocale::%2)") - .arg(locale->attributeLanguage(), locale->attributeCountry()); + QTextStream(&propertyValue) << "QLocale(QLocale" << language::qualifier + << locale->attributeLanguage() << ", QLocale" << language::qualifier + << locale->attributeCountry() << ')'; break; } case DomProperty::SizePolicy: { const QString spName = writeSizePolicy( p->elementSizePolicy()); - m_output << m_indent << spName << QString::fromLatin1( - ".setHeightForWidth(%1->sizePolicy().hasHeightForWidth());\n") - .arg(varName); + m_output << m_indent << spName << ".setHeightForWidth(" + << varName << language::derefPointer << "sizePolicy().hasHeightForWidth())" + << language::eol; propertyValue = spName; break; @@ -1449,8 +1515,8 @@ void WriteInitialization::writeProperties(const QString &varName, case DomProperty::Url: { const DomUrl* u = p->elementUrl(); - propertyValue = QString::fromLatin1("QUrl(QString::fromUtf8(%1))") - .arg(fixString(u->elementString()->text(), m_dindent)); + QTextStream(&propertyValue) << "QUrl(" + << language::qstring(u->elementString()->text(), m_dindent) << ")"; break; } case DomProperty::Brush: @@ -1460,29 +1526,19 @@ void WriteInitialization::writeProperties(const QString &varName, break; } - if (propertyValue.size()) { - const char* defineC = 0; - if (propertyName == QLatin1String("toolTip")) - defineC = toolTipDefineC; - else if (propertyName == QLatin1String("whatsThis")) - defineC = whatsThisDefineC; - else if (propertyName == QLatin1String("statusTip")) - defineC = statusTipDefineC; - else if (propertyName == QLatin1String("shortcut")) - defineC = shortcutDefineC; - else if (propertyName == QLatin1String("accessibleName") || propertyName == QLatin1String("accessibleDescription")) - defineC = accessibilityDefineC; + if (!propertyValue.isEmpty()) { + const QString configKey = configKeyForProperty(propertyName); QTextStream &o = delayProperty ? m_delayedOut : autoTrOutput(p); - if (defineC) - openIfndef(o, QLatin1String(defineC)); + if (!configKey.isEmpty()) + o << language::openQtConfig(configKey); o << m_indent << varNewName << setFunction << propertyValue; - if (!stdset) + if (!stdset && language::language() == Language::Cpp) o << ')'; - o << ");\n"; - if (defineC) - closeIfndef(o, QLatin1String(defineC)); + o << ')' << language::eol; + if (!configKey.isEmpty()) + o << language::closeQtConfig(configKey); if (varName == m_mainFormVarName && &o == &m_refreshOut) { // this is the only place (currently) where we output mainForm name to the retranslateUi(). @@ -1492,11 +1548,9 @@ void WriteInitialization::writeProperties(const QString &varName, } } if (leftMargin != -1 || topMargin != -1 || rightMargin != -1 || bottomMargin != -1) { - m_output << m_indent << varName << QLatin1String("->setContentsMargins(") - << leftMargin << QLatin1String(", ") - << topMargin << QLatin1String(", ") - << rightMargin << QLatin1String(", ") - << bottomMargin << QLatin1String(");\n"); + m_output << m_indent << varName << language::derefPointer << "setContentsMargins(" + << leftMargin << ", " << topMargin << ", " + << rightMargin << ", " << bottomMargin << ");\n"; } } @@ -1515,20 +1569,15 @@ QString WriteInitialization::writeSizePolicy(const DomSizePolicy *sp) const QString spName = m_driver->unique(QLatin1String("sizePolicy")); m_sizePolicyNameMap.insert(sizePolicyHandle, spName); - m_output << m_indent << "QSizePolicy " << spName; - do { - if (sp->hasElementHSizeType() && sp->hasElementVSizeType()) { - m_output << "(static_cast<QSizePolicy::Policy>(" << sp->elementHSizeType() - << "), static_cast<QSizePolicy::Policy>(" << sp->elementVSizeType() << "));\n"; - break; - } - if (sp->hasAttributeHSizeType() && sp->hasAttributeVSizeType()) { - m_output << "(QSizePolicy::" << sp->attributeHSizeType() << ", QSizePolicy::" - << sp->attributeVSizeType() << ");\n"; - break; - } - m_output << ";\n"; - } while (false); + m_output << m_indent << language::stackVariableWithInitParameters("QSizePolicy", spName); + if (sp->hasElementHSizeType() && sp->hasElementVSizeType()) { + m_output << "QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementHSizeType()) + << ", QSizePolicy" << language::qualifier << language::sizePolicy(sp->elementVSizeType()); + } else if (sp->hasAttributeHSizeType() && sp->hasAttributeVSizeType()) { + m_output << "QSizePolicy" << language::qualifier << sp->attributeHSizeType() + << ", QSizePolicy" << language::qualifier << sp->attributeVSizeType(); + } + m_output << ')' << language::eol; m_output << m_indent << spName << ".setHorizontalStretch(" << sp->elementHorStretch() << ");\n"; @@ -1552,10 +1601,11 @@ QString WriteInitialization::writeFontProperties(const DomFont *f) const QString fontName = m_driver->unique(QLatin1String("font")); m_fontPropertiesNameMap.insert(FontHandle(f), fontName); - m_output << m_indent << "QFont " << fontName << ";\n"; + m_output << m_indent << language::stackVariable("QFont", fontName) + << language::eol; if (f->hasElementFamily() && !f->elementFamily().isEmpty()) { - m_output << m_indent << fontName << ".setFamily(QString::fromUtf8(" - << fixString(f->elementFamily(), m_dindent) << "));\n"; + m_output << m_indent << fontName << ".setFamily(" + << language::qstring(f->elementFamily(), m_dindent) << ");\n"; } if (f->hasElementPointSize() && f->elementPointSize() > 0) { m_output << m_indent << fontName << ".setPointSize(" << f->elementPointSize() @@ -1564,15 +1614,15 @@ QString WriteInitialization::writeFontProperties(const DomFont *f) if (f->hasElementBold()) { m_output << m_indent << fontName << ".setBold(" - << (f->elementBold() ? "true" : "false") << ");\n"; + << language::boolValue(f->elementBold()) << ')' << language::eol; } if (f->hasElementItalic()) { m_output << m_indent << fontName << ".setItalic(" - << (f->elementItalic() ? "true" : "false") << ");\n"; + << language::boolValue(f->elementItalic()) << ')' << language::eol; } if (f->hasElementUnderline()) { m_output << m_indent << fontName << ".setUnderline(" - << (f->elementUnderline() ? "true" : "false") << ");\n"; + << language::boolValue(f->elementUnderline()) << ')' << language::eol; } if (f->hasElementWeight() && f->elementWeight() > 0) { m_output << m_indent << fontName << ".setWeight(" @@ -1580,23 +1630,35 @@ QString WriteInitialization::writeFontProperties(const DomFont *f) } if (f->hasElementStrikeOut()) { m_output << m_indent << fontName << ".setStrikeOut(" - << (f->elementStrikeOut() ? "true" : "false") << ");\n"; + << language::boolValue(f->elementStrikeOut()) << ')' << language::eol; } if (f->hasElementKerning()) { m_output << m_indent << fontName << ".setKerning(" - << (f->elementKerning() ? "true" : "false") << ");\n"; + << language::boolValue(f->elementKerning()) << ')' << language::eol; } if (f->hasElementAntialiasing()) { - m_output << m_indent << fontName << ".setStyleStrategy(" - << (f->elementAntialiasing() ? "QFont::PreferDefault" : "QFont::NoAntialias") << ");\n"; + m_output << m_indent << fontName << ".setStyleStrategy(QFont" + << language::qualifier + << (f->elementAntialiasing() ? "PreferDefault" : "NoAntialias") + << ')' << language::eol; } if (f->hasElementStyleStrategy()) { - m_output << m_indent << fontName << ".setStyleStrategy(QFont::" - << f->elementStyleStrategy() << ");\n"; + m_output << m_indent << fontName << ".setStyleStrategy(QFont" + << language::qualifier << f->elementStyleStrategy() << ')' << language::eol; } return fontName; } +static void writeIconAddFile(QTextStream &output, const QString &indent, + const QString &iconName, const QString &fileName, + const char *mode, const char *state) +{ + output << indent << iconName << ".addFile(" + << language::qstring(fileName, indent) << ", QSize(), QIcon" + << language::qualifier << mode << ", QIcon" << language::qualifier + << state << ')' << language::eol; +} + // Post 4.4 write resource icon static void writeResourceIcon(QTextStream &output, const QString &iconName, @@ -1604,91 +1666,92 @@ static void writeResourceIcon(QTextStream &output, const DomResourceIcon *i) { if (i->hasElementNormalOff()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementNormalOff()->text(), indent) - << "), QSize(), QIcon::Normal, QIcon::Off);\n"; + writeIconAddFile(output, indent, iconName, i->elementNormalOff()->text(), + "Normal", "Off"); } if (i->hasElementNormalOn()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementNormalOn()->text(), indent) - << "), QSize(), QIcon::Normal, QIcon::On);\n"; + writeIconAddFile(output, indent, iconName, i->elementNormalOn()->text(), + "Normal", "On"); } if (i->hasElementDisabledOff()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementDisabledOff()->text(), indent) - << "), QSize(), QIcon::Disabled, QIcon::Off);\n"; + writeIconAddFile(output, indent, iconName, i->elementDisabledOff()->text(), + "Disabled", "Off"); } if (i->hasElementDisabledOn()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementDisabledOn()->text(), indent) - << "), QSize(), QIcon::Disabled, QIcon::On);\n"; + writeIconAddFile(output, indent, iconName, i->elementDisabledOn()->text(), + "Disabled", "On"); } if (i->hasElementActiveOff()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementActiveOff()->text(), indent) - << "), QSize(), QIcon::Active, QIcon::Off);\n"; + writeIconAddFile(output, indent, iconName, i->elementActiveOff()->text(), + "Active", "Off"); } if (i->hasElementActiveOn()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementActiveOn()->text(), indent) - << "), QSize(), QIcon::Active, QIcon::On);\n"; + writeIconAddFile(output, indent, iconName, i->elementActiveOn()->text(), + "Active", "On"); } if (i->hasElementSelectedOff()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementSelectedOff()->text(), indent) - << "), QSize(), QIcon::Selected, QIcon::Off);\n"; + writeIconAddFile(output, indent, iconName, i->elementSelectedOff()->text(), + "Selected", "Off"); } if (i->hasElementSelectedOn()) { - output << indent << iconName << ".addFile(QString::fromUtf8(" - << fixString(i->elementSelectedOn()->text(), indent) - << "), QSize(), QIcon::Selected, QIcon::On);\n"; + writeIconAddFile(output, indent, iconName, i->elementSelectedOff()->text(), + "Selected", "On"); } } +static void writeIconAddPixmap(QTextStream &output, const QString &indent, + const QString &iconName, const QString &call, + const char *mode, const char *state) +{ + output << indent << iconName << ".addPixmap(" << call << ", QIcon" + << language::qualifier << mode << ", QIcon" << language::qualifier + << state << ')' << language::eol; +} + void WriteInitialization::writePixmapFunctionIcon(QTextStream &output, const QString &iconName, const QString &indent, const DomResourceIcon *i) const { if (i->hasElementNormalOff()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementNormalOff()->text()) - << ", QIcon::Normal, QIcon::Off);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementNormalOff()->text()), + "Normal", "Off"); } if (i->hasElementNormalOn()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementNormalOn()->text()) - << ", QIcon::Normal, QIcon::On);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementNormalOn()->text()), + "Normal", "On"); } if (i->hasElementDisabledOff()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementDisabledOff()->text()) - << ", QIcon::Disabled, QIcon::Off);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementDisabledOff()->text()), + "Disabled", "Off"); } if (i->hasElementDisabledOn()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementDisabledOn()->text()) - << ", QIcon::Disabled, QIcon::On);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementDisabledOn()->text()), + "Disabled", "On"); } if (i->hasElementActiveOff()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementActiveOff()->text()) - << ", QIcon::Active, QIcon::Off);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementActiveOff()->text()), + "Active", "Off"); } if (i->hasElementActiveOn()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementActiveOn()->text()) - << ", QIcon::Active, QIcon::On);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementActiveOn()->text()), + "Active", "On"); } if (i->hasElementSelectedOff()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementSelectedOff()->text()) - << ", QIcon::Selected, QIcon::Off);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementSelectedOff()->text()), + "Selected", "Off"); } if (i->hasElementSelectedOn()) { - output << indent << iconName << ".addPixmap(" - << pixCall(QLatin1String("QPixmap"), i->elementSelectedOn()->text()) - << ", QIcon::Selected, QIcon::On);\n"; + writeIconAddPixmap(output, indent, iconName, + pixCall(QLatin1String("QPixmap"), i->elementSelectedOn()->text()), + "Selected", "On"); } } @@ -1714,7 +1777,6 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i) writePixmapFunctionIcon(m_output, iconName, m_indent, i); } else { // Theme: Generate code to check the theme and default to resource - const QString themeIconName = fixString(i->attributeTheme(), QString()); if (iconHasStatePixmaps(i)) { // Theme + default state pixmaps: // Generate code to check the theme and default to state pixmaps @@ -1726,8 +1788,8 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i) m_output << "QString "; m_firstThemeIcon = false; } - m_output << themeNameStringVariableC << " = QString::fromUtf8(" - << themeIconName << ");\n"; + m_output << themeNameStringVariableC << " = " + << language::qstring(i->attributeTheme()) << ";\n"; m_output << m_indent << "if (QIcon::hasThemeIcon(" << themeNameStringVariableC << ")) {\n" @@ -1740,9 +1802,8 @@ QString WriteInitialization::writeIconProperties(const DomResourceIcon *i) m_output << m_indent << "}\n"; } else { // Theme, but no state pixmaps: Construct from theme directly. - m_output << m_indent << "QIcon " << iconName - << "(QIcon::fromTheme(QString::fromUtf8(" - << themeIconName << ")));\n"; + m_output << m_indent << "QIcon " << iconName << "(QIcon::fromTheme(" + << language::qstring(i->attributeTheme()) << "));\n"; } // Theme, but not state } // >= 4.4 } else { // pre-4.4 legacy @@ -1765,6 +1826,13 @@ QString WriteInitialization::domColor2QString(const DomColor *c) .arg(c->elementBlue()); } +static inline QVersionNumber colorRoleVersionAdded(const QString &roleName) +{ + if (roleName == QLatin1String("PlaceholderText")) + return QVersionNumber(5, 12, 0); + return QVersionNumber(); +} + void WriteInitialization::writeColorGroup(DomColorGroup *colorGroup, const QString &group, const QString &paletteName) { if (!colorGroup) @@ -1776,7 +1844,7 @@ void WriteInitialization::writeColorGroup(DomColorGroup *colorGroup, const QStri const DomColor *color = colors.at(i); m_output << m_indent << paletteName << ".setColor(" << group - << ", " << "static_cast<QPalette::ColorRole>(" << QString::number(i) << ')' + << ", QPalette" << language::qualifier << language::paletteColorRole(i) << ", " << domColor2QString(color) << ");\n"; } @@ -1785,10 +1853,20 @@ void WriteInitialization::writeColorGroup(DomColorGroup *colorGroup, const QStri const auto &colorRoles = colorGroup->elementColorRole(); for (const DomColorRole *colorRole : colorRoles) { if (colorRole->hasAttributeRole()) { + const QString roleName = colorRole->attributeRole(); + const QVersionNumber versionAdded = colorRoleVersionAdded(roleName); const QString brushName = writeBrushInitialization(colorRole->elementBrush()); - m_output << m_indent << paletteName << ".setBrush(" << group - << ", " << "QPalette::" << colorRole->attributeRole() + if (!versionAdded.isNull()) { + m_output << "#if QT_VERSION >= QT_VERSION_CHECK(" + << versionAdded.majorVersion() << ", " << versionAdded.minorVersion() + << ", " << versionAdded.microVersion() << ")\n"; + } + m_output << m_indent << paletteName << ".setBrush(" + << language::enumValue(group) << ", " + << "QPalette" << language::qualifier << roleName << ", " << brushName << ");\n"; + if (!versionAdded.isNull()) + m_output << "#endif\n"; } } } @@ -1877,10 +1955,10 @@ void WriteInitialization::writeBrush(const DomBrush *brush, const QString &brush } else { const DomColor *color = brush->elementColor(); m_output << m_indent << "QBrush " << brushName << '(' - << domColor2QString(color) << ");\n"; + << domColor2QString(color) << ')' << language::eol; m_output << m_indent << brushName << ".setStyle(" - << "Qt::" << style << ");\n"; + << language::qtQualifier << style << ')' << language::eol; } } @@ -1958,15 +2036,14 @@ QString WriteInitialization::pixCall(const QString &t, const QString &text) cons return type; } + QTextStream str(&type); + str << '('; QString pixFunc = m_uic->pixmapFunction(); if (pixFunc.isEmpty()) - pixFunc = QLatin1String("QString::fromUtf8"); - - type += QLatin1Char('(') - + pixFunc - + QLatin1Char('(') - + fixString(text, m_dindent) - + QLatin1String("))"); + str << language::qstring(text, m_dindent); + else + str << pixFunc << '(' << language::charliteral(text, m_dindent) << ')'; + str << ')'; return type; } @@ -1989,13 +2066,15 @@ void WriteInitialization::initializeComboBox(DomWidget *w) if (icon) iconValue = iconCall(icon); - m_output << m_indent << varName << "->addItem("; + m_output << m_indent << varName << language::derefPointer << "addItem("; if (icon) m_output << iconValue << ", "; if (needsTranslation(text->elementString())) { m_output << "QString());\n"; - m_refreshOut << m_indent << varName << "->setItemText(" << i << ", " << trCall(text->elementString()) << ");\n"; + m_refreshOut << m_indent << varName << language::derefPointer + << "setItemText(" << i << ", " << trCall(text->elementString()) + << ')' << language::eol; } else { m_output << noTrCall(text->elementString()) << ");\n"; } @@ -2010,9 +2089,13 @@ QString WriteInitialization::disableSorting(DomWidget *w, const QString &varName if (!w->elementItem().isEmpty()) { tempName = m_driver->unique(QLatin1String("__sortingEnabled")); m_refreshOut << "\n"; - m_refreshOut << m_indent << "const bool " << tempName - << " = " << varName << "->isSortingEnabled();\n"; - m_refreshOut << m_indent << varName << "->setSortingEnabled(false);\n"; + m_refreshOut << m_indent; + if (language::language() == Language::Cpp) + m_refreshOut << "const bool "; + m_refreshOut << tempName << " = " << varName << language::derefPointer + << "isSortingEnabled()" << language::eol + << m_indent << varName << language::derefPointer + << "setSortingEnabled(" << language::boolValue(false) << ')' << language::eol; } return tempName; } @@ -2020,7 +2103,8 @@ QString WriteInitialization::disableSorting(DomWidget *w, const QString &varName void WriteInitialization::enableSorting(DomWidget *w, const QString &varName, const QString &tempName) { if (!w->elementItem().isEmpty()) { - m_refreshOut << m_indent << varName << "->setSortingEnabled(" << tempName << ");\n\n"; + m_refreshOut << m_indent << varName << language::derefPointer + << "setSortingEnabled(" << tempName << ')' << language::eol << '\n'; } } @@ -2042,10 +2126,15 @@ void WriteInitialization::enableSorting(DomWidget *w, const QString &varName, co void WriteInitialization::addInitializer(Item *item, const QString &name, int column, const QString &value, const QString &directive, bool translatable) const { - if (!value.isEmpty()) - item->addSetter(QLatin1String("->set") + name.at(0).toUpper() + name.midRef(1) + - QLatin1Char('(') + (column < 0 ? QString() : QString::number(column) + - QLatin1String(", ")) + value + QLatin1String(");"), directive, translatable); + if (!value.isEmpty()) { + QString setter; + QTextStream str(&setter); + str << language::derefPointer << "set" << name.at(0).toUpper() << name.midRef(1) << '('; + if (column >= 0) + str << column << ", "; + str << value << ");"; + item->addSetter(setter, directive, translatable); + } } /*! @@ -2090,10 +2179,11 @@ void WriteInitialization::addQtFlagsInitializer(Item *item, const DomPropertyMap &properties, const QString &name, int column) const { if (const DomProperty *p = properties.value(name)) { + const QString orOperator = QLatin1Char('|') + language::qtQualifier; QString v = p->elementSet(); if (!v.isEmpty()) { - v.replace(QLatin1Char('|'), QLatin1String("|Qt::")); - addInitializer(item, name, column, QLatin1String("Qt::") + v); + v.replace(QLatin1Char('|'), orOperator); + addInitializer(item, name, column, language::qtQualifier + v); } } } @@ -2108,7 +2198,7 @@ void WriteInitialization::addQtEnumInitializer(Item *item, if (const DomProperty *p = properties.value(name)) { QString v = p->elementEnum(); if (!v.isEmpty()) - addInitializer(item, name, column, QLatin1String("Qt::") + v); + addInitializer(item, name, column, language::qtQualifier + v); } } @@ -2127,9 +2217,12 @@ void WriteInitialization::addCommonInitializers(Item *item, addQtFlagsInitializer(item, properties, QLatin1String("textAlignment"), column); addQtEnumInitializer(item, properties, QLatin1String("checkState"), column); addStringInitializer(item, properties, QLatin1String("text"), column); - addStringInitializer(item, properties, QLatin1String("toolTip"), column, QLatin1String(toolTipDefineC)); - addStringInitializer(item, properties, QLatin1String("whatsThis"), column, QLatin1String(whatsThisDefineC)); - addStringInitializer(item, properties, QLatin1String("statusTip"), column, QLatin1String(statusTipDefineC)); + addStringInitializer(item, properties, QLatin1String("toolTip"), column, + toolTipConfigKey()); + addStringInitializer(item, properties, QLatin1String("whatsThis"), column, + whatsThisConfigKey()); + addStringInitializer(item, properties, QLatin1String("statusTip"), column, + statusTipConfigKey()); } void WriteInitialization::initializeListWidget(DomWidget *w) @@ -2154,7 +2247,9 @@ void WriteInitialization::initializeListWidget(DomWidget *w) addCommonInitializers(&item, properties); item.writeSetupUi(varName); - item.writeRetranslateUi(varName + QLatin1String("->item(") + QString::number(i) + QLatin1Char(')')); + QString parentPath; + QTextStream(&parentPath) << varName << language::derefPointer << "item(" << i << ')'; + item.writeRetranslateUi(parentPath); } enableSorting(w, varName, tempName); } @@ -2175,14 +2270,19 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w) if (const DomProperty *p = properties.value(QLatin1String("text"))) { DomString *str = p->elementString(); - if (str && str->text().isEmpty()) - m_output << m_indent << varName << "->headerItem()->setText(" << i << ", QString());\n"; + if (str && str->text().isEmpty()) { + m_output << m_indent << varName << language::derefPointer + << "headerItem()" << language::derefPointer << "setText(" + << i << ", QString())" << language::eol; + } } } const QString itemName = item.writeSetupUi(QString(), Item::DontConstruct); - item.writeRetranslateUi(varName + QLatin1String("->headerItem()")); - if (!itemName.isNull()) - m_output << m_indent << varName << "->setHeaderItem(" << itemName << ");\n"; + item.writeRetranslateUi(varName + language::derefPointer + QLatin1String("headerItem()")); + if (!itemName.isNull()) { + m_output << m_indent << varName << language::derefPointer + << "setHeaderItem(" << itemName << ')' << language::eol; + } if (w->elementItem().empty()) return; @@ -2193,7 +2293,9 @@ void WriteInitialization::initializeTreeWidget(DomWidget *w) for (int i = 0; i < items.count(); i++) { Item *itm = items[i]; itm->writeSetupUi(varName); - itm->writeRetranslateUi(varName + QLatin1String("->topLevelItem(") + QString::number(i) + QLatin1Char(')')); + QString parentPath; + QTextStream(&parentPath) << varName << language::derefPointer << "topLevelItem(" << i << ')'; + itm->writeRetranslateUi(parentPath); delete itm; } @@ -2255,8 +2357,10 @@ void WriteInitialization::initializeTableWidget(DomWidget *w) const auto &columns = w->elementColumn(); if (!columns.empty()) { - m_output << m_indent << "if (" << varName << "->columnCount() < " << columns.size() << ")\n" - << m_dindent << varName << "->setColumnCount(" << columns.size() << ");\n"; + m_output << m_indent << "if (" << varName << language::derefPointer << "columnCount() < " + << columns.size() << ")\n" + << m_dindent << varName << language::derefPointer << "setColumnCount(" + << columns.size() << ')' << language::eol; } for (int i = 0; i < columns.size(); ++i) { @@ -2268,8 +2372,12 @@ void WriteInitialization::initializeTableWidget(DomWidget *w) addCommonInitializers(&item, properties); QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable); - item.writeRetranslateUi(varName + QLatin1String("->horizontalHeaderItem(") + QString::number(i) + QLatin1Char(')')); - m_output << m_indent << varName << "->setHorizontalHeaderItem(" << QString::number(i) << ", " << itemName << ");\n"; + QString parentPath; + QTextStream(&parentPath) << varName << language::derefPointer + << "horizontalHeaderItem(" << i << ')'; + item.writeRetranslateUi(parentPath); + m_output << m_indent << varName << language::derefPointer << "setHorizontalHeaderItem(" + << i << ", " << itemName << ')' << language::eol; } } @@ -2277,8 +2385,9 @@ void WriteInitialization::initializeTableWidget(DomWidget *w) const auto &rows = w->elementRow(); if (!rows.isEmpty()) { - m_output << m_indent << "if (" << varName << "->rowCount() < " << rows.size() << ")\n" - << m_dindent << varName << "->setRowCount(" << rows.size() << ");\n"; + m_output << m_indent << "if (" << varName << language::derefPointer << "rowCount() < " << rows.size() << ")\n" + << m_dindent << varName << language::derefPointer << "setRowCount(" + << rows.size() << ')' << language::eol; } for (int i = 0; i < rows.size(); ++i) { @@ -2290,8 +2399,11 @@ void WriteInitialization::initializeTableWidget(DomWidget *w) addCommonInitializers(&item, properties); QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable); - item.writeRetranslateUi(varName + QLatin1String("->verticalHeaderItem(") + QString::number(i) + QLatin1Char(')')); - m_output << m_indent << varName << "->setVerticalHeaderItem(" << QString::number(i) << ", " << itemName << ");\n"; + QString parentPath; + QTextStream(&parentPath) << varName << language::derefPointer << "verticalHeaderItem(" << i << ')'; + item.writeRetranslateUi(parentPath); + m_output << m_indent << varName << language::derefPointer << "setVerticalHeaderItem(" + << i << ", " << itemName << ')' << language::eol; } } @@ -2311,8 +2423,12 @@ void WriteInitialization::initializeTableWidget(DomWidget *w) addCommonInitializers(&item, properties); QString itemName = item.writeSetupUi(QString(), Item::ConstructItemAndVariable); - item.writeRetranslateUi(varName + QLatin1String("->item(") + QString::number(r) + QLatin1String(", ") + QString::number(c) + QLatin1Char(')')); - m_output << m_indent << varName << "->setItem(" << QString::number(r) << ", " << QString::number(c) << ", " << itemName << ");\n"; + QString parentPath; + QTextStream(&parentPath) << varName << language::derefPointer << "item(" << r + << ", " << c << ')'; + item.writeRetranslateUi(parentPath); + m_output << m_indent << varName << language::derefPointer << "setItem(" + << r << ", " << c << ", " << itemName << ')' << language::eol; } } enableSorting(w, varName, tempName); @@ -2324,28 +2440,31 @@ QString WriteInitialization::trCall(const QString &str, const QString &commentHi return QLatin1String("QString()"); QString result; - const QString comment = commentHint.isEmpty() ? QString(QLatin1String("nullptr")) : fixString(commentHint, m_dindent); + QTextStream ts(&result); const bool idBasedTranslations = m_driver->useIdBasedTranslations(); if (m_option.translateFunction.isEmpty()) { if (idBasedTranslations || m_option.idBased) { - result += QLatin1String("qtTrId("); + ts << "qtTrId("; } else { - result += QLatin1String("QApplication::translate(\"") - + m_generatedClass - + QLatin1String("\", "); + ts << "QCoreApplication" << language::qualifier << "translate(" + << '"' << m_generatedClass << "\", "; } } else { - result += m_option.translateFunction + QLatin1Char('('); + ts << m_option.translateFunction << '('; } - result += fixString(idBasedTranslations ? id : str, m_dindent); + ts << language::charliteral(idBasedTranslations ? id : str, m_dindent); if (!idBasedTranslations && !m_option.idBased) { - result += QLatin1String(", ") + comment; + ts << ", "; + if (commentHint.isEmpty()) + ts << language::nullPtr; + else + ts << language::charliteral(commentHint, m_dindent); } - result += QLatin1Char(')'); + ts << ')'; return result; } @@ -2356,7 +2475,8 @@ void WriteInitialization::initializeMenu(DomWidget *w, const QString &/*parentWi const DomAction *action = m_driver->actionByName(menuAction); if (action && action->hasAttributeMenu()) { - m_output << m_indent << menuAction << " = " << menuName << "->menuAction();\n"; + m_output << m_indent << menuAction << " = " << menuName + << language::derefPointer << "menuAction()" << language::eol; } } @@ -2380,9 +2500,9 @@ QString WriteInitialization::noTrCall(DomString *str, const QString &defaultStri return QString(); if (str) value = str->text(); - QString ret = QLatin1String("QString::fromUtf8("); - ret += fixString(value, m_dindent); - ret += QLatin1Char(')'); + QString ret; + QTextStream ts(&ret); + ts << language::qstring(value, m_dindent); return ret; } @@ -2410,54 +2530,42 @@ QTextStream &WriteInitialization::autoTrOutput(const DomString *str, const QStri return m_output; } -bool WriteInitialization::isValidObject(const QString &name) const -{ - return m_registeredWidgets.contains(name) - || m_registeredActions.contains(name); -} - -QString WriteInitialization::findDeclaration(const QString &name) +WriteInitialization::Declaration WriteInitialization::findDeclaration(const QString &name) { - const QString normalized = Driver::normalizedName(name); - - if (DomWidget *widget = m_driver->widgetByName(normalized)) - return m_driver->findOrInsertWidget(widget); - if (DomAction *action = m_driver->actionByName(normalized)) - return m_driver->findOrInsertAction(action); - if (const DomButtonGroup *group = m_driver->findButtonGroup(normalized)) - return m_driver->findOrInsertButtonGroup(group); - return QString(); + if (const DomWidget *widget = m_driver->widgetByName(name)) + return {m_driver->findOrInsertWidget(widget), widget->attributeClass()}; + if (const DomAction *action = m_driver->actionByName(name)) + return {m_driver->findOrInsertAction(action), QStringLiteral("QAction")}; + if (const DomButtonGroup *group = m_driver->findButtonGroup(name)) + return {m_driver->findOrInsertButtonGroup(group), QStringLiteral("QButtonGroup")}; + return {}; } void WriteInitialization::acceptConnection(DomConnection *connection) { - const QString sender = findDeclaration(connection->elementSender()); - const QString receiver = findDeclaration(connection->elementReceiver()); - - if (sender.isEmpty() || receiver.isEmpty()) - return; + const QString senderName = connection->elementSender(); + const QString receiverName = connection->elementReceiver(); - m_output << m_indent << "QObject::connect(" - << sender - << ", " - << "SIGNAL("<<connection->elementSignal()<<')' - << ", " - << receiver - << ", " - << "SLOT("<<connection->elementSlot()<<')' - << ");\n"; -} - -DomWidget *WriteInitialization::findWidget(QLatin1String widgetClass) -{ - for (int i = m_widgetChain.count() - 1; i >= 0; --i) { - DomWidget *widget = m_widgetChain.at(i); + const auto senderDecl = findDeclaration(senderName); + const auto receiverDecl = findDeclaration(receiverName); - if (widget && m_uic->customWidgetsInfo()->extends(widget->attributeClass(), widgetClass)) - return widget; + if (senderDecl.name.isEmpty() || receiverDecl.name.isEmpty()) { + QString message; + QTextStream(&message) << m_option.messagePrefix() + << ": Warning: Invalid signal/slot connection: \"" + << senderName << "\" -> \"" << receiverName << "\"."; + fprintf(stderr, "%s\n", qPrintable(message)); + return; } - return 0; + language::SignalSlot theSignal{senderDecl.name, connection->elementSignal(), + senderDecl.className}; + language::SignalSlot theSlot{receiverDecl.name, connection->elementSlot(), + receiverDecl.className}; + + m_output << m_indent; + language::formatConnection(m_output, theSignal, theSlot); + m_output << ";\n"; } static void generateMultiDirectiveBegin(QTextStream &outputStream, const QSet<QString> &directives) @@ -2466,7 +2574,7 @@ static void generateMultiDirectiveBegin(QTextStream &outputStream, const QSet<QS return; if (directives.size() == 1) { - outputStream << "#ifndef " << *directives.cbegin() << endl; + outputStream << language::openQtConfig(*directives.cbegin()); return; } @@ -2474,7 +2582,10 @@ static void generateMultiDirectiveBegin(QTextStream &outputStream, const QSet<QS // sort (always generate in the same order): std::sort(list.begin(), list.end()); - outputStream << "#if !defined(" << list.join(QLatin1String(") || !defined(")) << ')' << endl; + outputStream << "#if " << language::qtConfig(list.constFirst()); + for (int i = 1, size = list.size(); i < size; ++i) + outputStream << " || " << language::qtConfig(list.at(i)); + outputStream << endl; } static void generateMultiDirectiveEnd(QTextStream &outputStream, const QSet<QString> &directives) @@ -2510,7 +2621,8 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp bool generateMultiDirective = false; if (emptyItemPolicy == Item::ConstructItemOnly && m_children.isEmpty()) { if (m_setupUiData.policy == ItemData::DontGenerate) { - m_setupUiStream << m_indent << "new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent << language::operatorNew << m_itemClassName + << '(' << parent << ')' << language::eol; return QString(); } if (m_setupUiData.policy == ItemData::GenerateWithMultiDirective) @@ -2521,19 +2633,27 @@ QString WriteInitialization::Item::writeSetupUi(const QString &parent, Item::Emp generateMultiDirectiveBegin(m_setupUiStream, m_setupUiData.directives); const QString uniqueName = m_driver->unique(QLatin1String("__") + m_itemClassName.toLower()); - m_setupUiStream << m_indent << m_itemClassName << " *" << uniqueName << " = new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent; + if (language::language() == Language::Cpp) + m_setupUiStream << m_itemClassName << " *"; + m_setupUiStream << uniqueName + << " = " << language::operatorNew << m_itemClassName << '(' << parent + << ')' << language::eol; if (generateMultiDirective) { m_setupUiStream << "#else\n"; - m_setupUiStream << m_indent << "new " << m_itemClassName << '(' << parent << ");\n"; + m_setupUiStream << m_indent << language::operatorNew << m_itemClassName + << '(' << parent << ')' << language::eol; generateMultiDirectiveEnd(m_setupUiStream, m_setupUiData.directives); } QMultiMap<QString, QString>::ConstIterator it = m_setupUiData.setters.constBegin(); while (it != m_setupUiData.setters.constEnd()) { - openIfndef(m_setupUiStream, it.key()); + if (!it.key().isEmpty()) + m_setupUiStream << language::openQtConfig(it.key()); m_setupUiStream << m_indent << uniqueName << it.value() << endl; - closeIfndef(m_setupUiStream, it.key()); + if (!it.key().isEmpty()) + m_setupUiStream << language::closeQtConfig(it.key()); ++it; } for (Item *child : qAsConst(m_children)) @@ -2550,7 +2670,10 @@ void WriteInitialization::Item::writeRetranslateUi(const QString &parentPath) generateMultiDirectiveBegin(m_retranslateUiStream, m_retranslateUiData.directives); const QString uniqueName = m_driver->unique(QLatin1String("___") + m_itemClassName.toLower()); - m_retranslateUiStream << m_indent << m_itemClassName << " *" << uniqueName << " = " << parentPath << ";\n"; + m_retranslateUiStream << m_indent; + if (language::language() == Language::Cpp) + m_retranslateUiStream << m_itemClassName << " *"; + m_retranslateUiStream << uniqueName << " = " << parentPath << language::eol; if (m_retranslateUiData.policy == ItemData::GenerateWithMultiDirective) generateMultiDirectiveEnd(m_retranslateUiStream, m_retranslateUiData.directives); @@ -2560,17 +2683,23 @@ void WriteInitialization::Item::writeRetranslateUi(const QString &parentPath) while (it != m_retranslateUiData.setters.constEnd()) { const QString newDirective = it.key(); if (oldDirective != newDirective) { - closeIfndef(m_retranslateUiStream, oldDirective); - openIfndef(m_retranslateUiStream, newDirective); + if (!oldDirective.isEmpty()) + m_retranslateUiStream << language::closeQtConfig(oldDirective); + if (!newDirective.isEmpty()) + m_retranslateUiStream << language::openQtConfig(newDirective); oldDirective = newDirective; } m_retranslateUiStream << m_indent << uniqueName << it.value() << endl; ++it; } - closeIfndef(m_retranslateUiStream, oldDirective); + if (!oldDirective.isEmpty()) + m_retranslateUiStream << language::closeQtConfig(oldDirective); - for (int i = 0; i < m_children.size(); i++) - m_children[i]->writeRetranslateUi(uniqueName + QLatin1String("->child(") + QString::number(i) + QLatin1Char(')')); + for (int i = 0; i < m_children.size(); i++) { + QString method; + QTextStream(&method) << uniqueName << language::derefPointer << "child(" << i << ')'; + m_children[i]->writeRetranslateUi(method); + } } void WriteInitialization::Item::addSetter(const QString &setter, const QString &directive, bool translatable) diff --git a/src/tools/uic/cpp/cppwriteinitialization.h b/src/tools/uic/cpp/cppwriteinitialization.h index cce83bd677..b90ffe00a7 100644 --- a/src/tools/uic/cpp/cppwriteinitialization.h +++ b/src/tools/uic/cpp/cppwriteinitialization.h @@ -174,8 +174,6 @@ private: void writeRetranslateUi(const QString &parentPath); void addSetter(const QString &setter, const QString &directive = QString(), bool translatable = false); // don't call it if you already added *this as a child of another Item void addChild(Item *child); // all setters should already been added - int setupUiCount() const { return m_setupUiData.setters.count(); } - int retranslateUiCount() const { return m_retranslateUiData.setters.count(); } private: struct ItemData { @@ -222,10 +220,13 @@ private: QString disableSorting(DomWidget *w, const QString &varName); void enableSorting(DomWidget *w, const QString &varName, const QString &tempName); - QString findDeclaration(const QString &name); - DomWidget *findWidget(QLatin1String widgetClass); + struct Declaration + { + QString name; + QString className; + }; - bool isValidObject(const QString &name) const; + Declaration findDeclaration(const QString &name); private: QString writeFontProperties(const DomFont *f); diff --git a/src/tools/uic/customwidgetsinfo.cpp b/src/tools/uic/customwidgetsinfo.cpp index 0ac0c2b6a3..d6a409152b 100644 --- a/src/tools/uic/customwidgetsinfo.cpp +++ b/src/tools/uic/customwidgetsinfo.cpp @@ -31,6 +31,8 @@ #include "ui4.h" #include "utils.h" +#include <utility> + QT_BEGIN_NAMESPACE CustomWidgetsInfo::CustomWidgetsInfo() = default; @@ -73,6 +75,24 @@ bool CustomWidgetsInfo::extends(const QString &classNameIn, QLatin1String baseCl return false; } +bool CustomWidgetsInfo::extendsOneOf(const QString &classNameIn, + const QStringList &baseClassNames) const +{ + if (baseClassNames.contains(classNameIn)) + return true; + + QString className = classNameIn; + while (const DomCustomWidget *c = customWidget(className)) { + const QString extends = c->elementExtends(); + if (className == extends) // Faulty legacy custom widget entries exist. + return false; + if (baseClassNames.contains(extends)) + return true; + className = extends; + } + return false; +} + bool CustomWidgetsInfo::isCustomWidgetContainer(const QString &className) const { if (const DomCustomWidget *dcw = m_customWidgets.value(className, 0)) @@ -96,5 +116,24 @@ QString CustomWidgetsInfo::customWidgetAddPageMethod(const QString &name) const return QString(); } +// add page methods for simple containers taking only the widget parameter +QString CustomWidgetsInfo::simpleContainerAddPageMethod(const QString &name) const +{ + using AddPageMethod = std::pair<const char *, const char *>; + + static AddPageMethod addPageMethods[] = { + {"QStackedWidget", "addWidget"}, + {"QToolBar", "addWidget"}, + {"QDockWidget", "setWidget"}, + {"QScrollArea", "setWidget"}, + {"QSplitter", "addWidget"}, + {"QMdiArea", "addSubWindow"} + }; + for (const auto &m : addPageMethods) { + if (extends(name, QLatin1String(m.first))) + return QLatin1String(m.second); + } + return QString(); +} QT_END_NAMESPACE diff --git a/src/tools/uic/customwidgetsinfo.h b/src/tools/uic/customwidgetsinfo.h index 944ed59215..8a10999027 100644 --- a/src/tools/uic/customwidgetsinfo.h +++ b/src/tools/uic/customwidgetsinfo.h @@ -48,20 +48,16 @@ public: void acceptCustomWidgets(DomCustomWidgets *node) override; void acceptCustomWidget(DomCustomWidget *node) override; - inline QStringList customWidgets() const - { return m_customWidgets.keys(); } - - inline bool hasCustomWidget(const QString &name) const - { return m_customWidgets.contains(name); } - inline DomCustomWidget *customWidget(const QString &name) const { return m_customWidgets.value(name); } QString customWidgetAddPageMethod(const QString &name) const; + QString simpleContainerAddPageMethod(const QString &name) const; QString realClassName(const QString &className) const; bool extends(const QString &className, QLatin1String baseClassName) const; + bool extendsOneOf(const QString &className, const QStringList &baseClassNames) const; bool isCustomWidgetContainer(const QString &className) const; diff --git a/src/tools/uic/databaseinfo.h b/src/tools/uic/databaseinfo.h index bebf139c5a..4015e39dbc 100644 --- a/src/tools/uic/databaseinfo.h +++ b/src/tools/uic/databaseinfo.h @@ -48,12 +48,6 @@ public: inline QStringList connections() const { return m_connections; } - inline QStringList cursors(const QString &connection) const - { return m_cursors.value(connection); } - - inline QStringList fields(const QString &connection) const - { return m_fields.value(connection); } - private: QStringList m_connections; QMap<QString, QStringList> m_cursors; diff --git a/src/tools/uic/driver.cpp b/src/tools/uic/driver.cpp index 6b3a6f8f69..a0812932ce 100644 --- a/src/tools/uic/driver.cpp +++ b/src/tools/uic/driver.cpp @@ -30,9 +30,13 @@ #include "uic.h" #include "ui4.h" +#include <language.h> + #include <qfileinfo.h> #include <qdebug.h> +#include <algorithm> + QT_BEGIN_NAMESPACE Driver::Driver() @@ -43,39 +47,52 @@ Driver::Driver() Driver::~Driver() = default; -QString Driver::findOrInsertWidget(DomWidget *ui_widget) -{ - if (!m_widgets.contains(ui_widget)) - m_widgets.insert(ui_widget, unique(ui_widget->attributeName(), ui_widget->attributeClass())); +static inline QString spacerItemClass() { return QStringLiteral("QSpacerItem"); } +static inline QString actionGroupClass() { return QStringLiteral("QActionGroup"); } +static inline QString actionClass() { return QStringLiteral("QAction"); } +static inline QString buttonGroupClass() { return QStringLiteral("QButtonGroup"); } - return m_widgets.value(ui_widget); +template <class DomClass> +const DomClass *Driver::findByAttributeName(const DomObjectHash<DomClass> &domHash, + const QString &name) const +{ + for (auto it = domHash.cbegin(), end = domHash.cend(); it != end; ++it) { + if (it.key()->attributeName() == name) + return it.key(); + } + return nullptr; } -QString Driver::findOrInsertSpacer(DomSpacer *ui_spacer) +template <class DomClass> +QString Driver::findOrInsert(DomObjectHash<DomClass> *domHash, const DomClass *dom, + const QString &className, bool isMember) { - if (!m_spacers.contains(ui_spacer)) { - QString name; - if (ui_spacer->hasAttributeName()) - name = ui_spacer->attributeName(); - m_spacers.insert(ui_spacer, unique(name, QLatin1String("QSpacerItem"))); + auto it = domHash->find(dom); + if (it == domHash->end()) { + const QString name = this->unique(dom->attributeName(), className); + it = domHash->insert(dom, isMember ? language::self + name : name); } + return it.value(); +} - return m_spacers.value(ui_spacer); +QString Driver::findOrInsertWidget(const DomWidget *ui_widget) +{ + // Top level is passed into setupUI(), everything else is a member variable + const bool isMember = !m_widgets.isEmpty(); + return findOrInsert(&m_widgets, ui_widget, ui_widget->attributeClass(), isMember); } -QString Driver::findOrInsertLayout(DomLayout *ui_layout) +QString Driver::findOrInsertSpacer(const DomSpacer *ui_spacer) { - if (!m_layouts.contains(ui_layout)) { - QString name; - if (ui_layout->hasAttributeName()) - name = ui_layout->attributeName(); - m_layouts.insert(ui_layout, unique(name, ui_layout->attributeClass())); - } + return findOrInsert(&m_spacers, ui_spacer, spacerItemClass()); +} - return m_layouts.value(ui_layout); +QString Driver::findOrInsertLayout(const DomLayout *ui_layout) +{ + return findOrInsert(&m_layouts, ui_layout, ui_layout->attributeClass()); } -QString Driver::findOrInsertLayoutItem(DomLayoutItem *ui_layoutItem) +QString Driver::findOrInsertLayoutItem(const DomLayoutItem *ui_layoutItem) { switch (ui_layoutItem->kind()) { case DomLayoutItem::Widget: @@ -93,38 +110,25 @@ QString Driver::findOrInsertLayoutItem(DomLayoutItem *ui_layoutItem) return QString(); } -QString Driver::findOrInsertActionGroup(DomActionGroup *ui_group) +QString Driver::findOrInsertActionGroup(const DomActionGroup *ui_group) { - if (!m_actionGroups.contains(ui_group)) - m_actionGroups.insert(ui_group, unique(ui_group->attributeName(), QLatin1String("QActionGroup"))); - - return m_actionGroups.value(ui_group); + return findOrInsert(&m_actionGroups, ui_group, actionGroupClass()); } -QString Driver::findOrInsertAction(DomAction *ui_action) +QString Driver::findOrInsertAction(const DomAction *ui_action) { - if (!m_actions.contains(ui_action)) - m_actions.insert(ui_action, unique(ui_action->attributeName(), QLatin1String("QAction"))); - - return m_actions.value(ui_action); + return findOrInsert(&m_actions, ui_action, actionClass()); } QString Driver::findOrInsertButtonGroup(const DomButtonGroup *ui_group) { - ButtonGroupNameHash::iterator it = m_buttonGroups.find(ui_group); - if (it == m_buttonGroups.end()) - it = m_buttonGroups.insert(ui_group, unique(ui_group->attributeName(), QLatin1String("QButtonGroup"))); - return it.value(); + return findOrInsert(&m_buttonGroups, ui_group, buttonGroupClass()); } // Find a group by its non-uniqified name const DomButtonGroup *Driver::findButtonGroup(const QString &attributeName) const { - const ButtonGroupNameHash::const_iterator cend = m_buttonGroups.constEnd(); - for (ButtonGroupNameHash::const_iterator it = m_buttonGroups.constBegin(); it != cend; ++it) - if (it.key()->attributeName() == attributeName) - return it.key(); - return 0; + return findByAttributeName(m_buttonGroups, attributeName); } @@ -136,11 +140,9 @@ QString Driver::findOrInsertName(const QString &name) QString Driver::normalizedName(const QString &name) { QString result = name; - QChar *data = result.data(); - for (int i = name.size(); --i >= 0; ++data) { - if (!data->isLetterOrNumber()) - *data = QLatin1Char('_'); - } + std::replace_if(result.begin(), result.end(), + [] (QChar c) { return !c.isLetterOrNumber(); }, + QLatin1Char('_')); return result; } @@ -149,23 +151,21 @@ QString Driver::unique(const QString &instanceName, const QString &className) QString name; bool alreadyUsed = false; - if (instanceName.size()) { - int id = 1; - name = instanceName; - name = normalizedName(name); + if (!instanceName.isEmpty()) { + name = normalizedName(instanceName); QString base = name; - while (m_nameRepository.contains(name)) { + for (int id = 1; m_nameRepository.contains(name); ++id) { alreadyUsed = true; - name = base + QString::number(id++); + name = base + QString::number(id); } - } else if (className.size()) { + } else if (!className.isEmpty()) { name = unique(qtify(className)); } else { name = unique(QLatin1String("var")); } - if (alreadyUsed && className.size()) { + if (alreadyUsed && !className.isEmpty()) { fprintf(stderr, "%s: Warning: The name '%s' (%s) is already in use, defaulting to '%s'.\n", qPrintable(m_option.messagePrefix()), qPrintable(instanceName), qPrintable(className), @@ -181,17 +181,10 @@ QString Driver::qtify(const QString &name) QString qname = name; if (qname.at(0) == QLatin1Char('Q') || qname.at(0) == QLatin1Char('K')) - qname = qname.mid(1); + qname.remove(0, 1); - int i=0; - while (i < qname.length()) { - if (qname.at(i).toLower() != qname.at(i)) - qname[i] = qname.at(i).toLower(); - else - break; - - ++i; - } + for (int i = 0, size = qname.size(); i < size && qname.at(i).isUpper(); ++i) + qname[i] = qname.at(i).toLower(); return qname; } @@ -256,18 +249,11 @@ bool Driver::uic(const QString &fileName, DomUI *ui, QTextStream *out) m_output = out != 0 ? out : &m_stdout; Uic tool(this); - bool rtn = false; -#ifdef QT_UIC_CPP_GENERATOR - rtn = tool.write(ui); -#else - Q_UNUSED(ui); - fprintf(stderr, "uic: option to generate cpp code not compiled in [%s:%d]\n", - __FILE__, __LINE__); -#endif + const bool result = tool.write(ui); m_output = oldOutput; - return rtn; + return result; } bool Driver::uic(const QString &fileName, QTextStream *out) @@ -313,58 +299,19 @@ bool Driver::uic(const QString &fileName, QTextStream *out) return rtn; } -void Driver::reset() -{ - Q_ASSERT( m_output == 0 ); - - m_option = Option(); - m_output = 0; - m_problems.clear(); - - QStringList m_problems; - - m_widgets.clear(); - m_spacers.clear(); - m_layouts.clear(); - m_actionGroups.clear(); - m_actions.clear(); - m_nameRepository.clear(); - m_pixmaps.clear(); -} - -void Driver::insertPixmap(const QString &pixmap) -{ - m_pixmaps.insert(pixmap, true); -} - -bool Driver::containsPixmap(const QString &pixmap) const -{ - return m_pixmaps.contains(pixmap); -} - -DomWidget *Driver::widgetByName(const QString &name) const -{ - return m_widgets.key(name); -} - -DomSpacer *Driver::spacerByName(const QString &name) const -{ - return m_spacers.key(name); -} - -DomLayout *Driver::layoutByName(const QString &name) const +const DomWidget *Driver::widgetByName(const QString &name) const { - return m_layouts.key(name); + return findByAttributeName(m_widgets, name); } -DomActionGroup *Driver::actionGroupByName(const QString &name) const +const DomActionGroup *Driver::actionGroupByName(const QString &name) const { - return m_actionGroups.key(name); + return findByAttributeName(m_actionGroups, name); } -DomAction *Driver::actionByName(const QString &name) const +const DomAction *Driver::actionByName(const QString &name) const { - return m_actions.key(name); + return findByAttributeName(m_actions, name); } QT_END_NAMESPACE diff --git a/src/tools/uic/driver.h b/src/tools/uic/driver.h index 1563bdbd83..36336007b2 100644 --- a/src/tools/uic/driver.h +++ b/src/tools/uic/driver.h @@ -49,27 +49,20 @@ class DomButtonGroup; class Driver { - Q_DISABLE_COPY(Driver) + Q_DISABLE_COPY_MOVE(Driver) public: Driver(); virtual ~Driver(); // tools bool printDependencies(const QString &fileName); - bool uic(const QString &fileName, QTextStream *output = 0); - bool uic(const QString &fileName, DomUI *ui, QTextStream *output = 0); + bool uic(const QString &fileName, QTextStream *output = nullptr); + bool uic(const QString &fileName, DomUI *ui, QTextStream *output = nullptr); // configuration inline QTextStream &output() const { return *m_output; } inline Option &option() { return m_option; } - // initialization - void reset(); - - // error - inline QStringList problems() { return m_problems; } - inline void addProblem(const QString &problem) { m_problems.append(problem); } - // utils static QString headerFileName(const QString &fileName); QString headerFileName() const; @@ -80,50 +73,46 @@ public: const QString &className=QString()); // symbol table - QString findOrInsertWidget(DomWidget *ui_widget); - QString findOrInsertSpacer(DomSpacer *ui_spacer); - QString findOrInsertLayout(DomLayout *ui_layout); - QString findOrInsertLayoutItem(DomLayoutItem *ui_layoutItem); + QString findOrInsertWidget(const DomWidget *ui_widget); + QString findOrInsertSpacer(const DomSpacer *ui_spacer); + QString findOrInsertLayout(const DomLayout *ui_layout); + QString findOrInsertLayoutItem(const DomLayoutItem *ui_layoutItem); QString findOrInsertName(const QString &name); - QString findOrInsertActionGroup(DomActionGroup *ui_group); - QString findOrInsertAction(DomAction *ui_action); + QString findOrInsertActionGroup(const DomActionGroup *ui_group); + QString findOrInsertAction(const DomAction *ui_action); QString findOrInsertButtonGroup(const DomButtonGroup *ui_group); // Find a group by its non-uniqified name const DomButtonGroup *findButtonGroup(const QString &attributeName) const; - inline bool hasName(const QString &name) const - { return m_nameRepository.contains(name); } - - DomWidget *widgetByName(const QString &name) const; - DomSpacer *spacerByName(const QString &name) const; - DomLayout *layoutByName(const QString &name) const; - DomActionGroup *actionGroupByName(const QString &name) const; - DomAction *actionByName(const QString &name) const; - - // pixmap - void insertPixmap(const QString &pixmap); - bool containsPixmap(const QString &pixmap) const; + const DomWidget *widgetByName(const QString &name) const; + const DomActionGroup *actionGroupByName(const QString &name) const; + const DomAction *actionByName(const QString &name) const; bool useIdBasedTranslations() const { return m_idBasedTranslations; } void setUseIdBasedTranslations(bool u) { m_idBasedTranslations = u; } private: + template <class DomClass> using DomObjectHash = QHash<const DomClass *, QString>; + + template <class DomClass> + const DomClass *findByAttributeName(const DomObjectHash<DomClass> &domHash, + const QString &name) const; + template <class DomClass> + QString findOrInsert(DomObjectHash<DomClass> *domHash, const DomClass *dom, const QString &className, + bool isMember = true); + Option m_option; QTextStream m_stdout; QTextStream *m_output; - QStringList m_problems; - // symbol tables - QHash<DomWidget*, QString> m_widgets; - QHash<DomSpacer*, QString> m_spacers; - QHash<DomLayout*, QString> m_layouts; - QHash<DomActionGroup*, QString> m_actionGroups; - typedef QHash<const DomButtonGroup*, QString> ButtonGroupNameHash; - ButtonGroupNameHash m_buttonGroups; - QHash<DomAction*, QString> m_actions; + DomObjectHash<DomWidget> m_widgets; + DomObjectHash<DomSpacer> m_spacers; + DomObjectHash<DomLayout> m_layouts; + DomObjectHash<DomActionGroup> m_actionGroups; + DomObjectHash<DomButtonGroup> m_buttonGroups; + DomObjectHash<DomAction> m_actions; QHash<QString, bool> m_nameRepository; - QHash<QString, bool> m_pixmaps; bool m_idBasedTranslations = false; }; diff --git a/src/tools/uic/main.cpp b/src/tools/uic/main.cpp index 0e30bac28e..166fe78ff7 100644 --- a/src/tools/uic/main.cpp +++ b/src/tools/uic/main.cpp @@ -29,6 +29,7 @@ #include "uic.h" #include "option.h" #include "driver.h" +#include <language.h> #include <qfile.h> #include <qdir.h> @@ -96,7 +97,7 @@ int runUic(int argc, char *argv[]) QCommandLineOption generatorOption(QStringList() << QStringLiteral("g") << QStringLiteral("generator")); generatorOption.setDescription(QStringLiteral("Select generator.")); - generatorOption.setValueName(QStringLiteral("java|cpp")); + generatorOption.setValueName(QStringLiteral("python|cpp")); parser.addOption(generatorOption); QCommandLineOption idBasedOption(QStringLiteral("idbased")); @@ -115,7 +116,13 @@ int runUic(int argc, char *argv[]) driver.option().postfix = parser.value(postfixOption); driver.option().translateFunction = parser.value(translateOption); driver.option().includeFile = parser.value(includeOption); - driver.option().generator = (parser.value(generatorOption).toLower() == QLatin1String("java")) ? Option::JavaGenerator : Option::CppGenerator; + + Language language = Language::Cpp; + if (parser.isSet(generatorOption)) { + if (parser.value(generatorOption).compare(QLatin1String("python")) == 0) + language = Language::Python; + } + language::setLanguage(language); if (parser.isSet(noStringLiteralOption)) fprintf(stderr, "The -s, --no-stringliteral option is deprecated and it won't take any effect.\n"); @@ -132,14 +139,14 @@ int runUic(int argc, char *argv[]) QTextStream *out = 0; QFile f; - if (driver.option().outputFile.size()) { + if (!driver.option().outputFile.isEmpty()) { f.setFileName(driver.option().outputFile); if (!f.open(QIODevice::WriteOnly | QFile::Text)) { fprintf(stderr, "Could not create output file\n"); return 1; } out = new QTextStream(&f); -#ifndef QT_NO_TEXTCODEC +#if QT_CONFIG(textcodec) out->setCodec(QTextCodec::codecForName("UTF-8")); #endif } diff --git a/src/tools/uic/option.h b/src/tools/uic/option.h index a5b14abc5f..4fc442e94a 100644 --- a/src/tools/uic/option.h +++ b/src/tools/uic/option.h @@ -36,12 +36,6 @@ QT_BEGIN_NAMESPACE struct Option { - enum Generator - { - CppGenerator, - JavaGenerator - }; - unsigned int headerProtection : 1; unsigned int copyrightHeader : 1; unsigned int generateImplemetation : 1; @@ -51,7 +45,6 @@ struct Option unsigned int limitXPM_LineLength : 1; unsigned int implicitIncludes: 1; unsigned int idBased: 1; - Generator generator; QString inputFile; QString outputFile; @@ -61,10 +54,6 @@ struct Option QString postfix; QString translateFunction; QString includeFile; -#ifdef QT_UIC_JAVA_GENERATOR - QString javaPackage; - QString javaOutputDirectory; -#endif Option() : headerProtection(1), @@ -76,7 +65,6 @@ struct Option limitXPM_LineLength(0), implicitIncludes(1), idBased(0), - generator(CppGenerator), prefix(QLatin1String("Ui_")) { indent.fill(QLatin1Char(' '), 4); } diff --git a/src/tools/uic/python/python.pri b/src/tools/uic/python/python.pri new file mode 100644 index 0000000000..4dd31160a1 --- /dev/null +++ b/src/tools/uic/python/python.pri @@ -0,0 +1,8 @@ +INCLUDEPATH += $$PWD + +# Input +HEADERS += $$PWD/pythonwritedeclaration.h \ + $$PWD/pythonwriteimports.h + +SOURCES += $$PWD/pythonwritedeclaration.cpp \ + $$PWD/pythonwriteimports.cpp diff --git a/src/tools/uic/python/pythonwritedeclaration.cpp b/src/tools/uic/python/pythonwritedeclaration.cpp new file mode 100644 index 0000000000..5122b18e23 --- /dev/null +++ b/src/tools/uic/python/pythonwritedeclaration.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "pythonwritedeclaration.h" +#include <cppwriteinitialization.h> +#include <language.h> +#include <driver.h> +#include <ui4.h> +#include <uic.h> + +#include <QtCore/qtextstream.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace Python { + +WriteDeclaration::WriteDeclaration(Uic *uic) : + m_uic(uic), + m_driver(uic->driver()), + m_output(uic->output()), + m_option(uic->option()) +{ +} + +void WriteDeclaration::acceptUI(DomUI *node) +{ + // remove any left-over C++ namespaces + const QString qualifiedClassName = QLatin1String("Ui_") + node->elementClass() + + m_option.postfix; + m_output << "class " << language::fixClassName(qualifiedClassName) << "(object):\n"; + + TreeWalker::acceptWidget(node->elementWidget()); + if (const DomButtonGroups *domButtonGroups = node->elementButtonGroups()) + acceptButtonGroups(domButtonGroups); + CPP::WriteInitialization(m_uic).acceptUI(node); +} + +// Register button groups to prevent the on-the-fly creation legacy +// feature from triggering +void WriteDeclaration::acceptButtonGroup(const DomButtonGroup *buttonGroup) +{ + m_driver->findOrInsertButtonGroup(buttonGroup); +} + +} // namespace Python + +QT_END_NAMESPACE diff --git a/src/tools/uic/python/pythonwritedeclaration.h b/src/tools/uic/python/pythonwritedeclaration.h new file mode 100644 index 0000000000..a8d50b6fbf --- /dev/null +++ b/src/tools/uic/python/pythonwritedeclaration.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ + +#ifndef PYTHONWRITEDECLARATION_H +#define PYTHONWRITEDECLARATION_H + +#include <treewalker.h> + +QT_BEGIN_NAMESPACE + +class QTextStream; +class Driver; +class Uic; + +struct Option; + +namespace Python { + +struct WriteDeclaration : public TreeWalker +{ + WriteDeclaration(Uic *uic); + + void acceptUI(DomUI *node) override; + void acceptButtonGroup(const DomButtonGroup *buttonGroup) override; + +private: + Uic *m_uic; + Driver *m_driver; + QTextStream &m_output; + const Option &m_option; +}; + +} // namespace Python + +QT_END_NAMESPACE + +#endif // PYTHONWRITEDECLARATION_H diff --git a/src/tools/uic/python/pythonwriteimports.cpp b/src/tools/uic/python/pythonwriteimports.cpp new file mode 100644 index 0000000000..8e11981f37 --- /dev/null +++ b/src/tools/uic/python/pythonwriteimports.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "pythonwriteimports.h" + +#include <customwidgetsinfo.h> +#include <uic.h> + +#include <ui4.h> + +#include <QtCore/qtextstream.h> + +QT_BEGIN_NAMESPACE + +static const char *standardImports = +R"I(from PySide2.QtCore import (QCoreApplication, QMetaObject, QObject, QPoint, + QRect, QSize, QUrl, Qt) +from PySide2.QtGui import (QColor, QFont, QIcon, QPixmap) +from PySide2.QtWidgets import * +)I"; + +namespace Python { + +WriteImports::WriteImports(Uic *uic) : m_uic(uic) +{ +} + +void WriteImports::acceptUI(DomUI *node) +{ + auto &output = m_uic->output(); + output << standardImports << '\n'; + if (auto customWidgets = node->elementCustomWidgets()) { + TreeWalker::acceptCustomWidgets(customWidgets); + output << '\n'; + } +} + +QString WriteImports::qtModuleOf(const DomCustomWidget *node) const +{ + if (m_uic->customWidgetsInfo()->extends(node->elementClass(), QLatin1String("QAxWidget"))) + return QStringLiteral("QtAxContainer"); + if (const auto headerElement = node->elementHeader()) { + const auto &header = headerElement->text(); + if (header.startsWith(QLatin1String("Qt"))) { + const int slash = header.indexOf(QLatin1Char('/')); + if (slash != -1) + return header.left(slash); + } + } + return QString(); +} + +void WriteImports::acceptCustomWidget(DomCustomWidget *node) +{ + const auto &className = node->elementClass(); + if (className.contains(QLatin1String("::"))) + return; // Exclude namespaced names (just to make tests pass). + const QString &qtModule = qtModuleOf(node); + auto &output = m_uic->output(); + if (!qtModule.isEmpty()) + output << "from PySide2." << qtModule << ' '; + output << "import " << className << '\n'; +} + +} // namespace Python + +QT_END_NAMESPACE diff --git a/src/tools/uic/globaldefs.h b/src/tools/uic/python/pythonwriteimports.h index 5ad193c29a..427cbb48b1 100644 --- a/src/tools/uic/globaldefs.h +++ b/src/tools/uic/python/pythonwriteimports.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the tools applications of the Qt Toolkit. @@ -26,16 +26,33 @@ ** ****************************************************************************/ -#ifndef GLOBALDEFS_H -#define GLOBALDEFS_H +#ifndef PYTHONWRITEIMPORTS_H +#define PYTHONWRITEIMPORTS_H -#include <qglobal.h> +#include <treewalker.h> QT_BEGIN_NAMESPACE -enum { BOXLAYOUT_DEFAULT_MARGIN = 11 }; -enum { BOXLAYOUT_DEFAULT_SPACING = 6 }; +class Uic; + +namespace Python { + +struct WriteImports : public TreeWalker +{ +public: + explicit WriteImports(Uic *uic); + + void acceptUI(DomUI *node) override; + void acceptCustomWidget(DomCustomWidget *node) override; + +private: + QString qtModuleOf(const DomCustomWidget *node) const; + + Uic *const m_uic; +}; + +} // namespace Python QT_END_NAMESPACE -#endif // GLOBALDEFS_H +#endif // PYTHONWRITEIMPORTS_H diff --git a/src/tools/uic/shared/language.cpp b/src/tools/uic/shared/language.cpp new file mode 100644 index 0000000000..235a8ed2fc --- /dev/null +++ b/src/tools/uic/shared/language.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "language.h" + +#include <QtCore/qtextstream.h> + +namespace language { + +static Encoding encoding = Encoding::Utf8; +static Language _language = Language::Cpp; + +Language language() { return _language; } + +void setLanguage(Language l) +{ + _language = l; + switch (_language) { + case Language::Cpp: + derefPointer = QLatin1String("->"); + nullPtr = QLatin1String("nullptr"); + operatorNew = QLatin1String("new "); + qtQualifier = QLatin1String("Qt::"); + qualifier = QLatin1String("::"); + self = QLatin1String(""); // for testing: change to "this->"; + eol = QLatin1String(";\n"); + encoding = Encoding::Utf8; + break; + case Language::Python: + derefPointer = QLatin1String("."); + nullPtr = QLatin1String("None"); + operatorNew = QLatin1String(""); + qtQualifier = QLatin1String("Qt."); + qualifier = QLatin1String("."); + self = QLatin1String("self."); + eol = QLatin1String("\n"); + encoding = Encoding::Unicode; + break; + } +} + +QString derefPointer; +QString nullPtr; +QString operatorNew; +QString qtQualifier; +QString qualifier; +QString self; +QString eol; + +QString cppQualifier = QLatin1String("::"); +QString cppTrue = QLatin1String("true"); +QString cppFalse = QLatin1String("false"); + +QTextStream &operator<<(QTextStream &str, const qtConfig &c) +{ + str << "QT_CONFIG(" << c.parameter() << ')'; + return str; +} + +QTextStream &operator<<(QTextStream &str, const openQtConfig &c) +{ + str << "#if " << qtConfig(c.parameter()) << '\n'; + return str; +} + +QTextStream &operator<<(QTextStream &str, const closeQtConfig &c) +{ + str << "#endif // " << qtConfig(c.parameter()) << '\n'; + return str; +} + +struct EnumLookup +{ + int value; + const char *valueString; +}; + +template <int N> +const char *lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex = 0) +{ + for (int i = 0; i < N; ++i) { + if (value == array[i].value) + return array[i].valueString; + } + const char *defaultValue = array[defaultIndex].valueString; + qWarning("uic: Warning: Invalid enumeration value %d, defaulting to %s", + value, defaultValue); + return defaultValue; +} + +QString fixClassName(QString className) +{ + if (language() == Language::Python) + className.replace(cppQualifier, QLatin1String("_")); + return className; +} + +const char *toolbarArea(int v) +{ + static const EnumLookup toolBarAreas[] = + { + {0, "NoToolBarArea"}, + {0x1, "LeftToolBarArea"}, + {0x2, "RightToolBarArea"}, + {0x4, "TopToolBarArea"}, + {0x8, "BottomToolBarArea"}, + {0xf, "AllToolBarAreas"} + }; + return lookupEnum(toolBarAreas, v); +} + +const char *sizePolicy(int v) +{ + static const EnumLookup sizePolicies[] = + { + {0, "Fixed"}, + {0x1, "Minimum"}, + {0x4, "Maximum"}, + {0x5, "Preferred"}, + {0x3, "MinimumExpanding"}, + {0x7, "Expanding"}, + {0xD, "Ignored"} + }; + return lookupEnum(sizePolicies, v, 3); +} + +const char *dockWidgetArea(int v) +{ + static const EnumLookup dockWidgetAreas[] = + { + {0, "NoDockWidgetArea"}, + {0x1, "LeftDockWidgetArea"}, + {0x2, "RightDockWidgetArea"}, + {0x4, "TopDockWidgetArea"}, + {0x8, "BottomDockWidgetArea"}, + {0xf, "AllDockWidgetAreas"} + }; + return lookupEnum(dockWidgetAreas, v); +} + +const char *paletteColorRole(int v) +{ + static const EnumLookup colorRoles[] = + { + {0, "WindowText"}, + {1, "Button"}, + {2, "Light"}, + {3, "Midlight"}, + {4, "Dark"}, + {5, "Mid"}, + {6, "Text"}, + {7, "BrightText"}, + {8, "ButtonText"}, + {9, "Base"}, + {10, "Window"}, + {11, "Shadow"}, + {12, "Highlight"}, + {13, "HighlightedText"}, + {14, "Link"}, + {15, "LinkVisited"}, + {16, "AlternateBase"}, + {17, "NoRole"}, + {18, "ToolTipBase"}, + {19, "ToolTipText"}, + {20, "PlaceholderText"}, + }; + return lookupEnum(colorRoles, v); +} + +// Helpers for formatting a character sequences + +// Format a special character like '\x0a' +static int formatEscapedNumber(QTextStream &str, ushort value, int base, int width, + char prefix = 0) +{ + int length = 1 + width; + str << '\\'; + if (prefix) { + str << prefix; + ++length; + } + const auto oldPadChar = str.padChar(); + const auto oldFieldWidth = str.fieldWidth(); + const auto oldFieldAlignment = str.fieldAlignment(); + const auto oldIntegerBase = str.integerBase(); + str.setPadChar(QLatin1Char('0')); + str.setFieldWidth(width); + str.setFieldAlignment(QTextStream::AlignRight); + str.setIntegerBase(base); + str << value; + str.setIntegerBase(oldIntegerBase); + str.setFieldAlignment(oldFieldAlignment); + str.setFieldWidth(oldFieldWidth); + str.setPadChar(oldPadChar); + return length; +} + +static int formatSpecialCharacter(QTextStream &str, ushort value) +{ + int length = 0; + switch (value) { + case '\\': + str << "\\\\"; + length += 2; + break; + case '\"': + str << "\\\""; + length += 2; + break; + case '\n': + str << "\\n\"\n\""; + length += 5; + break; + default: + break; + } + return length; +} + +// Format a sequence of characters for C++ with special characters numerically +// escaped (non-raw string literals), wrappped at maxSegmentSize. FormattingTraits +// are used to transform characters into (unsigned) codes, which can be used +// for either normal escapes or Unicode code points as used in Unicode literals. + +enum : int { maxSegmentSize = 1024 }; + +template <Encoding e> +struct FormattingTraits +{ +}; + +template <> +struct FormattingTraits<Encoding::Utf8> +{ + static ushort code(char c) { return uchar(c); } +}; + +template <> +struct FormattingTraits<Encoding::Unicode> +{ + static ushort code(QChar c) { return c.unicode(); } +}; + +template <Encoding e, class Iterator> +static void formatStringSequence(QTextStream &str, Iterator it, Iterator end, + const QString &indent, + int escapeIntegerBase, int escapeWidth, + char escapePrefix = 0) +{ + str << '"'; + int length = 0; + while (it != end) { + const auto code = FormattingTraits<e>::code(*it); + if (code >= 0x80) { + length += formatEscapedNumber(str, code, escapeIntegerBase, escapeWidth, escapePrefix); + } else if (const int l = formatSpecialCharacter(str, code)) { + length += l; + } else if (code != '\r') { + str << *it; + ++length; + } + ++it; + if (it != end && length > maxSegmentSize) { + str << "\"\n" << indent << indent << '"'; + length = 0; + } + } + str << '"'; +} + +void _formatString(QTextStream &str, const QString &value, const QString &indent, + bool qString) +{ + switch (encoding) { + // Special characters as 3 digit octal escapes (u8"\303\234mlaut") + case Encoding::Utf8: { + if (qString && _language == Language::Cpp) + str << "QString::fromUtf8("; + const QByteArray utf8 = value.toUtf8(); + formatStringSequence<Encoding::Utf8>(str, utf8.cbegin(), utf8.cend(), indent, + 8, 3); + if (qString && _language == Language::Cpp) + str << ')'; + } + break; + // Special characters as 4 digit hex Unicode points (u8"\u00dcmlaut") + case Encoding::Unicode: + str << 'u'; // Python Unicode literal (would be UTF-16 in C++) + formatStringSequence<Encoding::Unicode>(str, value.cbegin(), value.cend(), indent, + 16, 4, 'u'); + break; + } +} + +QTextStream &operator<<(QTextStream &str, const repeat &r) +{ + for (int i = 0; i < r.m_count; ++i) + str << r.m_char; + return str; +} + +startFunctionDefinition1::startFunctionDefinition1(const char *name, const QString ¶meterType, + const QString ¶meterName, + const QString &indent, + const char *returnType) : + m_name(name), m_parameterType(parameterType), m_parameterName(parameterName), + m_indent(indent), m_return(returnType) +{ +} + +QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f) +{ + switch (language()) { + case Language::Cpp: + str << (f.m_return ? f.m_return : "void") << ' ' << f.m_name << '(' + << f.m_parameterType; + if (f.m_parameterType.cend()->isLetter()) + str << ' '; + str << f.m_parameterName << ')' << '\n' << f.m_indent << "{\n"; + break; + case Language::Python: + str << "def " << f.m_name << "(self, " << f.m_parameterName << "):\n"; + break; + } + return str; +} + +endFunctionDefinition::endFunctionDefinition(const char *name) : m_name(name) +{ +} + +QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f) +{ + switch (language()) { + case Language::Cpp: + str << "} // " << f.m_name << "\n\n"; + break; + case Language::Python: + str << "# " << f.m_name << "\n\n"; + break; + } + return str; +} + +void _formatStackVariable(QTextStream &str, const char *className, QStringView varName, + bool withInitParameters) +{ + switch (language()) { + case Language::Cpp: + str << className << ' ' << varName; + if (withInitParameters) + str << '('; + break; + case Language::Python: + str << varName << " = " << className << '('; + if (!withInitParameters) + str << ')'; + break; + } +} + +void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSlot &receiver) +{ + switch (language()) { + case Language::Cpp: + str << "QObject::connect(" << sender.name << ", SIGNAL("<< sender.signature + << "), " << receiver.name << ", SLOT("<< receiver.signature << "))"; + break; + case Language::Python: + str << sender.name << '.' + << sender.signature.leftRef(sender.signature.indexOf(QLatin1Char('('))) + << ".connect(" << receiver.name << '.' + << receiver.signature.leftRef(receiver.signature.indexOf(QLatin1Char('('))) + << ')'; + break; + } +} + +QString boolValue(bool v) +{ + switch (language()) { + case Language::Cpp: + return v ? cppTrue : cppFalse; + case Language::Python: + return v ? QStringLiteral("True") : QStringLiteral("False"); + } + Q_UNREACHABLE(); +} + +static inline QString dot() { return QStringLiteral("."); } + +QString enumValue(const QString &value) +{ + if (language() == Language::Cpp || !value.contains(cppQualifier)) + return value; + QString fixed = value; + fixed.replace(cppQualifier, dot()); + return fixed; +} + +} // namespace language diff --git a/src/tools/uic/shared/language.h b/src/tools/uic/shared/language.h new file mode 100644 index 0000000000..fc8af9715b --- /dev/null +++ b/src/tools/uic/shared/language.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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$ +** +****************************************************************************/ + +#ifndef LANGUAGE_H +#define LANGUAGE_H + +#include <QtCore/qstring.h> +#include <QtCore/qstringview.h> +#include <QtCore/qstring.h> + +QT_FORWARD_DECLARE_CLASS(QTextStream) + +enum class Language { Cpp, Python }; + +namespace language { + +Language language(); +void setLanguage(Language); + +extern QString derefPointer; +extern QString nullPtr; +extern QString operatorNew; +extern QString qtQualifier; +extern QString qualifier; +extern QString self; +extern QString eol; + +extern QString cppQualifier; +extern QString cppTrue; +extern QString cppFalse; + +// Base class for streamable objects with one QStringView parameter +class StringViewStreamable +{ +public: + StringViewStreamable(QStringView parameter) : m_parameter(parameter) {} + + QStringView parameter() const { return m_parameter; } + +private: + QStringView m_parameter; +}; + +class qtConfig : public StringViewStreamable +{ +public: + qtConfig(QStringView name) : StringViewStreamable(name) {} +}; + +QTextStream &operator<<(QTextStream &str, const qtConfig &c); + +class openQtConfig : public StringViewStreamable +{ +public: + openQtConfig(QStringView name) : StringViewStreamable(name) {} +}; + +QTextStream &operator<<(QTextStream &str, const openQtConfig &c); + +class closeQtConfig : public StringViewStreamable +{ +public: + closeQtConfig(QStringView name) : StringViewStreamable(name) {} +}; + +QTextStream &operator<<(QTextStream &, const closeQtConfig &c); + +QString fixClassName(QString className); + +const char *toolbarArea(int v); +const char *sizePolicy(int v); +const char *dockWidgetArea(int v); +const char *paletteColorRole(int v); + +enum class Encoding { Utf8, Unicode }; + +void _formatString(QTextStream &str, const QString &value, const QString &indent, + bool qString); + +template <bool AsQString> +class _string +{ +public: + explicit _string(const QString &value, const QString &indent = QString()) + : m_value(value), m_indent(indent) {} + + void format(QTextStream &str) const + { _formatString(str, m_value, m_indent, AsQString); } + +private: + const QString &m_value; + const QString &m_indent; +}; + +template <bool AsQString> +inline QTextStream &operator<<(QTextStream &str, const language::_string<AsQString> &s) +{ + s.format(str); + return str; +} + +using charliteral = _string<false>; +using qstring = _string<true>; + +class repeat { +public: + explicit repeat(int count, char c) : m_count(count), m_char(c) {} + + friend QTextStream &operator<<(QTextStream &str, const repeat &r); + +private: + const int m_count; + const char m_char; +}; + +class startFunctionDefinition1 { +public: + explicit startFunctionDefinition1(const char *name, const QString ¶meterType, + const QString ¶meterName, + const QString &indent, + const char *returnType = nullptr); + + friend QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f); +private: + const char *m_name; + const QString &m_parameterType; + const QString &m_parameterName; + const QString &m_indent; + const char *m_return; +}; + +class endFunctionDefinition { +public: + explicit endFunctionDefinition(const char *name); + + friend QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f); +private: + const char *m_name; +}; + +void _formatStackVariable(QTextStream &str, const char *className, QStringView varName, bool withInitParameters); + +template <bool withInitParameters> +class _stackVariable { +public: + explicit _stackVariable(const char *className, QStringView varName) : + m_className(className), m_varName(varName) {} + + void format(QTextStream &str) const + { _formatStackVariable(str, m_className, m_varName, withInitParameters); } + +private: + const char *m_className; + QStringView m_varName; + QStringView m_parameters; +}; + +template <bool withInitParameters> +inline QTextStream &operator<<(QTextStream &str, const _stackVariable<withInitParameters> &s) +{ + s.format(str); + return str; +} + +using stackVariable = _stackVariable<false>; +using stackVariableWithInitParameters = _stackVariable<true>; + +struct SignalSlot +{ + QString name; + QString signature; + QString className; +}; + +void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSlot &receiver); + +QString boolValue(bool v); + +QString enumValue(const QString &value); + +} // namespace language + +#endif // LANGUAGE_H diff --git a/src/tools/uic/shared/shared.pri b/src/tools/uic/shared/shared.pri new file mode 100644 index 0000000000..dce2af8bf1 --- /dev/null +++ b/src/tools/uic/shared/shared.pri @@ -0,0 +1,5 @@ +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/language.h + +SOURCES += $$PWD/language.cpp diff --git a/src/tools/uic/treewalker.h b/src/tools/uic/treewalker.h index 43d4633d83..7e8eda57d9 100644 --- a/src/tools/uic/treewalker.h +++ b/src/tools/uic/treewalker.h @@ -101,7 +101,6 @@ struct TreeWalker virtual void acceptTime(DomTime *time); virtual void acceptDateTime(DomDateTime *dateTime); virtual void acceptProperty(DomProperty *property); - typedef QVector<DomScript *> DomScripts; typedef QVector<DomWidget *> DomWidgets; virtual void acceptIncludes(DomIncludes *includes); virtual void acceptInclude(DomInclude *incl); diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp index 957a50b1d5..b95f1a784b 100644 --- a/src/tools/uic/uic.cpp +++ b/src/tools/uic/uic.cpp @@ -33,18 +33,16 @@ #include "treewalker.h" #include "validator.h" -#ifdef QT_UIC_CPP_GENERATOR #include "cppwriteincludes.h" #include "cppwritedeclaration.h" -#endif +#include <pythonwritedeclaration.h> +#include <pythonwriteimports.h> -#ifdef QT_UIC_JAVA_GENERATOR -#include "javawriteincludes.h" -#include "javawritedeclaration.h" -#endif +#include <language.h> #include <qxmlstream.h> #include <qfileinfo.h> +#include <qscopedpointer.h> #include <qtextstream.h> QT_BEGIN_NAMESPACE @@ -109,21 +107,63 @@ bool Uic::printDependencies() return true; } -void Uic::writeCopyrightHeader(DomUI *ui) +void Uic::writeCopyrightHeaderCpp(const DomUI *ui) const { QString comment = ui->elementComment(); - if (comment.size()) + 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 " << QLatin1String(QT_VERSION_STR) << "\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) @@ -172,128 +212,92 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader) bool Uic::write(QIODevice *in) { - if (option().generator == Option::JavaGenerator) { - // the Java generator ignores header protection - opt.headerProtection = false; - } - - DomUI *ui = 0; + QScopedPointer<DomUI> ui; { QXmlStreamReader reader; reader.setDevice(in); - ui = parseUiFile(reader); - - if (!ui) - return false; + ui.reset(parseUiFile(reader)); } + if (ui.isNull()) + return false; + double version = ui->attributeVersion().toDouble(); if (version < 4.0) { - delete ui; - fprintf(stderr, "uic: File generated with too old version of Qt Designer\n"); return false; } - QString language = ui->attributeLanguage(); + const QString &language = ui->attributeLanguage(); driver()->setUseIdBasedTranslations(ui->attributeIdbasedtr()); - bool rtn = false; - - if (option().generator == Option::JavaGenerator) { -#ifdef QT_UIC_JAVA_GENERATOR - if (language.toLower() != QLatin1String("jambi")) { - fprintf(stderr, "uic: File is not a 'jambi' form\n"); - delete ui; - return false; - } - rtn = jwrite (ui); -#else - fprintf(stderr, "uic: option to generate java code not compiled in\n"); -#endif - } else { -#ifdef QT_UIC_CPP_GENERATOR - if (!language.isEmpty() && language.toLower() != QLatin1String("c++")) { - fprintf(stderr, "uic: File is not a 'c++' ui file, language=%s\n", qPrintable(language)); - delete ui; - return false; - } - - rtn = write (ui); -#else - fprintf(stderr, "uic: option to generate cpp code not compiled in\n"); -#endif + 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; } - delete ui; - - return rtn; + return write(ui.data()); } -#ifdef QT_UIC_CPP_GENERATOR bool Uic::write(DomUI *ui) { - using namespace CPP; - if (!ui || !ui->elementWidget()) return false; - if (opt.copyrightHeader) - writeCopyrightHeader(ui); + const auto lang = language::language(); + + if (lang == Language::Python) + out << "# -*- coding: utf-8 -*-\n\n"; - if (opt.headerProtection) { + 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"); + 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); - WriteIncludes writeIncludes(this); - writeIncludes.acceptUI(ui); - Validator(this).acceptUI(ui); - WriteDeclaration(this).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) + if (opt.headerProtection && lang == Language::Cpp) writeHeaderProtectionEnd(); return true; } -#endif - -#ifdef QT_UIC_JAVA_GENERATOR -bool Uic::jwrite(DomUI *ui) -{ - using namespace Java; - - if (!ui || !ui->elementWidget()) - return false; - - if (opt.copyrightHeader) - writeCopyrightHeader(ui); - - pixFunction = ui->elementPixmapFunction(); - if (pixFunction == QLatin1String("QPixmap::fromMimeSource")) - pixFunction = QLatin1String("qPixmapFromMimeSource"); - - externalPix = ui->elementImages() == 0; - - info.acceptUI(ui); - cWidgetsInfo.acceptUI(ui); - WriteIncludes(this).acceptUI(ui); - - Validator(this).acceptUI(ui); - WriteDeclaration(this).acceptUI(ui); - - return true; -} -#endif - -#ifdef QT_UIC_CPP_GENERATOR void Uic::writeHeaderProtectionStart() { @@ -307,57 +311,35 @@ void Uic::writeHeaderProtectionEnd() QString h = drv->headerFileName(); out << "#endif // " << h << "\n"; } -#endif - -bool Uic::isMainWindow(const QString &className) const -{ - return customWidgetsInfo()->extends(className, QLatin1String("QMainWindow")); -} - -bool Uic::isToolBar(const QString &className) const -{ - return customWidgetsInfo()->extends(className, QLatin1String("QToolBar")); -} bool Uic::isButton(const QString &className) const { - return customWidgetsInfo()->extends(className, QLatin1String("QRadioButton")) - || customWidgetsInfo()->extends(className, QLatin1String("QToolButton")) - || customWidgetsInfo()->extends(className, QLatin1String("QCheckBox")) - || customWidgetsInfo()->extends(className, QLatin1String("QPushButton")) - || customWidgetsInfo()->extends(className, QLatin1String("QCommandLinkButton")); + 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 { - return customWidgetsInfo()->extends(className, QLatin1String("QStackedWidget")) - || customWidgetsInfo()->extends(className, QLatin1String("QToolBox")) - || customWidgetsInfo()->extends(className, QLatin1String("QTabWidget")) - || customWidgetsInfo()->extends(className, QLatin1String("QScrollArea")) - || customWidgetsInfo()->extends(className, QLatin1String("QMdiArea")) - || customWidgetsInfo()->extends(className, QLatin1String("QWizard")) - || customWidgetsInfo()->extends(className, QLatin1String("QDockWidget")); -} - -bool Uic::isCustomWidgetContainer(const QString &className) const -{ - return customWidgetsInfo()->isCustomWidgetContainer(className); -} - -bool Uic::isStatusBar(const QString &className) const -{ - return customWidgetsInfo()->extends(className, QLatin1String("QStatusBar")); -} - -bool Uic::isMenuBar(const QString &className) const -{ - return customWidgetsInfo()->extends(className, QLatin1String("QMenuBar")); + 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 { - return customWidgetsInfo()->extends(className, QLatin1String("QMenu")) - || customWidgetsInfo()->extends(className, QLatin1String("QPopupMenu")); + static const QStringList menus = { + QLatin1String("QMenu"), QLatin1String("QPopupMenu") + }; + return customWidgetsInfo()->extendsOneOf(className, menus); } QT_END_NAMESPACE diff --git a/src/tools/uic/uic.h b/src/tools/uic/uic.h index 1c229bc516..f3dfd49149 100644 --- a/src/tools/uic/uic.h +++ b/src/tools/uic/uic.h @@ -53,7 +53,7 @@ struct Option; class Uic { - Q_DISABLE_COPY(Uic) + Q_DISABLE_COPY_MOVE(Uic) public: Uic(Driver *driver); ~Uic(); @@ -83,33 +83,21 @@ public: bool write(QIODevice *in); -#ifdef QT_UIC_JAVA_GENERATOR - bool jwrite(DomUI *ui); -#endif - -#ifdef QT_UIC_CPP_GENERATOR bool write(DomUI *ui); -#endif - bool isMainWindow(const QString &className) const; - bool isToolBar(const QString &className) const; - bool isStatusBar(const QString &className) const; bool isButton(const QString &className) const; bool isContainer(const QString &className) const; - bool isCustomWidgetContainer(const QString &className) const; - bool isMenuBar(const QString &className) const; bool isMenu(const QString &className) const; private: // copyright header - void writeCopyrightHeader(DomUI *ui); + void writeCopyrightHeaderCpp(const DomUI *ui) const; + void writeCopyrightHeaderPython(const DomUI *ui) const; DomUI *parseUiFile(QXmlStreamReader &reader); -#ifdef QT_UIC_CPP_GENERATOR // header protection void writeHeaderProtectionStart(); void writeHeaderProtectionEnd(); -#endif private: Driver *drv; diff --git a/src/tools/uic/uic.pri b/src/tools/uic/uic.pri index 3f0bab05dd..1c9098dcf9 100644 --- a/src/tools/uic/uic.pri +++ b/src/tools/uic/uic.pri @@ -5,7 +5,6 @@ HEADERS += \ $$PWD/customwidgetsinfo.h \ $$PWD/databaseinfo.h \ $$PWD/driver.h \ - $$PWD/globaldefs.h \ $$PWD/option.h \ $$PWD/treewalker.h \ $$PWD/utils.h \ diff --git a/src/tools/uic/uic.pro b/src/tools/uic/uic.pro index 18511395d9..1bf8ef8ad3 100644 --- a/src/tools/uic/uic.pro +++ b/src/tools/uic/uic.pro @@ -5,7 +5,9 @@ option(host_build) DEFINES += QT_UIC QT_NO_CAST_FROM_ASCII QT_NO_FOREACH include(uic.pri) +include(shared/shared.pri) include(cpp/cpp.pri) +include(python/python.pri) HEADERS += uic.h diff --git a/src/tools/uic/utils.h b/src/tools/uic/utils.h index 3f32a532ca..34c4ab23d4 100644 --- a/src/tools/uic/utils.h +++ b/src/tools/uic/utils.h @@ -42,55 +42,6 @@ inline bool toBool(const QString &str) inline QString toString(const DomString *str) { return str ? str->text() : QString(); } -inline QString fixString(const QString &str, const QString &indent) -{ - QString cursegment; - QStringList result; - const QByteArray utf8 = str.toUtf8(); - const int utf8Length = utf8.length(); - - for (int i = 0; i < utf8Length; ++i) { - const uchar cbyte = utf8.at(i); - if (cbyte >= 0x80) { - cursegment += QLatin1Char('\\'); - cursegment += QString::number(cbyte, 8); - } else { - switch(cbyte) { - case '\\': - cursegment += QLatin1String("\\\\"); break; - case '\"': - cursegment += QLatin1String("\\\""); break; - case '\r': - break; - case '\n': - cursegment += QLatin1String("\\n\"\n\""); break; - default: - cursegment += QLatin1Char(cbyte); - } - } - - if (cursegment.length() > 1024) { - result << cursegment; - cursegment.clear(); - } - } - - if (!cursegment.isEmpty()) - result << cursegment; - - - QString joinstr = QLatin1String("\"\n"); - joinstr += indent; - joinstr += indent; - joinstr += QLatin1Char('"'); - - QString rc(QLatin1Char('"')); - rc += result.join(joinstr); - rc += QLatin1Char('"'); - - return rc; -} - inline QHash<QString, DomProperty *> propertyMap(const QList<DomProperty *> &properties) { QHash<QString, DomProperty *> map; |