2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2023-02-13 00:49:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2009-05-04 12:19:22 +02:00
|
|
|
|
|
|
|
|
#include "qmlproject.h"
|
2019-10-25 09:55:32 +02:00
|
|
|
|
2017-03-03 18:16:34 +01:00
|
|
|
#include <qtsupport/baseqtversion.h>
|
|
|
|
|
#include <qtsupport/qtkitinformation.h>
|
2013-03-07 14:02:04 +01:00
|
|
|
#include <qtsupport/qtsupportconstants.h>
|
2010-02-17 13:37:00 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
#include <QTimer>
|
2017-03-03 18:16:34 +01:00
|
|
|
|
2022-06-22 15:43:33 +02:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2023-02-14 15:47:22 +01:00
|
|
|
#include <projectexplorer/projectmanager.h>
|
2012-04-24 15:49:09 +02:00
|
|
|
#include <projectexplorer/target.h>
|
2017-03-03 18:16:34 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
|
|
|
#include <coreplugin/icontext.h>
|
|
|
|
|
#include <coreplugin/icore.h>
|
2019-08-15 08:56:07 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
#include "projectexplorer/devicesupport/idevice.h"
|
|
|
|
|
#include "qmlprojectconstants.h"
|
|
|
|
|
#include "qmlprojectmanagerconstants.h"
|
|
|
|
|
#include "qmlprojectmanagertr.h"
|
|
|
|
|
#include "utils/algorithm.h"
|
2017-03-03 18:16:34 +01:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
2011-04-15 15:55:11 +02:00
|
|
|
|
2019-10-25 09:55:32 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
|
|
|
|
|
2017-03-01 14:26:46 +01:00
|
|
|
#include <utils/algorithm.h>
|
2021-03-23 14:04:04 +01:00
|
|
|
#include <utils/infobar.h>
|
2023-05-03 17:05:35 +02:00
|
|
|
#include <utils/process.h>
|
2022-06-22 15:43:33 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2017-03-01 14:26:46 +01:00
|
|
|
|
2012-08-06 13:42:46 +02:00
|
|
|
#include <QDebug>
|
2021-03-09 17:53:36 +01:00
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QMessageBox>
|
2019-10-25 09:55:32 +02:00
|
|
|
#include <QRegularExpression>
|
|
|
|
|
#include <QTextCodec>
|
2021-03-09 17:53:36 +01:00
|
|
|
#include <QTimer>
|
2010-01-19 13:41:02 +01:00
|
|
|
|
2013-09-05 01:52:17 +02:00
|
|
|
using namespace Core;
|
2013-09-10 17:59:34 +02:00
|
|
|
using namespace ProjectExplorer;
|
2020-03-03 12:30:28 +01:00
|
|
|
|
2013-09-05 01:52:17 +02:00
|
|
|
namespace QmlProjectManager {
|
2019-08-15 12:26:14 +02:00
|
|
|
QmlProject::QmlProject(const Utils::FilePath &fileName)
|
|
|
|
|
: Project(QString::fromLatin1(Constants::QMLPROJECT_MIMETYPE), fileName)
|
2009-05-04 12:19:22 +02:00
|
|
|
{
|
2017-12-08 14:50:57 +01:00
|
|
|
setId(QmlProjectManager::Constants::QML_PROJECT_ID);
|
2023-02-13 00:49:09 +01:00
|
|
|
setProjectLanguages(Core::Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID));
|
2021-06-04 07:59:00 +02:00
|
|
|
setDisplayName(fileName.completeBaseName());
|
2019-08-13 12:11:07 +02:00
|
|
|
|
|
|
|
|
setNeedsBuildConfigurations(false);
|
2019-10-25 09:55:32 +02:00
|
|
|
setBuildSystemCreator([](Target *t) { return new QmlBuildSystem(t); });
|
2021-03-09 17:53:36 +01:00
|
|
|
|
2022-05-06 14:27:58 +03:00
|
|
|
if (QmlProject::isQtDesignStudio()) {
|
2022-10-26 15:42:47 +02:00
|
|
|
if (allowOnlySingleProject()) {
|
|
|
|
|
EditorManager::closeAllDocuments();
|
2023-02-14 15:47:22 +01:00
|
|
|
ProjectManager::closeAllProjects();
|
2022-10-26 15:42:47 +02:00
|
|
|
}
|
2021-03-09 17:53:36 +01:00
|
|
|
}
|
2019-10-25 09:55:32 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
connect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished);
|
2009-05-04 12:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
void QmlProject::parsingFinished(const Target *target, bool success)
|
2012-10-09 16:20:26 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
// trigger only once
|
|
|
|
|
disconnect(this, &QmlProject::anyParsingFinished, this, &QmlProject::parsingFinished);
|
2012-10-09 16:20:26 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
// FIXME: what to do in this case?
|
|
|
|
|
if (!target || !success || !activeTarget())
|
|
|
|
|
return;
|
2013-11-06 12:59:04 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
auto targetActive = activeTarget();
|
|
|
|
|
auto qmlBuildSystem = qobject_cast<QmlProjectManager::QmlBuildSystem *>(
|
|
|
|
|
targetActive->buildSystem());
|
2013-05-02 15:29:33 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath();
|
2009-05-06 16:13:44 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (mainUiFile.completeSuffix() == "ui.qml" && mainUiFile.exists()) {
|
|
|
|
|
QTimer::singleShot(1000, [mainUiFile]() {
|
|
|
|
|
Core::EditorManager::openEditor(mainUiFile, Utils::Id());
|
|
|
|
|
});
|
|
|
|
|
return;
|
2022-05-10 14:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
Utils::FilePaths uiFiles = collectUiQmlFilesForFolder(
|
|
|
|
|
projectDirectory().pathAppended("content"));
|
|
|
|
|
if (uiFiles.isEmpty()) {
|
|
|
|
|
uiFiles = collectUiQmlFilesForFolder(projectDirectory());
|
|
|
|
|
if (uiFiles.isEmpty())
|
|
|
|
|
return;
|
2022-05-10 14:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
Utils::FilePath currentFile;
|
|
|
|
|
if (auto cd = Core::EditorManager::currentDocument())
|
|
|
|
|
currentFile = cd->filePath();
|
2022-05-10 14:23:51 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (currentFile.isEmpty() || !isKnownFile(currentFile)) {
|
|
|
|
|
QTimer::singleShot(1000, [uiFiles]() {
|
|
|
|
|
Core::EditorManager::openEditor(uiFiles.first(), Utils::Id());
|
|
|
|
|
});
|
2022-05-10 14:23:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
Project::RestoreResult QmlProject::fromMap(const QVariantMap &map, QString *errorMessage)
|
2009-05-04 12:19:22 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
RestoreResult result = Project::fromMap(map, errorMessage);
|
|
|
|
|
if (result != RestoreResult::Ok)
|
|
|
|
|
return result;
|
2022-06-13 13:23:12 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (activeTarget())
|
|
|
|
|
return RestoreResult::Ok;
|
2009-05-04 12:19:22 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
// 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;
|
|
|
|
|
});
|
2010-12-03 17:04:29 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (!kits.isEmpty()) {
|
|
|
|
|
if (kits.contains(KitManager::defaultKit()))
|
|
|
|
|
addTargetForDefaultKit();
|
|
|
|
|
else
|
|
|
|
|
addTargetForKit(kits.first());
|
|
|
|
|
}
|
2022-05-10 14:23:51 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
// FIXME: are there any other way?
|
|
|
|
|
// What if it's not a Design Studio project? What should we do then?
|
|
|
|
|
if (QmlProject::isQtDesignStudio()) {
|
|
|
|
|
int preferedVersion = preferedQtTarget(activeTarget());
|
2023-02-22 14:55:53 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
setKitWithVersion(preferedVersion, kits);
|
|
|
|
|
}
|
2023-02-22 14:55:53 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
return RestoreResult::Ok;
|
2021-03-22 21:15:11 +01:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
bool QmlProject::setKitWithVersion(const int qtMajorVersion, const QList<Kit *> kits)
|
2022-05-10 14:23:51 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
const QList<Kit *> qtVersionkits = Utils::filtered(kits, [qtMajorVersion](const Kit *k) {
|
|
|
|
|
if (!k->isAutoDetected())
|
|
|
|
|
return false;
|
2023-02-22 14:55:53 +01:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (k->isReplacementKit())
|
|
|
|
|
return false;
|
2022-05-10 14:23:51 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
|
|
|
|
|
return (version && version->qtVersion().majorVersion() == qtMajorVersion);
|
|
|
|
|
});
|
2022-05-10 14:23:51 +02:00
|
|
|
|
|
|
|
|
|
2023-04-27 15:30:07 +02:00
|
|
|
Target *target = nullptr;
|
2022-05-10 14:23:51 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
if (!qtVersionkits.isEmpty()) {
|
|
|
|
|
if (qtVersionkits.contains(KitManager::defaultKit()))
|
2023-04-27 15:30:07 +02:00
|
|
|
target = addTargetForDefaultKit();
|
2023-02-13 00:49:09 +01:00
|
|
|
else
|
2023-04-27 15:30:07 +02:00
|
|
|
target = addTargetForKit(qtVersionkits.first());
|
2022-05-10 14:23:51 +02:00
|
|
|
}
|
|
|
|
|
|
2023-04-27 15:30:07 +02:00
|
|
|
if (target)
|
2023-05-22 19:48:42 +02:00
|
|
|
target->project()->setActiveTarget(target, SetActive::NoCascade);
|
2022-05-10 14:23:51 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
Utils::FilePaths QmlProject::collectUiQmlFilesForFolder(const Utils::FilePath &folder) const
|
2018-02-26 14:26:07 +01:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
const Utils::FilePaths uiFiles = files([&](const Node *node) {
|
|
|
|
|
return node->filePath().completeSuffix() == "ui.qml"
|
|
|
|
|
&& node->filePath().parentDir() == folder;
|
2018-02-26 14:26:07 +01:00
|
|
|
});
|
2023-02-13 00:49:09 +01:00
|
|
|
return uiFiles;
|
2017-11-28 15:57:15 +01:00
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
|
2019-05-27 16:09:44 +02:00
|
|
|
Tasks QmlProject::projectIssues(const Kit *k) const
|
2017-11-28 15:57:15 +01:00
|
|
|
{
|
2019-05-27 16:09:44 +02:00
|
|
|
Tasks result = Project::projectIssues(k);
|
2018-04-18 13:39:05 +02:00
|
|
|
|
2022-01-21 16:06:36 +01:00
|
|
|
const QtSupport::QtVersion *version = QtSupport::QtKitAspect::qtVersion(k);
|
2018-04-18 13:39:05 +02:00
|
|
|
if (!version)
|
2023-01-19 15:36:40 +01:00
|
|
|
result.append(createProjectTask(Task::TaskType::Warning, Tr::tr("No Qt version set in kit.")));
|
2018-03-08 17:46:20 +01:00
|
|
|
|
2019-02-06 12:50:51 +01:00
|
|
|
IDevice::ConstPtr dev = DeviceKitAspect::device(k);
|
2018-04-18 13:39:05 +02:00
|
|
|
if (dev.isNull())
|
2023-01-19 15:36:40 +01:00
|
|
|
result.append(createProjectTask(Task::TaskType::Error, Tr::tr("Kit has no device.")));
|
2018-03-08 17:46:20 +01:00
|
|
|
|
2022-07-04 18:36:40 +02:00
|
|
|
if (version && version->qtVersion() < QVersionNumber(5, 0, 0))
|
2023-01-19 15:36:40 +01:00
|
|
|
result.append(createProjectTask(Task::TaskType::Error, Tr::tr("Qt version is too old.")));
|
2018-04-18 13:39:05 +02:00
|
|
|
|
|
|
|
|
if (dev.isNull() || !version)
|
|
|
|
|
return result; // No need to check deeper than this
|
2018-03-08 17:46:20 +01:00
|
|
|
|
|
|
|
|
if (dev->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
|
2018-03-08 17:46:51 +01:00
|
|
|
if (version->type() == QtSupport::Constants::DESKTOPQT) {
|
2021-07-13 09:41:26 +02:00
|
|
|
if (version->qmlRuntimeFilePath().isEmpty()) {
|
2023-02-13 00:49:09 +01:00
|
|
|
result.append(
|
2023-06-09 13:33:02 +02:00
|
|
|
createProjectTask(Task::TaskType::Error,
|
|
|
|
|
Tr::tr("Qt version has no QML utility.")));
|
2018-03-08 17:46:20 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Non-desktop Qt on a desktop device? We don't support that.
|
2018-04-18 13:39:05 +02:00
|
|
|
result.append(createProjectTask(Task::TaskType::Error,
|
2023-01-19 15:36:40 +01:00
|
|
|
Tr::tr("Non-desktop Qt is used with a desktop device.")));
|
2018-03-08 17:46:20 +01:00
|
|
|
}
|
2018-04-18 13:39:05 +02:00
|
|
|
} else {
|
2021-07-12 10:38:13 +02:00
|
|
|
// 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
|
2018-04-18 13:39:05 +02:00
|
|
|
// in $PATH if it's not explicitly given.
|
2018-03-08 17:46:20 +01:00
|
|
|
}
|
|
|
|
|
|
2018-04-18 13:39:05 +02:00
|
|
|
return result;
|
2009-05-04 12:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-04 16:59:04 +01:00
|
|
|
bool QmlProject::isQtDesignStudio()
|
|
|
|
|
{
|
|
|
|
|
QSettings *settings = Core::ICore::settings();
|
|
|
|
|
const QString qdsStandaloneEntry = "QML/Designer/StandAloneMode";
|
|
|
|
|
return settings->value(qdsStandaloneEntry, false).toBool();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-22 16:33:53 +01:00
|
|
|
bool QmlProject::isQtDesignStudioStartedFromQtC()
|
|
|
|
|
{
|
|
|
|
|
return qEnvironmentVariableIsSet(Constants::enviromentLaunchedQDS);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
DeploymentKnowledge QmlProject::deploymentKnowledge() const
|
2019-04-12 14:49:59 +02:00
|
|
|
{
|
|
|
|
|
return DeploymentKnowledge::Perfect;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
bool QmlProject::isEditModePreferred() const
|
2019-10-25 09:55:32 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
return !isQtDesignStudio();
|
2019-10-25 09:55:32 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
int QmlProject::preferedQtTarget(Target *target)
|
2019-10-25 09:55:32 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
if (!target)
|
|
|
|
|
return -1;
|
2019-10-25 09:55:32 +02:00
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
auto buildSystem = qobject_cast<QmlBuildSystem *>(target->buildSystem());
|
|
|
|
|
return (buildSystem && buildSystem->qt6Project()) ? 6 : 5;
|
2019-10-25 09:55:32 +02:00
|
|
|
}
|
|
|
|
|
|
2023-02-13 00:49:09 +01:00
|
|
|
bool QmlProject::allowOnlySingleProject()
|
2019-10-25 09:55:32 +02:00
|
|
|
{
|
2023-02-13 00:49:09 +01:00
|
|
|
QSettings *settings = Core::ICore::settings();
|
|
|
|
|
auto key = "QML/Designer/AllowMultipleProjects";
|
2023-05-23 09:26:30 +02:00
|
|
|
return !settings->value(QString::fromUtf8(key), false).toBool();
|
2017-11-28 15:57:15 +01:00
|
|
|
}
|
2019-10-22 14:55:51 +02:00
|
|
|
|
2010-02-16 13:39:13 +01:00
|
|
|
} // namespace QmlProjectManager
|