diff options
Diffstat (limited to 'src/libs/installer/component.cpp')
-rw-r--r-- | src/libs/installer/component.cpp | 339 |
1 files changed, 198 insertions, 141 deletions
diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index c7dfa65da..ce76a2927 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -1,6 +1,6 @@ /************************************************************************** ** -** Copyright (C) 2022 The Qt Company Ltd. +** Copyright (C) 2023 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Installer Framework. @@ -37,15 +37,18 @@ #include "remoteclient.h" #include "settings.h" #include "utils.h" +#include "constants.h" #include "updateoperationfactory.h" #include <productkeycheck.h> #include <QtCore/QDirIterator> -#include <QtCore/QRegExp> #include <QtCore/QTranslator> #include <QtCore/QRegularExpression> +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include <QtCore/QTextCodec> +#endif #include <QApplication> #include <QtConcurrentFilter> @@ -61,20 +64,14 @@ #include <private/qv4object_p.h> #include <algorithm> +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) +#include <QJSEngine> +#else +#include <QQmlEngine> +#endif using namespace QInstaller; -static const QLatin1String scScriptTag("Script"); -static const QLatin1String scVirtual("Virtual"); -static const QLatin1String scInstalled("Installed"); -static const QLatin1String scUpdateText("UpdateText"); -static const QLatin1String scUninstalled("Uninstalled"); -static const QLatin1String scCurrentState("CurrentState"); -static const QLatin1String scForcedInstallation("ForcedInstallation"); -static const QLatin1String scCheckable("Checkable"); -static const QLatin1String scExpandedByDefault("ExpandedByDefault"); -static const QLatin1String scUnstable("Unstable"); - /*! \enum QInstaller::Component::UnstableError @@ -90,6 +87,8 @@ static const QLatin1String scUnstable("Unstable"); Component has dependencies to missing components. \value InvalidTreeName Component has an invalid tree name. + \value DescendantOfUnstable + Component is descendant of an unstable component. */ /*! @@ -251,8 +250,13 @@ static const QLatin1String scUnstable("Unstable"); */ Component::Component(PackageManagerCore *core) : d(new ComponentPrivate(core, this)) - , m_defaultArchivePath(QLatin1String("@TargetDir@")) + , m_defaultArchivePath(scTargetDirPlaceholder) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + QJSEngine::setObjectOwnership(this, QJSEngine::CppOwnership); +#else + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +#endif setPrivate(d); connect(this, &Component::valueChanged, this, &Component::updateModelData); @@ -292,11 +296,11 @@ void Component::loadDataFromPackage(const KDUpdater::LocalPackage &package) setValue(scVersion, package.version); setValue(scInheritVersion, package.inheritVersionFrom); setValue(scInstalledVersion, package.version); - setValue(QLatin1String("LastUpdateDate"), package.lastUpdateDate.toString()); - setValue(QLatin1String("InstallDate"), package.installDate.toString()); + setValue(scLastUpdateDate, package.lastUpdateDate.toString()); + setValue(scInstallDate, package.installDate.toString()); setValue(scUncompressedSize, QString::number(package.uncompressedSize)); - setValue(scDependencies, package.dependencies.join(QLatin1String(","))); - setValue(scAutoDependOn, package.autoDependencies.join(QLatin1String(","))); + setValue(scDependencies, package.dependencies.join(scCommaWithSpace)); + setValue(scAutoDependOn, package.autoDependencies.join(scCommaWithSpace)); setValue(scSortingPriority, QString::number(package.sortingPriority)); setValue(scForcedInstallation, package.forcedInstallation ? scTrue : scFalse); @@ -343,8 +347,7 @@ void Component::loadDataFromPackage(const Package &package) setValue(scUpdateText, package.data(scUpdateText).toString()); setValue(scNewComponent, package.data(scNewComponent).toString()); setValue(scRequiresAdminRights, package.data(scRequiresAdminRights).toString()); - - setValue(scScriptTag, package.data(scScriptTag).toString()); + d->m_scriptHash = package.data(scScriptTag).toHash(); setValue(scReplaces, package.data(scReplaces).toString()); setValue(scReleaseDate, package.data(scReleaseDate).toString()); setValue(scCheckable, package.data(scCheckable).toString()); @@ -355,8 +358,9 @@ void Component::loadDataFromPackage(const Package &package) forced = scFalse; setValue(scForcedInstallation, forced); setValue(scContentSha1, package.data(scContentSha1).toString()); + setValue(scCheckSha1CheckSum, package.data(scCheckSha1CheckSum, scTrue).toString().toLower()); - const auto treeNamePair = package.data(QLatin1String(scTreeName)).value<QPair<QString, bool>>(); + const auto treeNamePair = package.data(scTreeName).value<QPair<QString, bool>>(); setValue(scTreeName, treeNamePair.first); d->m_treeNameMoveChildren = treeNamePair.second; @@ -364,20 +368,20 @@ void Component::loadDataFromPackage(const Package &package) return; setLocalTempPath(QInstaller::pathFromUrl(package.packageSource().url)); - const QStringList uis = package.data(QLatin1String("UserInterfaces")).toString() - .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); - if (!uis.isEmpty()) - loadUserInterfaces(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), uis); + + const QStringList uiList = QInstaller::splitStringWithComma(package.data(scUserInterfaces).toString()); + if (!uiList.isEmpty()) + loadUserInterfaces(QDir(scTwoArgs.arg(localTempPath(), name())), uiList); + #ifndef IFW_DISABLE_TRANSLATIONS - const QStringList qms = package.data(QLatin1String("Translations")).toString() - .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); + const QStringList qms = QInstaller::splitStringWithComma(package.data(scTranslations).toString()); if (!qms.isEmpty()) - loadTranslations(QDir(QString::fromLatin1("%1/%2").arg(localTempPath(), name())), qms); + loadTranslations(QDir(scTwoArgs.arg(localTempPath(), name())), qms); #endif - QHash<QString, QVariant> licenseHash = package.data(QLatin1String("Licenses")).toHash(); + QHash<QString, QVariant> licenseHash = package.data(scLicenses).toHash(); if (!licenseHash.isEmpty()) - loadLicenses(QString::fromLatin1("%1/%2/").arg(localTempPath(), name()), licenseHash); - QVariant operationsVariant = package.data(QLatin1String("Operations")); + loadLicenses(scTwoArgs.arg(localTempPath(), name()), licenseHash); + QVariant operationsVariant = package.data(scOperations); if (operationsVariant.canConvert<QList<QPair<QString, QVariant>>>()) m_operationsList = operationsVariant.value<QList<QPair<QString, QVariant>>>(); } @@ -463,12 +467,14 @@ void Component::setValue(const QString &key, const QString &value) if (key == scName) d->m_componentName = normalizedValue; - if (key == scCheckable) - this->setCheckable(normalizedValue.toLower() == scTrue); + if (key == scCheckable) // Non-checkable components can still be toggled in updater + this->setCheckable(normalizedValue.toLower() == scTrue || d->m_core->isUpdater()); if (key == scExpandedByDefault) this->setExpandedByDefault(normalizedValue.toLower() == scTrue); if (key == scForcedInstallation) { - if (value == scTrue && !PackageManagerCore::noForceInstallation()) { + if (value == scTrue && !d->m_core->isUpdater() && !PackageManagerCore::noForceInstallation()) { + // Forced installation components can still be toggled in updater or when + // core is set to ignore forced installations. setCheckable(false); setCheckState(Qt::Checked); } @@ -595,38 +601,45 @@ bool Component::treeNameMoveChildren() const } /*! - Loads the component script into the script engine. + Loads the component script into the script engine. Call this method with + \a postLoad \c true to a list of components that are updated or installed + to improve performance if the amount of components is huge and there are no script + functions that need to be called before the installation starts. */ -void Component::loadComponentScript() +void Component::loadComponentScript(const bool postLoad) { - const QString script = d->m_vars.value(scScriptTag); - if (!localTempPath().isEmpty() && !script.isEmpty()) - loadComponentScript(QString::fromLatin1("%1/%2/%3").arg(localTempPath(), name(), script)); + const QString installScript(!postLoad ? d->m_scriptHash.value(scInstallScript).toString() + : d->m_scriptHash.value(scPostLoadScript).toString()); + + if (!localTempPath().isEmpty() && !installScript.isEmpty()) { + evaluateComponentScript(scThreeArgs.arg(localTempPath(), name() + , installScript), postLoad); + } } /*! - Loads the script at \a fileName into the script engine. The installer and all its - components as well as other useful things are being exported into the script. - For more information, see \l{Component Scripting}. - - Throws an error when either the script at \a fileName could not be opened, or QScriptEngine - could not evaluate the script. + \internal */ -void Component::loadComponentScript(const QString &fileName) +void Component::evaluateComponentScript(const QString &fileName, const bool postScriptContent) { // introduce the component object as javascript value and call the name to check that it // was successful try { - d->m_scriptContext = d->scriptEngine()->loadInContext(QLatin1String("Component"), fileName, - QString::fromLatin1("var component = installer.componentByName('%1'); component.name;") - .arg(name())); - } catch (const Error &error) { - if (packageManagerCore()->settings().allowUnstableComponents()) { - setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message()); - qCWarning(QInstaller::lcDeveloperBuild) << error.message(); + if (postScriptContent) { + d->m_postScriptContext = d->scriptEngine()->loadInContext(scComponent, fileName, + scComponentScriptTest.arg(name())); } else { - throw error; + d->m_scriptContext = d->scriptEngine()->loadInContext(scComponent, fileName, + scComponentScriptTest.arg(name())); } + } catch (const Error &error) { + qCWarning(QInstaller::lcDeveloperBuild) << error.message(); + setUnstable(Component::UnstableError::ScriptLoadingFailed, error.message()); + // evaluateComponentScript is called with postScriptContent after we have selected components + // and are about to install. Do not allow install if unstable components are allowed + // as we then end up installing a component which has invalid script. + if (!packageManagerCore()->settings().allowUnstableComponents() || postScriptContent) + throw error; } emit loaded(); @@ -640,7 +653,7 @@ void Component::loadComponentScript(const QString &fileName) */ void Component::languageChanged() { - d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("retranslateUi")); + callScriptMethod(scRetranslateUi); } /*! @@ -652,7 +665,7 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms) { QDirIterator it(directory.path(), qms, QDir::Files); const QStringList translations = d->m_core->settings().translations(); - const QString uiLanguage = QLocale().uiLanguages().value(0, QLatin1String("en")); + const QString uiLanguage = QLocale().uiLanguages().value(0, scEn); while (it.hasNext()) { const QString filename = it.next(); const QString basename = QFileInfo(filename).baseName(); @@ -660,18 +673,18 @@ void Component::loadTranslations(const QDir &directory, const QStringList &qms) if (!translations.isEmpty()) { bool found = false; foreach (const QString &translation, translations) - found |= translation.startsWith(QLatin1String("ifw_") + basename, Qt::CaseInsensitive); + found |= translation.startsWith(scIfw_ + basename, Qt::CaseInsensitive); if (!found) // don't load the file if it does match the UI language but is not allowed to be used continue; } else if (!uiLanguage.startsWith(QFileInfo(filename).baseName(), Qt::CaseInsensitive)) { continue; // do not load the file if it does not match the UI language } - QScopedPointer<QTranslator> translator(new QTranslator(this)); + std::unique_ptr<QTranslator> translator(new QTranslator(this)); if (translator->load(filename)) { // Do not throw if translator returns false as it may just be an intentionally // empty file. See also QTBUG-31031 - qApp->installTranslator(translator.take()); + qApp->installTranslator(translator.release()); } } } @@ -689,17 +702,17 @@ void Component::loadUserInterfaces(const QDir &directory, const QStringList &uis while (it.hasNext()) { QFile file(it.next()); if (!file.open(QIODevice::ReadOnly)) { - throw Error(tr("Cannot open the requested UI file \"%1\": %2").arg( - it.fileName(), file.errorString())); + throw Error(tr("Cannot open the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg( + it.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } - static QUiLoader loader; - loader.setTranslationEnabled(true); - loader.setLanguageChangeEnabled(true); - QWidget *const widget = loader.load(&file, 0); + QUiLoader *const loader = ProductKeyCheck::instance()->uiLoader(); + loader->setTranslationEnabled(true); + loader->setLanguageChangeEnabled(true); + QWidget *const widget = loader->load(&file, 0); if (!widget) { - throw Error(tr("Cannot load the requested UI file \"%1\": %2").arg( - it.fileName(), loader.errorString())); + throw Error(tr("Cannot load the requested UI file \"%1\": %2.\n\n%3 \"%4\"").arg( + it.fileName(), loader->errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } d->scriptEngine()->newQObject(widget); d->m_userInterfaces.insert(widget->objectName(), widget); @@ -715,7 +728,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar QHash<QString, QVariant>::const_iterator it; for (it = licenseHash.begin(); it != licenseHash.end(); ++it) { QVariantMap license = it.value().toMap(); - const QString &fileName = license.value(QLatin1String("file")).toString(); + const QString &fileName = license.value(scFile).toString(); if (!ProductKeyCheck::instance()->isValidLicenseTextFile(fileName)) continue; @@ -727,7 +740,7 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar QList<QFileInfo> fileCandidates; foreach (const QString &locale, QInstaller::localeCandidates(lang)) { - fileCandidates << QFileInfo(QString::fromLatin1("%1%2_%3.%4").arg( + fileCandidates << QFileInfo(scLocalesArgs.arg( directory, fileInfo.baseName(), locale, fileInfo.completeSuffix())); } @@ -744,12 +757,14 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar QFile file(fileInfo.filePath()); if (!file.open(QIODevice::ReadOnly)) { - throw Error(tr("Cannot open the requested license file \"%1\": %2").arg( - file.fileName(), file.errorString())); + throw Error(tr("Cannot open the requested license file \"%1\": %2.\n\n%3 \"%4\"").arg( + file.fileName(), file.errorString(), tr(scClearCacheHint), packageManagerCore()->settings().localCachePath())); } QTextStream stream(&file); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) stream.setCodec("UTF-8"); - license.insert(QLatin1String("content"), stream.readAll()); +#endif + license.insert(scContent, stream.readAll()); d->m_licenses.insert(it.key(), license); } } @@ -760,8 +775,8 @@ void Component::loadLicenses(const QString &directory, const QHash<QString, QVar */ void Component::loadXMLOperations() { - for (auto operation: m_operationsList) { - if (operation.first != QLatin1String("Extract")) + for (auto operation: qAsConst(m_operationsList)) { + if (operation.first != scExtract) addOperation(operation.first, operation.second.toStringList()); } } @@ -772,14 +787,14 @@ void Component::loadXMLOperations() */ void Component::loadXMLExtractOperations() { - for (auto operation: m_operationsList) { - if (operation.first == QLatin1String("Extract")) { + for (auto &operation: qAsConst(m_operationsList)) { + if (operation.first == scExtract) { // Create hash for Extract operations. Operation has a mandatory extract folder as // first argument and optional archive name as second argument. const QStringList &operationArgs = operation.second.toStringList(); if (operationArgs.count() == 2) { const QString archiveName = value(scVersion) + operationArgs.at(1); - const QString archivePath = QString::fromLatin1("installer://%1/%2").arg(name()).arg(archiveName); + const QString archivePath = scInstallerPrefixWithTwoArgs.arg(name()).arg(archiveName); m_archivesHash.insert(archivePath, operationArgs.at(0)); } else if (operationArgs.count() == 1) { m_defaultArchivePath = operationArgs.at(0); @@ -837,25 +852,23 @@ void Component::createOperationsForPath(const QString &path) const QFileInfo fi(path); // don't copy over a checksum file - if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) + if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) return; // the script can override this method - if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, - QLatin1String("createOperationsForPath"), QJSValueList() << path).isUndefined()) { - return; - } + if (!callScriptMethod(scCreateOperationsForPath, QJSValueList() << path).isUndefined()) + return; QString target; - static const QString prefix = QString::fromLatin1("installer://"); - target = QString::fromLatin1("@TargetDir@%1").arg(path.mid(prefix.length() + name().length())); + static const QString prefix = scInstallerPrefix; + target = scTargetDirPlaceholderWithArg.arg(path.mid(prefix.length() + name().length())); if (fi.isFile()) { - static const QString copy = QString::fromLatin1("Copy"); + static const QString copy = scCopy; addOperation(copy, QStringList() << fi.filePath() << target); } else if (fi.isDir()) { qApp->processEvents(); - static const QString mkdir = QString::fromLatin1("Mkdir"); + static const QString mkdir = scMkdir; addOperation(mkdir, QStringList(target)); QDirIterator it(fi.filePath()); @@ -883,14 +896,12 @@ void Component::createOperationsForArchive(const QString &archive) const QFileInfo fi(archive); // don't do anything with sha1 files - if (fi.suffix() == QLatin1String("sha1") && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) + if (fi.suffix() == scSha1 && QFileInfo(fi.dir(), fi.completeBaseName()).exists()) return; // the script can override this method - if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, - QLatin1String("createOperationsForArchive"), QJSValueList() << archive).isUndefined()) { - return; - } + if (!callScriptMethod(scCreateOperationsForArchive, QJSValueList() << archive).isUndefined()) + return; QScopedPointer<AbstractArchive> archiveFile(ArchiveFactory::instance().create(archive)); const bool isZip = (archiveFile && archiveFile->open(QIODevice::ReadOnly) && archiveFile->isSupported()); @@ -898,9 +909,9 @@ void Component::createOperationsForArchive(const QString &archive) if (isZip) { // component.xml can override this value if (m_archivesHash.contains(archive)) - addOperation(QLatin1String("Extract"), QStringList() << archive << m_archivesHash.value(archive)); + addOperation(scExtract, QStringList() << archive << m_archivesHash.value(archive)); else - addOperation(QLatin1String("Extract"), QStringList() << archive << m_defaultArchivePath); + addOperation(scExtract, QStringList() << archive << m_defaultArchivePath); } else { createOperationsForPath(archive); } @@ -912,7 +923,7 @@ void Component::createOperationsForArchive(const QString &archive) void Component::beginInstallation() { // the script can override this method - d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("beginInstallation")); + callScriptMethod(scBeginInstallation); } /*! @@ -922,10 +933,9 @@ void Component::beginInstallation() void Component::createOperations() { // the script can override this method - if (!d->scriptEngine()->callScriptMethod(d->m_scriptContext, QLatin1String("createOperations")) - .isUndefined()) { - d->m_operationsCreated = true; - return; + if (!callScriptMethod(scCreateOperations).isUndefined()) { + d->m_operationsCreated = true; + return; } loadXMLExtractOperations(); foreach (const QString &archive, archives()) @@ -962,10 +972,17 @@ QList<QPair<QString, bool> > Component::pathsForUninstallation() const */ QStringList Component::archives() const { - QString pathString = QString::fromLatin1("installer://%1/").arg(name()); + static const QRegularExpression regExp(scCaretSymbol); + QString pathString = scInstallerPrefixWithOneArgs.arg(name()); QStringList archivesNameList = QDir(pathString).entryList(); + + // In resources we may have older version of archives, this can happen + // when there is offline installer with same component with lower version + // number and newer version is available online + archivesNameList = archivesNameList.filter(value(scVersion)); + //RegExp "^" means line beginning - archivesNameList.replaceInStrings(QRegExp(QLatin1String("^")), pathString); + archivesNameList.replaceInStrings(regExp, pathString); return archivesNameList; } @@ -985,6 +1002,15 @@ void Component::addDownloadableArchive(const QString &path) } /*! + \internal +*/ +void Component::addDownloadableArchives(const QString& archives) +{ + Q_ASSERT(isFromOnlineRepository()); + d->m_downloadableArchivesVariable = archives; +} + +/*! Removes the archive \a path previously added via addDownloadableArchive() from this component. This can only be called if this component was downloaded from an online repository. @@ -999,9 +1025,15 @@ void Component::removeDownloadableArchive(const QString &path) /*! Returns the archives to be downloaded from the online repository before installation. + Should be called only once when the installation starts. */ -QStringList Component::downloadableArchives() const +QStringList Component::downloadableArchives() { + const QStringList downloadableArchives = d->m_downloadableArchivesVariable + .split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); + foreach (const QString downloadableArchive, downloadableArchives) + addDownloadableArchive(downloadableArchive); + return d->m_downloadableArchives; } @@ -1062,30 +1094,32 @@ OperationList Component::operations(const Operation::OperationGroups &mask) cons if (!d->m_minimumProgressOperation) { d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance() - .create(QLatin1String("MinimumProgress"), d->m_core); - d->m_minimumProgressOperation->setValue(QLatin1String("component"), name()); + .create(scMinimumProgress, d->m_core); + d->m_minimumProgressOperation->setValue(scComponentSmall, name()); d->m_operations.append(d->m_minimumProgressOperation); } if (!d->m_licenses.isEmpty()) { d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance() - .create(QLatin1String("License"), d->m_core); - d->m_licenseOperation->setValue(QLatin1String("component"), name()); + .create(scLicense, d->m_core); + d->m_licenseOperation->setValue(scComponentSmall, name()); QVariantMap licenses; const QList<QVariantMap> values = d->m_licenses.values(); for (int i = 0; i < values.count(); ++i) { - licenses.insert(values.at(i).value(QLatin1String("file")).toString(), - values.at(i).value(QLatin1String("content"))); + licenses.insert(values.at(i).value(scFile).toString(), + values.at(i).value(scContent)); } - d->m_licenseOperation->setValue(QLatin1String("licenses"), licenses); + d->m_licenseOperation->setValue(scLicensesValue, licenses); d->m_operations.append(d->m_licenseOperation); } } - OperationList operations = d->m_operations; - QtConcurrent::blockingFilter(operations, [&](const Operation *op) { - return mask.testFlag(op->group()); - }); + OperationList operations; + std::copy_if(d->m_operations.begin(), d->m_operations.end(), std::back_inserter(operations), + [&](const Operation *op) { + return mask.testFlag(op->group()); + } + ); return operations; } @@ -1096,7 +1130,7 @@ void Component::addOperation(Operation *operation) { d->m_operations.append(operation); if (RemoteClient::instance().isActive()) - operation->setValue(QLatin1String("admin"), true); + operation->setValue(scAdmin, true); } /*! @@ -1106,7 +1140,7 @@ void Component::addOperation(Operation *operation) void Component::addElevatedOperation(Operation *operation) { addOperation(operation); - operation->setValue(QLatin1String("admin"), true); + operation->setValue(scAdmin, true); } /*! @@ -1161,9 +1195,6 @@ Operation *Component::createOperation(const QString &operationName, const QStrin return operation; } - if (operation->name() == QLatin1String("Delete")) - operation->setValue(QLatin1String("performUndo"), false); - // Operation can contain variables which are resolved when performing the operation if (operation->requiresUnreplacedVariables()) operation->setArguments(parameters); @@ -1171,7 +1202,7 @@ Operation *Component::createOperation(const QString &operationName, const QStrin operation->setArguments(d->m_core->replaceVariables(parameters)); - operation->setValue(QLatin1String("component"), name()); + operation->setValue(scComponentSmall, name()); return operation; } @@ -1190,6 +1221,21 @@ void Component::markComponentUnstable(Component::UnstableError error, const QStr setValue(scUnstable, scTrue); QMetaEnum metaEnum = QMetaEnum::fromType<Component::UnstableError>(); emit packageManagerCore()->unstableComponentFound(QLatin1String(metaEnum.valueToKey(error)), errorMessage, this->name()); + + // Update the description and tooltip texts to contain + // information about the unstable error. + updateModelData(scDescription, QString()); +} + +QJSValue Component::callScriptMethod(const QString &methodName, const QJSValueList &arguments) const +{ + QJSValue scriptContext; + if (!d->m_postScriptContext.isUndefined() && d->m_postScriptContext.property(methodName).isCallable()) + scriptContext = d->m_postScriptContext; + else + scriptContext = d->m_scriptContext; + return d->scriptEngine()->callScriptMethod(scriptContext, + methodName, arguments); } namespace { @@ -1211,7 +1257,7 @@ inline bool convert(QQmlV4Function *func, QStringList *toArgs) QV4::Object *array = val->as<QV4::Object>(); uint length = array->getLength(); for (uint ii = 0; ii < length; ++ii) { - valtmp = array->getIndexed(ii); + valtmp = array->get(ii); *toArgs << valtmp->toQStringNoThrow(); } } else { @@ -1326,6 +1372,15 @@ bool Component::forcedInstallation() const } /*! + Returns whether this component is essential. Essential components + are always installed, and updated before other components. +*/ +bool Component::isEssential() const +{ + return d->m_vars.value(scEssential, scFalse).toLower() == scTrue; +} + +/*! Sets the validator callback name to \a name. */ void Component::setValidatorCallbackName(const QString &name) @@ -1340,7 +1395,7 @@ void Component::setValidatorCallbackName(const QString &name) bool Component::validatePage() { if (!validatorCallbackName.isEmpty()) - return d->scriptEngine()->callScriptMethod(d->m_scriptContext, validatorCallbackName).toBool(); + return callScriptMethod(validatorCallbackName).toBool(); return true; } @@ -1359,7 +1414,7 @@ void Component::addDependency(const QString &newDependency) if (oldDependencies.isEmpty()) setValue(scDependencies, newDependency); else - setValue(scDependencies, oldDependencies + QLatin1String(", ") + newDependency); + setValue(scDependencies, oldDependencies + scCommaWithSpace + newDependency); } /*! @@ -1367,7 +1422,7 @@ void Component::addDependency(const QString &newDependency) */ QStringList Component::dependencies() const { - return d->m_vars.value(scDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); + return QInstaller::splitStringWithComma(d->m_vars.value(scDependencies)); } /*! @@ -1375,7 +1430,7 @@ QStringList Component::dependencies() const */ QStringList Component::localDependencies() const { - return d->m_vars.value(scLocalDependencies).split(QInstaller::commaRegExp(), Qt::SkipEmptyParts); + return QInstaller::splitStringWithComma(d->m_vars.value(scLocalDependencies)); } /*! @@ -1393,7 +1448,7 @@ void Component::addAutoDependOn(const QString &newDependOn) if (oldDependOn.isEmpty()) setValue(scAutoDependOn, newDependOn); else - setValue(scAutoDependOn, oldDependOn + QLatin1String(", ") + newDependOn); + setValue(scAutoDependOn, oldDependOn + scCommaWithSpace + newDependOn); } QStringList Component::autoDependencies() const @@ -1485,8 +1540,7 @@ bool Component::isDefault() const if (d->m_vars.value(scDefault).compare(scScript, Qt::CaseInsensitive) == 0) { QJSValue valueFromScript; try { - valueFromScript = d->scriptEngine()->callScriptMethod(d->m_scriptContext, - QLatin1String("isDefault")); + valueFromScript = callScriptMethod(scIsDefault); } catch (const Error &error) { MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), QLatin1String("isDefaultError"), tr("Cannot resolve isDefault in %1").arg(name()), @@ -1720,23 +1774,26 @@ void Component::updateModelData(const QString &key, const QString &data) setData(humanReadableSize(size), UncompressedSize); } - QString tooltipText; - const QString &updateInfo = d->m_vars.value(scUpdateText); - if (!d->m_core->isUpdater() || updateInfo.isEmpty()) { - tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription)); - } else { - tooltipText = d->m_vars.value(scDescription) + QLatin1String("<br><br>") - + tr("Update Info: ") + updateInfo; - } - if (isUnstable()) { - tooltipText += QLatin1String("<br>") + tr("There was an error loading the selected component. " + if (key == scUpdateText || key == scDescription) { + QString tooltipText; + const QString &updateInfo = d->m_vars.value(scUpdateText); + if (!d->m_core->isUpdater() || updateInfo.isEmpty()) { + tooltipText = QString::fromLatin1("<html><body>%1</body></html>").arg(d->m_vars.value(scDescription)); + } else { + tooltipText = d->m_vars.value(scDescription) + scBr + scBr + + tr("Update Info: ") + updateInfo; + } + if (isUnstable()) { + tooltipText += scBr + tr("There was an error loading the selected component. " "This component cannot be installed."); - } - // replace {external-link}='' fields in component description with proper link tags - tooltipText.replace(QRegularExpression(QLatin1String("{external-link}='(.*?)'")), - QLatin1String("<a href=\"\\1\">\\1</a>")); + } + static const QRegularExpression externalLinkRegexp(QLatin1String("{external-link}='(.*?)'")); + static const QLatin1String externalLinkElement(QLatin1String("<a href=\"\\1\">\\1</a>")); + // replace {external-link}='' fields in component description with proper link tags + tooltipText.replace(externalLinkRegexp, externalLinkElement); - setData(tooltipText, Qt::ToolTipRole); + setData(tooltipText, Qt::ToolTipRole); + } } /*! |