From 38da356153e6802ef03e23b3299672bd19467291 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 22 Nov 2023 14:18:32 +0100 Subject: [PATCH] ProjectExplorer: Do not create replacement kits automatically Instead keep a list of "vanished targets". These are shown in Projects mode in a separate list under the list of kits. Via the context menus of those items (or with single-click), the user can choose to either copy the steps of the vanished target to another kit (already configured or not), or to create a new replacement kit for it (similar to the previous replacement kits, with the same device type and the steps restored, but no other parameters from the kit restored - the project doesn't save that information), or remove them. The vanished targets are not removed from the project's settings as long as the user doesn't create a kit or copy the steps, so if the kit re-appears on a later run of Qt Creator, the original target is restored for that kit and the entry in the "vanished targets" list automatically vanishes. This has the advantage that in contrast to the replacement kits, the vanished targets are clearly separate from "normal" kits, and that they are local to the project. Nothing is left behind after closing the project. Change-Id: Iccec04fea38cd55ff683665c9cf4edc9a2388c82 Reviewed-by: Christian Kandeler Reviewed-by: --- src/plugins/projectexplorer/kit.cpp | 5 - src/plugins/projectexplorer/kit.h | 1 - src/plugins/projectexplorer/project.cpp | 107 ++++++++--- src/plugins/projectexplorer/project.h | 8 + src/plugins/projectexplorer/projectwindow.cpp | 175 +++++++++++++++++- src/plugins/projectexplorer/target.cpp | 32 ++-- src/plugins/projectexplorer/target.h | 1 + 7 files changed, 284 insertions(+), 45 deletions(-) diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index fd374cc23b5..d2a83381254 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -768,11 +768,6 @@ void Kit::kitUpdated() static Id replacementKey() { return "IsReplacementKit"; } -void ProjectExplorer::Kit::makeReplacementKit() -{ - setValueSilently(replacementKey(), true); -} - bool Kit::isReplacementKit() const { return value(replacementKey()).toBool(); diff --git a/src/plugins/projectexplorer/kit.h b/src/plugins/projectexplorer/kit.h index 3a8ccdfee8d..16a5c1e151c 100644 --- a/src/plugins/projectexplorer/kit.h +++ b/src/plugins/projectexplorer/kit.h @@ -116,7 +116,6 @@ public: void setMutable(Utils::Id id, bool b); bool isMutable(Utils::Id id) const; - void makeReplacementKit(); bool isReplacementKit() const; void setRelevantAspects(const QSet &relevant); diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index da912177f68..c3019e2e203 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -195,6 +195,8 @@ public: mutable QVector m_sortedNodeList; Store m_extraData; + + QList m_vanishedTargets; }; ProjectPrivate::~ProjectPrivate() @@ -442,6 +444,54 @@ void Project::setActiveTarget(Target *target, SetActive cascade) } } +QList Project::vanishedTargets() const +{ + return d->m_vanishedTargets; +} + +void Project::removeVanishedTarget(int index) +{ + QTC_ASSERT(index >= 0 && index < d->m_vanishedTargets.size(), return); + d->m_vanishedTargets.removeAt(index); + emit vanishedTargetsChanged(); +} + +void Project::removeAllVanishedTargets() +{ + d->m_vanishedTargets.clear(); + emit vanishedTargetsChanged(); +} + +Target *Project::createKitAndTargetFromStore(const Utils::Store &store) +{ + const Id id = idFromMap(store); + Id deviceTypeId = Id::fromSetting(store.value(Target::deviceTypeKey())); + if (!deviceTypeId.isValid()) + deviceTypeId = Constants::DESKTOP_DEVICE_TYPE; + const QString formerKitName = store.value(Target::displayNameKey()).toString(); + Kit *k = KitManager::registerKit( + [deviceTypeId, &formerKitName](Kit *kit) { + const QString kitName = makeUniquelyNumbered(formerKitName, + transform(KitManager::kits(), + &Kit::unexpandedDisplayName)); + kit->setUnexpandedDisplayName(kitName); + DeviceTypeKitAspect::setDeviceTypeId(kit, deviceTypeId); + kit->setup(); + }, + id); + QTC_ASSERT(k, return nullptr); + auto t = std::make_unique(this, k, Target::_constructor_tag{}); + if (!t->fromMap(store)) + return nullptr; + + if (t->runConfigurations().isEmpty() && t->buildConfigurations().isEmpty()) + return nullptr; + + auto pointer = t.get(); + addTarget(std::move(t)); + return pointer; +} + Tasks Project::projectIssues(const Kit *k) const { Tasks result; @@ -566,6 +616,23 @@ bool Project::copySteps(Target *sourceTarget, Target *newTarget) return !fatalError; } +bool Project::copySteps(const Utils::Store &store, Kit *targetKit) +{ + Target *t = target(targetKit->id()); + if (!t) { + auto t = std::make_unique(this, targetKit, Target::_constructor_tag{}); + if (!t->fromMap(store)) + return false; + + if (t->runConfigurations().isEmpty() && t->buildConfigurations().isEmpty()) + return false; + + addTarget(std::move(t)); + return true; + } + return t->addConfigurationsFromMap(store, /*setActiveConfigurations=*/false); +} + bool Project::setupTarget(Target *t) { if (d->m_needsBuildConfigurations) @@ -700,11 +767,19 @@ FilePaths Project::files(const NodeMatcher &filter) const void Project::toMap(Store &map) const { const QList ts = targets(); + const QList vts = vanishedTargets(); map.insert(ACTIVE_TARGET_KEY, ts.indexOf(d->m_activeTarget)); - map.insert(TARGET_COUNT_KEY, ts.size()); - for (int i = 0; i < ts.size(); ++i) - map.insert(numberedKey(TARGET_KEY_PREFIX, i), variantFromStore(ts.at(i)->toMap())); + map.insert(TARGET_COUNT_KEY, ts.size() + vts.size()); + int index = 0; + for (Target *t : ts) { + map.insert(numberedKey(TARGET_KEY_PREFIX, index), variantFromStore(t->toMap())); + ++index; + } + for (const Store &store : vts) { + map.insert(numberedKey(TARGET_KEY_PREFIX, index), variantFromStore(store)); + ++index; + } map.insert(EDITOR_SETTINGS_KEY, variantFromStore(d->m_editorConfiguration.toMap())); if (!d->m_pluginSettings.isEmpty()) @@ -826,32 +901,16 @@ void Project::createTargetFromMap(const Store &map, int index) if (ICore::isQtDesignStudio()) return; - Id deviceTypeId = Id::fromSetting(targetMap.value(Target::deviceTypeKey())); - if (!deviceTypeId.isValid()) - deviceTypeId = Constants::DESKTOP_DEVICE_TYPE; + d->m_vanishedTargets.append(targetMap); const QString formerKitName = targetMap.value(Target::displayNameKey()).toString(); - k = KitManager::registerKit( - [deviceTypeId, &formerKitName](Kit *kit) { - const QString kitNameSuggestion - = formerKitName.contains(::PE::Tr::tr("Replacement for")) - ? formerKitName - : ::PE::Tr::tr("Replacement for \"%1\"").arg(formerKitName); - const QString tempKitName = makeUniquelyNumbered(kitNameSuggestion, - transform(KitManager::kits(), &Kit::unexpandedDisplayName)); - kit->setUnexpandedDisplayName(tempKitName); - DeviceTypeKitAspect::setDeviceTypeId(kit, deviceTypeId); - kit->makeReplacementKit(); - kit->setup(); - }, - id); - QTC_ASSERT(k, return); TaskHub::addTask(BuildSystemTask( Task::Warning, ::PE::Tr::tr( "Project \"%1\" was configured for " - "kit \"%2\" with id %3, which does not exist anymore. The new kit \"%4\" was " - "created in its place, in an attempt not to lose custom project settings.") - .arg(displayName(), formerKitName, id.toString(), k->displayName()))); + "kit \"%2\" with id %3, which does not exist anymore. You can create a new kit " + "or copy the steps of the vanished kit to another kit in %4 mode.") + .arg(displayName(), formerKitName, id.toString(), Tr::tr("Projects")))); + return; } auto t = std::make_unique(this, k, Target::_constructor_tag{}); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 2304dd3850c..06508a18fe1 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -97,6 +97,7 @@ public: virtual Tasks projectIssues(const Kit *k) const; static bool copySteps(Target *sourceTarget, Target *newTarget); + bool copySteps(const Utils::Store &store, Kit *targetKit); void saveSettings(); enum class RestoreResult { Ok, Error, UserAbort }; @@ -179,6 +180,11 @@ public: Utils::MacroExpander *expander, const std::function &projectGetter); + QList vanishedTargets() const; + void removeVanishedTarget(int index); + void removeAllVanishedTargets(); + Target *createKitAndTargetFromStore(const Utils::Store &store); + signals: void projectFileIsDirty(const Utils::FilePath &path); @@ -193,6 +199,8 @@ signals: void removedTarget(ProjectExplorer::Target *target); void addedTarget(ProjectExplorer::Target *target); + void vanishedTargetsChanged(); + void settingsLoaded(); void aboutToSaveSettings(); diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index 40d4bdd8386..88b98ee6aa9 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -4,6 +4,8 @@ #include "projectwindow.h" #include "buildinfo.h" +#include "devicesupport/idevicefactory.h" + #include "kit.h" #include "kitmanager.h" #include "kitoptionspage.h" @@ -204,6 +206,154 @@ void BuildSystemOutputWindow::updateFilter() m_invertFilterAction.isChecked()); } +class VanishedTargetPanelItem : public TreeItem +{ +public: + VanishedTargetPanelItem(const Store &store, Project *project) + : m_store(store) + , m_project(project) + {} + + QVariant data(int column, int role) const override; + bool setData(int column, const QVariant &data, int role) override; + Qt::ItemFlags flags(int column) const override; + +protected: + Store m_store; + QPointer m_project; +}; + +static QString deviceTypeDisplayName(const Store &store) +{ + Id deviceTypeId = Id::fromSetting(store.value(Target::deviceTypeKey())); + if (!deviceTypeId.isValid()) + deviceTypeId = Constants::DESKTOP_DEVICE_TYPE; + + QString typeDisplayName = Tr::tr("Unknown device type"); + if (deviceTypeId.isValid()) { + if (IDeviceFactory *factory = IDeviceFactory::find(deviceTypeId)) + typeDisplayName = factory->displayName(); + } + return typeDisplayName; +} +static QString msgOptionsForRestoringSettings() +{ + return "" + + Tr::tr("The project was configured for kits that no longer exist. Select one of the " + "following options in the context menu to restore the project's settings:") + + "
  • " + + Tr::tr("Create a new kit with the same name for the same device type, with the " + "original build, deploy, and run steps. Other kit settings are not restored.") + + "
  • " + Tr::tr("Copy the build, deploy, and run steps to another kit.") + + "
"; +} + +QVariant VanishedTargetPanelItem::data(int column, int role) const +{ + Q_UNUSED(column) + switch (role) { + case Qt::DisplayRole: + //: vanished target display role: vanished target name (device type name) + return Tr::tr("%1 (%2)").arg(m_store.value(Target::displayNameKey()).toString(), + deviceTypeDisplayName(m_store)); + case Qt::ToolTipRole: + return msgOptionsForRestoringSettings(); + } + + return {}; +} + +bool VanishedTargetPanelItem::setData(int column, const QVariant &data, int role) +{ + Q_UNUSED(column) + + const auto addToMenu = [this](QMenu *menu) { + const int index = indexInParent(); + menu->addAction(Tr::tr("Create a New Kit"), + m_project.data(), + [index, store = m_store, project = m_project] { + Target *t = project->createKitAndTargetFromStore(store); + if (t) { + project->setActiveTarget(t, SetActive::Cascade); + project->removeVanishedTarget(index); + } + }); + QMenu *copyMenu = menu->addMenu(Tr::tr("Copy Steps to Another Kit")); + const QList kits = KitManager::kits(); + for (Kit *kit : kits) { + QAction *copyAction = copyMenu->addAction(kit->displayName()); + QObject::connect(copyAction, + &QAction::triggered, + [index, store = m_store, project = m_project, kit] { + if (project->copySteps(store, kit)) + project->removeVanishedTarget(index); + }); + } + menu->addSeparator(); + menu->addAction(Tr::tr("Remove Vanished Target \"%1\"") + .arg(m_store.value(Target::displayNameKey()).toString()), + m_project.data(), + [index, project = m_project] { project->removeVanishedTarget(index); }); + menu->addAction(Tr::tr("Remove All Vanished Targets"), + m_project.data(), + [project = m_project] { project->removeAllVanishedTargets(); }); + }; + + if (role == ContextMenuItemAdderRole) { + auto *menu = data.value(); + addToMenu(menu); + return true; + } + if (role == ItemActivatedDirectlyRole) { + QMenu menu; + addToMenu(&menu); + menu.exec(QCursor::pos()); + } + return false; +} + +Qt::ItemFlags VanishedTargetPanelItem::flags(int column) const +{ + Q_UNUSED(column) + return Qt::ItemIsEnabled; +} + +// The middle part of the second tree level, i.e. the list of vanished configured kits/targets. +class VanishedTargetsGroupItem : public TreeItem +{ +public: + explicit VanishedTargetsGroupItem(Project *project) + : m_project(project) + { + QTC_ASSERT(m_project, return); + rebuild(); + } + + void rebuild() + { + removeChildren(); + for (const Store &store : m_project->vanishedTargets()) + appendChild(new VanishedTargetPanelItem(store, m_project)); + } + + Qt::ItemFlags flags(int) const override { return Qt::NoItemFlags; } + + QVariant data(int column, int role) const override + { + Q_UNUSED(column) + switch (role) { + case Qt::DisplayRole: + return Tr::tr("Vanished Targets"); + case Qt::ToolTipRole: + return msgOptionsForRestoringSettings(); + } + return {}; + } + +private: + QPointer m_project; +}; + // Standard third level for the generic case: i.e. all except for the Build/Run page class MiscSettingsPanelItem : public TreeItem // TypedTreeItem { @@ -338,9 +488,26 @@ public: : m_project(project), m_changeListener(changeListener) { QTC_ASSERT(m_project, return); - QString display = Tr::tr("Build & Run"); - appendChild(m_targetsItem = new TargetGroupItem(display, project)); - appendChild(m_miscItem = new MiscSettingsGroupItem(project)); + appendChild(m_targetsItem = new TargetGroupItem(Tr::tr("Build & Run"), m_project)); + if (!m_project->vanishedTargets().isEmpty()) + appendChild(m_vanishedTargetsItem = new VanishedTargetsGroupItem(m_project)); + appendChild(m_miscItem = new MiscSettingsGroupItem(m_project)); + QObject::connect( + m_project, + &Project::vanishedTargetsChanged, + &m_guard, + [this] { rebuildVanishedTargets(); }, + Qt::QueuedConnection /* this is triggered by a child item, so queue */); + } + + void rebuildVanishedTargets() + { + if (m_vanishedTargetsItem) { + if (m_project->vanishedTargets().isEmpty()) + removeChildAt(indexOf(m_vanishedTargetsItem)); + else + m_vanishedTargetsItem->rebuild(); + } } QVariant data(int column, int role) const override @@ -423,9 +590,11 @@ public: } private: + QObject m_guard; int m_currentChildIndex = 0; // Start with Build & Run. Project *m_project = nullptr; TargetGroupItem *m_targetsItem = nullptr; + VanishedTargetsGroupItem *m_vanishedTargetsItem; MiscSettingsGroupItem *m_miscItem = nullptr; const std::function m_changeListener; }; diff --git a/src/plugins/projectexplorer/target.cpp b/src/plugins/projectexplorer/target.cpp index 3c5e87effe6..8231366dfce 100644 --- a/src/plugins/projectexplorer/target.cpp +++ b/src/plugins/projectexplorer/target.cpp @@ -889,15 +889,26 @@ bool Target::fromMap(const Store &map) { QTC_ASSERT(d->m_kit == KitManager::kit(id()), return false); + if (!addConfigurationsFromMap(map, /*setActiveConfigurations=*/true)) + return false; + + if (map.contains(PLUGIN_SETTINGS_KEY)) + d->m_pluginSettings = storeFromVariant(map.value(PLUGIN_SETTINGS_KEY)); + + return true; +} + +bool Target::addConfigurationsFromMap(const Utils::Store &map, bool setActiveConfigurations) +{ bool ok; int bcCount = map.value(BC_COUNT_KEY, 0).toInt(&ok); if (!ok || bcCount < 0) bcCount = 0; int activeConfiguration = map.value(ACTIVE_BC_KEY, 0).toInt(&ok); - if (!ok || activeConfiguration < 0) - activeConfiguration = 0; - if (0 > activeConfiguration || bcCount < activeConfiguration) + if (!ok || 0 > activeConfiguration || bcCount < activeConfiguration) activeConfiguration = 0; + if (!setActiveConfigurations) + activeConfiguration = -1; for (int i = 0; i < bcCount; ++i) { const Key key = numberedKey(BC_KEY_PREFIX, i); @@ -921,10 +932,10 @@ bool Target::fromMap(const Store &map) if (!ok || dcCount < 0) dcCount = 0; activeConfiguration = map.value(ACTIVE_DC_KEY, 0).toInt(&ok); - if (!ok || activeConfiguration < 0) - activeConfiguration = 0; - if (0 > activeConfiguration || dcCount < activeConfiguration) + if (!ok || 0 > activeConfiguration || dcCount < activeConfiguration) activeConfiguration = 0; + if (!setActiveConfigurations) + activeConfiguration = -1; for (int i = 0; i < dcCount; ++i) { const Key key = numberedKey(DC_KEY_PREFIX, i); @@ -948,10 +959,10 @@ bool Target::fromMap(const Store &map) if (!ok || rcCount < 0) rcCount = 0; activeConfiguration = map.value(ACTIVE_RC_KEY, 0).toInt(&ok); - if (!ok || activeConfiguration < 0) - activeConfiguration = 0; - if (0 > activeConfiguration || rcCount < activeConfiguration) + if (!ok || 0 > activeConfiguration || rcCount < activeConfiguration) activeConfiguration = 0; + if (!setActiveConfigurations) + activeConfiguration = -1; for (int i = 0; i < rcCount; ++i) { const Key key = numberedKey(RC_KEY_PREFIX, i); @@ -972,9 +983,6 @@ bool Target::fromMap(const Store &map) setActiveRunConfiguration(rc); } - if (map.contains(PLUGIN_SETTINGS_KEY)) - d->m_pluginSettings = storeFromVariant(map.value(PLUGIN_SETTINGS_KEY)); - return true; } diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h index fa8fa4b3b0e..395ce6005cd 100644 --- a/src/plugins/projectexplorer/target.h +++ b/src/plugins/projectexplorer/target.h @@ -147,6 +147,7 @@ signals: private: bool fromMap(const Utils::Store &map); + bool addConfigurationsFromMap(const Utils::Store &map, bool setActiveConfigurations); void updateDeviceState();