Files
qt-creator/src/plugins/qmlprojectmanager/qmlproject.cpp

798 lines
26 KiB
C++
Raw Normal View History

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "qmlproject.h"
#include "fileformat/qmlprojectfileformat.h"
#include "fileformat/qmlprojectitem.h"
#include "qmlprojectconstants.h"
#include "qmlprojectmanagerconstants.h"
#include "qmlprojectnodes.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icontext.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/session.h>
#include <projectexplorer/target.h>
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <texteditor/textdocument.h>
#include <utils/algorithm.h>
#include <utils/infobar.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <QDebug>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QRegularExpression>
#include <QTextCodec>
#include <QTimer>
using namespace Core;
using namespace ProjectExplorer;
using namespace QmlProjectManager::Internal;
using namespace Utils;
namespace {
Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg)
}
namespace QmlProjectManager {
static int preferedQtTarget(Target *target)
{
if (target) {
const QmlBuildSystem *buildSystem = qobject_cast<QmlBuildSystem *>(target->buildSystem());
if (buildSystem && buildSystem->qt6Project())
return 6;
}
return 5;
}
Utils::FilePaths QmlProject::getUiQmlFilesForFolder(const Utils::FilePath &folder)
{
const Utils::FilePaths uiFiles = files([&](const ProjectExplorer::Node *node) {
return node->filePath().completeSuffix() == "ui.qml"
&& node->filePath().parentDir() == folder;
});
return uiFiles;
}
QmlProject::QmlProject(const Utils::FilePath &fileName)
: Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName)
{
setId(QmlProjectManager::Constants::QML_PROJECT_ID);
setProjectLanguages(Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID));
setDisplayName(fileName.completeBaseName());
setNeedsBuildConfigurations(false);
setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); });
if (QmlProject::isQtDesignStudio()) {
EditorManager::closeAllDocuments();
SessionManager::closeAllProjects();
m_openFileConnection
= connect(this,
&QmlProject::anyParsingFinished,
this,
[this](Target *target, bool success) {
if (m_openFileConnection)
disconnect(m_openFileConnection);
if (target && success) {
auto target = activeTarget();
if (!target)
return;
auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
target->buildSystem());
const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath();
if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) {
QTimer::singleShot(1000, [mainUiFile]() {
Core::EditorManager::openEditor(mainUiFile,
Utils::Id());
});
} else {
Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory()
+ "/content");
if (uiFiles.isEmpty())
uiFiles = getUiQmlFilesForFolder(projectDirectory());
if (!uiFiles.isEmpty()) {
Utils::FilePath currentFile;
if (auto cd = Core::EditorManager::currentDocument())
currentFile = cd->filePath();
if (currentFile.isEmpty() || !isKnownFile(currentFile))
QTimer::singleShot(1000, [uiFiles]() {
Core::EditorManager::openEditor(uiFiles.first(),
Utils::Id());
});
}
}
}
});
}
}
QmlBuildSystem::QmlBuildSystem(Target *target)
: BuildSystem(target)
{
m_canonicalProjectDir =
target->project()->projectFilePath().canonicalPath().normalizedPathName().parentDir();
connect(target->project(), &Project::projectFileIsDirty,
this, &QmlBuildSystem::refreshProjectFile);
// refresh first - project information is used e.g. to decide the default RC's
refresh(Everything);
// FIXME: Check. Probably bogus after the BuildSystem move.
// // addedTarget calls updateEnabled on the runconfigurations
// // which needs to happen after refresh
// foreach (Target *t, targets())
// addedTarget(t);
connect(target->project(), &Project::activeTargetChanged,
this, &QmlBuildSystem::onActiveTargetChanged);
updateDeploymentData();
}
QmlBuildSystem::~QmlBuildSystem() = default;
void QmlBuildSystem::triggerParsing()
{
refresh(Everything);
}
void QmlBuildSystem::onActiveTargetChanged(Target *)
{
// make sure e.g. the default qml imports are adapted
refresh(Configuration);
}
void QmlBuildSystem::onKitChanged()
{
// make sure e.g. the default qml imports are adapted
refresh(Configuration);
}
Utils::FilePath QmlBuildSystem::canonicalProjectDir() const
{
return m_canonicalProjectDir;
}
void QmlBuildSystem::parseProject(RefreshOptions options)
{
if (options & Files) {
if (options & ProjectFile)
m_projectItem.reset();
if (!m_projectItem) {
QString errorMessage;
m_projectItem.reset(
QmlProjectFileFormat::parseProjectFile(projectFilePath(), &errorMessage));
if (m_projectItem) {
connect(m_projectItem.get(),
&QmlProjectItem::qmlFilesChanged,
this,
&QmlBuildSystem::refreshFiles);
} else {
MessageManager::writeFlashing(
tr("Error while loading project file %1.").arg(projectFilePath().toUserOutput()));
MessageManager::writeSilently(errorMessage);
}
}
if (m_projectItem) {
m_projectItem->setSourceDirectory(canonicalProjectDir().toString());
if (m_projectItem->targetDirectory().isEmpty())
m_projectItem->setTargetDirectory(canonicalProjectDir().toString());
if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
QStringList files = m_projectItem->files();
Automatic qmlls support (experimental) Looks for qmlls (the qml language server of Qt) and if available and set in the preferences uses it instead of the embedded code model for the supported features. Its usage is driven by two flags that can be set in the QtQuick > QML/JS Editing preferences: "use qmlls" activates the use of qmlls if available; "use latest qmlls" always uses the qmlls of the latest Qt, instead of the one of the target (with the one used to compile QtCreator as fallback). To support disabling/enabling of qmlls as soon as one changes the preferences the singleton QmllsSettingsManager can emit a signal on changes. It also keeps track of the latest qmlls binary known. QmlJS::ModelmanagerInterface::ProjectInfo is also extended to keep track of the qmlls binary. QmlJSEditorDocument uses the ProjectInfo and QmllsSettingsManager to decide if a LanguageClient::Client should be started for that document. The client uses the QmllsClient subclass to keep track of the path of the qmlls clients and use the same qmlls process or all files that use the same binary. Currently qmlls <6.4.0 are not considered because they might have too many issues. The enabling/disabling of warnings and highlight is a bit cumbersome because they are handled together in the semantic highlighter, but must be handled separately depending on the qmlls capabilities. The disabling is done at the latest moment stopping the visualization of the embedded model warnings/highlights/suggestions. The computation of the semantic info is not suppressed to support the other features (find usages, semantic highlighting if active,...). When qmlls supports more features a complete removal of the semantic info construction could be evaluated. Change-Id: I3487e1680841025cabba6b339fbfe820ef83f858 Reviewed-by: David Schulz <david.schulz@qt.io>
2022-09-28 11:16:49 +02:00
modelManager
->updateSourceFiles(Utils::transform(files,
[](const QString &p) {
return Utils::FilePath::fromString(p);
}),
true);
}
QString mainFilePath = m_projectItem->mainFile();
if (!mainFilePath.isEmpty()) {
mainFilePath
= QDir(canonicalProjectDir().toString()).absoluteFilePath(mainFilePath);
Utils::FileReader reader;
QString errorMessage;
if (!reader.fetch(Utils::FilePath::fromString(mainFilePath), &errorMessage)) {
MessageManager::writeFlashing(tr("Warning while loading project file %1.")
.arg(projectFilePath().toUserOutput()));
MessageManager::writeSilently(errorMessage);
}
}
}
generateProjectTree();
}
if (options & Configuration) {
// update configuration
}
}
bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting, const Utils::FilePath &mainFilePath, const QString &oldFile)
{
// make sure to change it also in the qmlproject file
const Utils::FilePath qmlProjectFilePath = project()->projectFilePath();
Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath);
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath);
TextEditor::TextDocument *document = nullptr;
if (!editors.isEmpty()) {
document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
if (document && document->isModified())
if (!Core::DocumentManager::saveDocument(document))
return false;
}
QString fileContent;
QString error;
Utils::TextFileFormat textFileFormat;
const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error)
!= Utils::TextFileFormat::ReadSuccess) {
qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error;
}
const QString settingQmlCode = setting + ":";
const Utils::FilePath projectDir = project()->projectFilePath().parentDir();
const QString relativePath = mainFilePath.relativeChildPath(projectDir).path();
if (fileContent.indexOf(settingQmlCode) < 0) {
QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath);
auto index = fileContent.lastIndexOf("}");
fileContent.insert(index, addedText);
} else {
QString originalFileName = oldFile;
originalFileName.replace(".", "\\.");
const QRegularExpression expression(QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName));
const QRegularExpressionMatch match = expression.match(fileContent);
fileContent.replace(match.capturedStart(1),
match.capturedLength(1),
relativePath);
}
if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error))
qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error;
refresh(Everything);
return true;
}
void QmlBuildSystem::refresh(RefreshOptions options)
{
ParseGuard guard = guardParsingRun();
parseProject(options);
if (options & Files)
generateProjectTree();
auto modelManager = QmlJS::ModelManagerInterface::instance();
if (!modelManager)
return;
QmlJS::ModelManagerInterface::ProjectInfo projectInfo =
modelManager->defaultProjectInfoForProject(project());
const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths());
for (const QString &searchPath : searchPaths)
projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath),
QmlJS::Dialect::Qml);
modelManager->updateProjectInfo(projectInfo, project());
guard.markAsSuccess();
emit projectChanged();
}
QString QmlBuildSystem::mainFile() const
{
if (m_projectItem)
return m_projectItem->mainFile();
return QString();
}
QString QmlBuildSystem::mainUiFile() const
{
if (m_projectItem)
return m_projectItem->mainUiFile();
return QString();
}
Utils::FilePath QmlBuildSystem::mainFilePath() const
{
return projectDirectory().pathAppended(mainFile());
}
Utils::FilePath QmlBuildSystem::mainUiFilePath() const
{
return projectDirectory().pathAppended(mainUiFile());
}
bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath)
{
return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile());
}
bool QmlBuildSystem::setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath)
{
return setMainUiFileInMainFile(newMainUiFilePath)
&& setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, mainUiFile());
}
bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath)
{
Core::FileChangeBlocker fileChangeBlocker(mainFilePath());
const QList<Core::IEditor *> editors = Core::DocumentModel::editorsForFilePath(mainFilePath());
TextEditor::TextDocument *document = nullptr;
if (!editors.isEmpty()) {
document = qobject_cast<TextEditor::TextDocument*>(editors.first()->document());
if (document && document->isModified())
if (!Core::DocumentManager::saveDocument(document))
return false;
}
QString fileContent;
QString error;
Utils::TextFileFormat textFileFormat;
const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8
if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error)
!= Utils::TextFileFormat::ReadSuccess) {
qWarning() << "Failed to read file" << mainFilePath() << ":" << error;
}
const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName());
const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName());
if (fileContent.contains(currentMain))
fileContent.replace(currentMain, newMain);
if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error))
qWarning() << "Failed to write file" << mainFilePath() << ":" << error;
return true;
}
bool QmlBuildSystem::qtForMCUs() const
{
if (m_projectItem)
return m_projectItem->qtForMCUs();
return false;
}
bool QmlBuildSystem::qt6Project() const
{
if (m_projectItem)
return m_projectItem->qt6Project();
return false;
}
void QmlBuildSystem::setMainFile(const QString &mainFilePath)
{
if (m_projectItem)
m_projectItem->setMainFile(mainFilePath);
}
Utils::FilePath QmlBuildSystem::targetDirectory() const
{
if (DeviceTypeKitAspect::deviceTypeId(kit())
== ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE)
return canonicalProjectDir();
return m_projectItem ? Utils::FilePath::fromString(m_projectItem->targetDirectory())
: Utils::FilePath();
}
Utils::FilePath QmlBuildSystem::targetFile(const Utils::FilePath &sourceFile) const
{
const QDir sourceDir(m_projectItem ? m_projectItem->sourceDirectory()
: canonicalProjectDir().toString());
const QDir targetDir(targetDirectory().toString());
const QString relative = sourceDir.relativeFilePath(sourceFile.toString());
return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative)));
}
Utils::EnvironmentItems QmlBuildSystem::environment() const
{
if (m_projectItem)
return m_projectItem->environment();
return {};
}
QStringList QmlBuildSystem::customImportPaths() const
{
if (m_projectItem)
return m_projectItem->importPaths();
return {};
}
QStringList QmlBuildSystem::customFileSelectors() const
{
if (m_projectItem)
return m_projectItem->fileSelectors();
return {};
}
bool QmlBuildSystem::multilanguageSupport() const
{
if (m_projectItem)
return m_projectItem->multilanguageSupport();
return false;
}
QStringList QmlBuildSystem::supportedLanguages() const
{
if (m_projectItem)
return m_projectItem->supportedLanguages();
return {};
}
void QmlBuildSystem::setSupportedLanguages(QStringList languages)
{
if (m_projectItem)
m_projectItem->setSupportedLanguages(languages);
}
QString QmlBuildSystem::primaryLanguage() const
{
if (m_projectItem)
return m_projectItem->primaryLanguage();
return {};
}
void QmlBuildSystem::setPrimaryLanguage(QString language)
{
if (m_projectItem)
m_projectItem->setPrimaryLanguage(language);
}
void QmlBuildSystem::refreshProjectFile()
{
refresh(QmlBuildSystem::ProjectFile | Files);
}
QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths)
{
if (path.isEmpty())
return relativePaths;
const QDir baseDir(path.toString());
return Utils::transform(relativePaths, [&baseDir](const QString &path) {
return QDir::cleanPath(baseDir.absoluteFilePath(path));
});
}
void QmlBuildSystem::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString> &removed)
{
if (m_blockFilesUpdate) {
qCDebug(infoLogger) << "Auto files refresh blocked.";
return;
}
refresh(Files);
if (!removed.isEmpty()) {
if (auto modelManager = QmlJS::ModelManagerInterface::instance()) {
Automatic qmlls support (experimental) Looks for qmlls (the qml language server of Qt) and if available and set in the preferences uses it instead of the embedded code model for the supported features. Its usage is driven by two flags that can be set in the QtQuick > QML/JS Editing preferences: "use qmlls" activates the use of qmlls if available; "use latest qmlls" always uses the qmlls of the latest Qt, instead of the one of the target (with the one used to compile QtCreator as fallback). To support disabling/enabling of qmlls as soon as one changes the preferences the singleton QmllsSettingsManager can emit a signal on changes. It also keeps track of the latest qmlls binary known. QmlJS::ModelmanagerInterface::ProjectInfo is also extended to keep track of the qmlls binary. QmlJSEditorDocument uses the ProjectInfo and QmllsSettingsManager to decide if a LanguageClient::Client should be started for that document. The client uses the QmllsClient subclass to keep track of the path of the qmlls clients and use the same qmlls process or all files that use the same binary. Currently qmlls <6.4.0 are not considered because they might have too many issues. The enabling/disabling of warnings and highlight is a bit cumbersome because they are handled together in the semantic highlighter, but must be handled separately depending on the qmlls capabilities. The disabling is done at the latest moment stopping the visualization of the embedded model warnings/highlights/suggestions. The computation of the semantic info is not suppressed to support the other features (find usages, semantic highlighting if active,...). When qmlls supports more features a complete removal of the semantic info construction could be evaluated. Change-Id: I3487e1680841025cabba6b339fbfe820ef83f858 Reviewed-by: David Schulz <david.schulz@qt.io>
2022-09-28 11:16:49 +02:00
modelManager->removeFiles(
Utils::transform<QList<Utils::FilePath>>(removed, [](const QString &s) {
return Utils::FilePath::fromString(s);
}));
}
}
refreshTargetDirectory();
}
void QmlBuildSystem::refreshTargetDirectory()
{
updateDeploymentData();
}
Tasks QmlProject::projectIssues(const Kit *k) const
{
Tasks result = Project::projectIssues(k);
const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
if (!version)
result.append(createProjectTask(Task::TaskType::Error, tr("No Qt version set in kit.")));
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
if (dev.isNull())
result.append(createProjectTask(Task::TaskType::Error, tr("Kit has no device.")));
if (version && version->qtVersion() < QVersionNumber(5, 0, 0))
result.append(createProjectTask(Task::TaskType::Error, tr("Qt version is too old.")));
if (dev.isNull() || !version)
return result; // No need to check deeper than this
if (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
if (version->type() == QtSupport::Constants::DESKTOPQT) {
if (version->qmlRuntimeFilePath().isEmpty()) {
result.append(createProjectTask(Task::TaskType::Error,
tr("Qt version has no QML utility.")));
}
} else {
// Non-desktop Qt on a desktop device? We don't support that.
result.append(createProjectTask(Task::TaskType::Error,
tr("Non-desktop Qt is used with a desktop device.")));
}
} else {
// If not a desktop device, don't check the Qt version for qml runtime binary.
// The device is responsible for providing it and we assume qml runtime can be found
// in $PATH if it's not explicitly given.
}
return result;
}
bool QmlProject::isEditModePreferred() const
{
return !isQtDesignStudio();
}
Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage)
{
RestoreResult result = Project::fromMap(map, errorMessage);
if (result != RestoreResult::Ok)
return result;
if (!activeTarget()) {
// find a kit that matches prerequisites (prefer default one)
const QList<Kit *> kits = Utils::filtered(KitManager::kits(), [this](const Kit *k) {
return !containsType(projectIssues(k), Task::TaskType::Error)
&& DeviceTypeKitAspect::deviceTypeId(k)
== ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
});
if (!kits.isEmpty()) {
if (kits.contains(KitManager::defaultKit()))
addTargetForDefaultKit();
else
addTargetForKit(kits.first());
}
if (QmlProject::isQtDesignStudio()) {
auto setKitWithVersion = [&](int qtMajorVersion) {
const QList<Kit *> qtVersionkits
= Utils::filtered(kits, [qtMajorVersion](const Kit *k) {
if (!k->isAutoDetected())
return false;
if (k->isReplacementKit())
return false;
QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
return (version && version->qtVersion().majorVersion() == qtMajorVersion);
});
if (!qtVersionkits.isEmpty()) {
if (qtVersionkits.contains(KitManager::defaultKit()))
addTargetForDefaultKit();
else
addTargetForKit(qtVersionkits.first());
}
};
int preferedVersion = preferedQtTarget(activeTarget());
if (activeTarget())
removeTarget(activeTarget());
setKitWithVersion(preferedVersion);
}
}
return RestoreResult::Ok;
}
bool QmlProject::isQtDesignStudio()
{
QSettings *settings = Core::ICore::settings();
const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode";
return settings->value(qdsStandaloneEntry, false).toBool();
}
bool QmlProject::isQtDesignStudioStartedFromQtC()
{
return qEnvironmentVariableIsSet(Constants::enviromentLaunchedQDS);
}
ProjectExplorer::DeploymentKnowledge QmlProject::deploymentKnowledge() const
{
return DeploymentKnowledge::Perfect;
}
void QmlBuildSystem::generateProjectTree()
{
if (!m_projectItem)
return;
auto newRoot = std::make_unique<QmlProjectNode>(project());
for (const QString &f : m_projectItem->files()) {
const Utils::FilePath fileName = Utils::FilePath::fromString(f);
const FileType fileType = (fileName == projectFilePath())
? FileType::Project : FileNode::fileTypeForFileName(fileName);
newRoot->addNestedNode(std::make_unique<FileNode>(fileName, fileType));
}
newRoot->addNestedNode(std::make_unique<FileNode>(projectFilePath(), FileType::Project));
setRootProjectNode(std::move(newRoot));
refreshTargetDirectory();
}
void QmlBuildSystem::updateDeploymentData()
{
if (!m_projectItem)
return;
if (DeviceTypeKitAspect::deviceTypeId(kit())
== ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
return;
}
ProjectExplorer::DeploymentData deploymentData;
for (const QString &file : m_projectItem->files()) {
deploymentData.addFile(
FilePath::fromString(file),
targetFile(Utils::FilePath::fromString(file)).parentDir().toString());
}
setDeploymentData(deploymentData);
}
QVariant QmlBuildSystem::additionalData(Id id) const
{
if (id == Constants::customFileSelectorsData)
return customFileSelectors();
if (id == Constants::supportedLanguagesData)
return supportedLanguages();
if (id == Constants::primaryLanguageData)
return primaryLanguage();
if (id == Constants::customForceFreeTypeData)
return forceFreeType();
if (id == Constants::customQtForMCUs)
return qtForMCUs();
if (id == Constants::customQt6Project)
return qt6Project();
if (id == Constants::mainFilePath)
return mainFilePath().toString();
if (id == Constants::customImportPaths)
return customImportPaths();
if (id == Constants::canonicalProjectDir)
return canonicalProjectDir().toString();
return {};
}
bool QmlBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
{
if (dynamic_cast<QmlProjectNode *>(context)) {
if (action == AddNewFile || action == EraseFile)
return true;
QTC_ASSERT(node, return false);
if (action == Rename && node->asFileNode()) {
const FileNode *fileNode = node->asFileNode();
QTC_ASSERT(fileNode, return false);
return fileNode->fileType() != FileType::Project;
}
return false;
}
return BuildSystem::supportsAction(context, action, node);
}
QmlProject *QmlBuildSystem::qmlProject() const
{
return static_cast<QmlProject *>(BuildSystem::project());
}
bool QmlBuildSystem::forceFreeType() const
{
if (m_projectItem)
return m_projectItem->forceFreeType();
return false;
}
bool QmlBuildSystem::widgetApp() const
{
if (m_projectItem)
return m_projectItem->widgetApp();
return false;
}
QStringList QmlBuildSystem::shaderToolArgs() const
{
if (m_projectItem)
return m_projectItem->shaderToolArgs();
return {};
}
QStringList QmlBuildSystem::shaderToolFiles() const
{
if (m_projectItem)
return m_projectItem->shaderToolFiles();
return {};
}
QStringList QmlBuildSystem::importPaths() const
{
if (m_projectItem)
return m_projectItem->importPaths();
return {};
}
QStringList QmlBuildSystem::files() const
{
if (m_projectItem)
return m_projectItem->files();
return {};
}
bool QmlBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FilePaths *)
{
if (!dynamic_cast<QmlProjectNode *>(context))
return false;
FilePaths toAdd;
for (const FilePath &filePath : filePaths) {
if (!m_projectItem->matchesFile(filePath.toString()))
toAdd << filePaths;
}
return toAdd.isEmpty();
}
bool QmlBuildSystem::deleteFiles(Node *context, const FilePaths &filePaths)
{
if (dynamic_cast<QmlProjectNode *>(context))
return true;
return BuildSystem::deleteFiles(context, filePaths);
}
bool QmlBuildSystem::renameFile(Node * context, const FilePath &oldFilePath, const FilePath &newFilePath)
{
if (dynamic_cast<QmlProjectNode *>(context)) {
if (oldFilePath.endsWith(mainFile()))
return setMainFileInProjectFile(newFilePath);
if (oldFilePath.endsWith(mainUiFile()))
return setMainUiFileInProjectFile(newFilePath);
return true;
}
return BuildSystem::renameFile(context, oldFilePath, newFilePath);
}
} // namespace QmlProjectManager