// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "runsettingspropertiespage.h" #include "addrunconfigdialog.h" #include "buildmanager.h" #include "buildstepspage.h" #include "deployconfiguration.h" #include "projectconfigurationmodel.h" #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "runconfiguration.h" #include "target.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ProjectExplorer { namespace Internal { // RunSettingsWidget RunSettingsWidget::RunSettingsWidget(Target *target) : m_target(target) { Q_ASSERT(m_target); m_deployConfigurationCombo = new QComboBox(this); Utils::setWheelScrollingWithoutFocusBlocked(m_deployConfigurationCombo); m_addDeployToolButton = new QPushButton(Tr::tr("Add"), this); m_removeDeployToolButton = new QPushButton(Tr::tr("Remove"), this); m_renameDeployButton = new QPushButton(Tr::tr("Rename..."), this); auto deployWidget = new QWidget(this); m_runConfigurationCombo = new QComboBox(this); m_runConfigurationCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_runConfigurationCombo->setMinimumContentsLength(15); Utils::setWheelScrollingWithoutFocusBlocked(m_runConfigurationCombo); m_addRunToolButton = new QPushButton(Tr::tr("Add..."), this); m_removeRunToolButton = new QPushButton(Tr::tr("Remove"), this); m_removeAllRunConfigsButton = new QPushButton(Tr::tr("Remove All"), this); m_renameRunButton = new QPushButton(Tr::tr("Rename..."), this); m_cloneRunButton = new QPushButton(Tr::tr("Clone..."), this); auto spacer1 = new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Minimum); auto spacer2 = new QSpacerItem(10, 10, QSizePolicy::Minimum, QSizePolicy::Expanding); auto runWidget = new QWidget(this); auto deployTitle = new QLabel(Tr::tr("Deployment"), this); auto deployLabel = new QLabel(Tr::tr("Method:"), this); auto runTitle = new QLabel(Tr::tr("Run"), this); auto runLabel = new QLabel(Tr::tr("Run configuration:"), this); runLabel->setBuddy(m_runConfigurationCombo); const QFont f = Utils::StyleHelper::uiFont(Utils::StyleHelper::UiElementH4); runTitle->setFont(f); deployTitle->setFont(f); m_gridLayout = new QGridLayout(this); m_gridLayout->setContentsMargins(0, 20, 0, 0); m_gridLayout->setHorizontalSpacing(6); m_gridLayout->setVerticalSpacing(8); m_gridLayout->addWidget(deployTitle, 0, 0, 1, -1); m_gridLayout->addWidget(deployLabel, 1, 0, 1, 1); m_gridLayout->addWidget(m_deployConfigurationCombo, 1, 1, 1, 1); m_gridLayout->addWidget(m_addDeployToolButton, 1, 2, 1, 1); m_gridLayout->addWidget(m_removeDeployToolButton, 1, 3, 1, 1); m_gridLayout->addWidget(m_renameDeployButton, 1, 4, 1, 1); m_gridLayout->addWidget(deployWidget, 2, 0, 1, -1); m_gridLayout->addWidget(runTitle, 3, 0, 1, -1); m_gridLayout->addWidget(runLabel, 4, 0, 1, 1); m_gridLayout->addWidget(m_runConfigurationCombo, 4, 1, 1, 1); m_gridLayout->addWidget(m_addRunToolButton, 4, 2, 1, 1); m_gridLayout->addWidget(m_removeRunToolButton, 4, 3, 1, 1); m_gridLayout->addWidget(m_removeAllRunConfigsButton, 4, 4, 1, 1); m_gridLayout->addWidget(m_renameRunButton, 4, 5, 1, 1); m_gridLayout->addWidget(m_cloneRunButton, 4, 6, 1, 1); m_gridLayout->addItem(spacer1, 4, 7, 1, 1); m_gridLayout->addWidget(runWidget, 5, 0, 1, -1); m_gridLayout->addItem(spacer2, 6, 0, 1, 1); // deploy part deployWidget->setContentsMargins(0, 10, 0, 25); m_deployLayout = new QVBoxLayout(deployWidget); m_deployLayout->setContentsMargins(0, 0, 0, 0); m_deployLayout->setSpacing(5); m_deployConfigurationCombo->setModel(m_target->deployConfigurationModel()); m_addDeployMenu = new QMenu(m_addDeployToolButton); m_addDeployToolButton->setMenu(m_addDeployMenu); updateDeployConfiguration(m_target->activeDeployConfiguration()); // Some projects may not support deployment, so we need this: m_addDeployToolButton->setEnabled(m_target->activeDeployConfiguration()); m_deployConfigurationCombo->setEnabled(m_target->activeDeployConfiguration()); m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().count() > 1); m_renameDeployButton->setEnabled(m_target->activeDeployConfiguration()); connect(m_addDeployMenu, &QMenu::aboutToShow, this, &RunSettingsWidget::aboutToShowDeployMenu); connect(m_deployConfigurationCombo, &QComboBox::currentIndexChanged, this, &RunSettingsWidget::currentDeployConfigurationChanged); connect(m_removeDeployToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeDeployConfiguration); connect(m_renameDeployButton, &QAbstractButton::clicked, this, &RunSettingsWidget::renameDeployConfiguration); connect(m_target, &Target::activeDeployConfigurationChanged, this, &RunSettingsWidget::activeDeployConfigurationChanged); // run part runWidget->setContentsMargins(0, 10, 0, 0); m_runLayout = new QVBoxLayout(runWidget); m_runLayout->setContentsMargins(0, 0, 0, 0); m_runLayout->setSpacing(5); m_disabledText = new Utils::InfoLabel({}, Utils::InfoLabel::Warning); m_runLayout->addWidget(m_disabledText); ProjectConfigurationModel *model = m_target->runConfigurationModel(); RunConfiguration *rc = m_target->activeRunConfiguration(); m_runConfigurationCombo->setModel(model); m_runConfigurationCombo->setCurrentIndex(model->indexFor(rc)); updateRemoveToolButtons(); m_renameRunButton->setEnabled(rc); m_cloneRunButton->setEnabled(rc); setConfigurationWidget(rc); connect(m_addRunToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::showAddRunConfigDialog); connect(m_runConfigurationCombo, &QComboBox::currentIndexChanged, this, &RunSettingsWidget::currentRunConfigurationChanged); connect(m_removeRunToolButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeRunConfiguration); connect(m_removeAllRunConfigsButton, &QAbstractButton::clicked, this, &RunSettingsWidget::removeAllRunConfigurations); connect(m_renameRunButton, &QAbstractButton::clicked, this, &RunSettingsWidget::renameRunConfiguration); connect(m_cloneRunButton, &QAbstractButton::clicked, this, &RunSettingsWidget::cloneRunConfiguration); connect(m_target, &Target::addedRunConfiguration, this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::removedRunConfiguration, this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::addedDeployConfiguration, this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::removedDeployConfiguration, this, &RunSettingsWidget::updateRemoveToolButtons); connect(m_target, &Target::activeRunConfigurationChanged, this, &RunSettingsWidget::activeRunConfigurationChanged); } void RunSettingsWidget::showAddRunConfigDialog() { AddRunConfigDialog dlg(m_target, this); if (dlg.exec() != QDialog::Accepted) return; RunConfigurationCreationInfo rci = dlg.creationInfo(); QTC_ASSERT(rci.factory, return); RunConfiguration *newRC = rci.create(m_target); if (!newRC) return; QTC_CHECK(newRC->id() == rci.factory->runConfigurationId()); m_target->addRunConfiguration(newRC); m_target->setActiveRunConfiguration(newRC); updateRemoveToolButtons(); } void RunSettingsWidget::cloneRunConfiguration() { RunConfiguration* activeRunConfiguration = m_target->activeRunConfiguration(); //: Title of a the cloned RunConfiguration window, text of the window QString name = uniqueRCName( QInputDialog::getText(this, Tr::tr("Clone Configuration"), Tr::tr("New configuration name:"), QLineEdit::Normal, activeRunConfiguration->displayName())); if (name.isEmpty()) return; RunConfiguration *newRc = RunConfigurationFactory::clone(m_target, activeRunConfiguration); if (!newRc) return; newRc->setDisplayName(name); m_target->addRunConfiguration(newRc); m_target->setActiveRunConfiguration(newRc); } void RunSettingsWidget::removeRunConfiguration() { RunConfiguration *rc = m_target->activeRunConfiguration(); QMessageBox msgBox(QMessageBox::Question, Tr::tr("Remove Run Configuration?"), Tr::tr("Do you really want to delete the run configuration %1?").arg(rc->displayName()), QMessageBox::Yes|QMessageBox::No, this); msgBox.setDefaultButton(QMessageBox::No); msgBox.setEscapeButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::No) return; m_target->removeRunConfiguration(rc); updateRemoveToolButtons(); m_renameRunButton->setEnabled(m_target->activeRunConfiguration()); m_cloneRunButton->setEnabled(m_target->activeRunConfiguration()); } void RunSettingsWidget::removeAllRunConfigurations() { QMessageBox msgBox(QMessageBox::Question, Tr::tr("Remove Run Configurations?"), Tr::tr("Do you really want to delete all run configurations?"), QMessageBox::Cancel, this); msgBox.addButton(Tr::tr("Delete"), QMessageBox::YesRole); msgBox.setDefaultButton(QMessageBox::Cancel); msgBox.setEscapeButton(QMessageBox::Cancel); if (msgBox.exec() == QMessageBox::Cancel) return; m_target->removeAllRunConfigurations(); updateRemoveToolButtons(); m_renameRunButton->setEnabled(false); m_cloneRunButton->setEnabled(false); } void RunSettingsWidget::activeRunConfigurationChanged() { if (m_ignoreChanges.isLocked()) return; ProjectConfigurationModel *model = m_target->runConfigurationModel(); int index = model->indexFor(m_target->activeRunConfiguration()); { const Utils::GuardLocker locker(m_ignoreChanges); m_runConfigurationCombo->setCurrentIndex(index); setConfigurationWidget(qobject_cast(model->projectConfigurationAt(index))); } m_renameRunButton->setEnabled(m_target->activeRunConfiguration()); m_cloneRunButton->setEnabled(m_target->activeRunConfiguration()); } void RunSettingsWidget::renameRunConfiguration() { bool ok; QString name = QInputDialog::getText(this, Tr::tr("Rename..."), Tr::tr("New name for run configuration %1:"). arg(m_target->activeRunConfiguration()->displayName()), QLineEdit::Normal, m_target->activeRunConfiguration()->displayName(), &ok); if (!ok) return; name = uniqueRCName(name); if (name.isEmpty()) return; m_target->activeRunConfiguration()->setDisplayName(name); } void RunSettingsWidget::currentRunConfigurationChanged(int index) { if (m_ignoreChanges.isLocked()) return; RunConfiguration *selectedRunConfiguration = nullptr; if (index >= 0) selectedRunConfiguration = qobject_cast (m_target->runConfigurationModel()->projectConfigurationAt(index)); if (selectedRunConfiguration == m_runConfiguration) return; { const Utils::GuardLocker locker(m_ignoreChanges); m_target->setActiveRunConfiguration(selectedRunConfiguration); } // Update the run configuration configuration widget setConfigurationWidget(selectedRunConfiguration); } void RunSettingsWidget::currentDeployConfigurationChanged(int index) { if (m_ignoreChanges.isLocked()) return; if (index == -1) m_target->setActiveDeployConfiguration(nullptr, SetActive::Cascade); else m_target->setActiveDeployConfiguration(qobject_cast(m_target->deployConfigurationModel()->projectConfigurationAt(index)), SetActive::Cascade); } void RunSettingsWidget::aboutToShowDeployMenu() { m_addDeployMenu->clear(); for (DeployConfigurationFactory *factory : DeployConfigurationFactory::find(m_target)) { QAction *action = m_addDeployMenu->addAction(factory->defaultDisplayName()); connect(action, &QAction::triggered, this, [factory, this] { DeployConfiguration *newDc = factory->create(m_target); if (!newDc) return; m_target->addDeployConfiguration(newDc); m_target->setActiveDeployConfiguration(newDc, SetActive::Cascade); m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1); }); } } void RunSettingsWidget::removeDeployConfiguration() { DeployConfiguration *dc = m_target->activeDeployConfiguration(); if (BuildManager::isBuilding(dc)) { QMessageBox box; QPushButton *closeAnyway = box.addButton(Tr::tr("Cancel Build && Remove Deploy Configuration"), QMessageBox::AcceptRole); QPushButton *cancelClose = box.addButton(Tr::tr("Do Not Remove"), QMessageBox::RejectRole); box.setDefaultButton(cancelClose); box.setWindowTitle(Tr::tr("Remove Deploy Configuration %1?").arg(dc->displayName())); box.setText(Tr::tr("The deploy configuration %1 is currently being built.").arg(dc->displayName())); box.setInformativeText(Tr::tr("Do you want to cancel the build process and remove the Deploy Configuration anyway?")); box.exec(); if (box.clickedButton() != closeAnyway) return; BuildManager::cancel(); } else { QMessageBox msgBox(QMessageBox::Question, Tr::tr("Remove Deploy Configuration?"), Tr::tr("Do you really want to delete deploy configuration %1?").arg(dc->displayName()), QMessageBox::Yes|QMessageBox::No, this); msgBox.setDefaultButton(QMessageBox::No); msgBox.setEscapeButton(QMessageBox::No); if (msgBox.exec() == QMessageBox::No) return; } m_target->removeDeployConfiguration(dc); m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().size() > 1); } void RunSettingsWidget::activeDeployConfigurationChanged() { updateDeployConfiguration(m_target->activeDeployConfiguration()); } void RunSettingsWidget::renameDeployConfiguration() { bool ok; QString name = QInputDialog::getText(this, Tr::tr("Rename..."), Tr::tr("New name for deploy configuration %1:"). arg(m_target->activeDeployConfiguration()->displayName()), QLineEdit::Normal, m_target->activeDeployConfiguration()->displayName(), &ok); if (!ok) return; name = uniqueDCName(name); if (name.isEmpty()) return; m_target->activeDeployConfiguration()->setDisplayName(name); } void RunSettingsWidget::updateRemoveToolButtons() { m_removeDeployToolButton->setEnabled(m_target->deployConfigurations().count() > 1); const bool hasRunConfigs = !m_target->runConfigurations().isEmpty(); m_removeRunToolButton->setEnabled(hasRunConfigs); m_removeAllRunConfigsButton->setEnabled(hasRunConfigs); } void RunSettingsWidget::updateDeployConfiguration(DeployConfiguration *dc) { delete m_deployConfigurationWidget; m_deployConfigurationWidget = nullptr; delete m_deploySteps; m_deploySteps = nullptr; { const Utils::GuardLocker locker(m_ignoreChanges); m_deployConfigurationCombo->setCurrentIndex(-1); } m_renameDeployButton->setEnabled(dc); if (!dc) return; int index = m_target->deployConfigurationModel()->indexFor(dc); { const Utils::GuardLocker locker(m_ignoreChanges); m_deployConfigurationCombo->setCurrentIndex(index); } m_deployConfigurationWidget = dc->createConfigWidget(); if (m_deployConfigurationWidget) m_deployLayout->addWidget(m_deployConfigurationWidget); m_deploySteps = new BuildStepListWidget(dc->stepList()); m_deployLayout->addWidget(m_deploySteps); } void RunSettingsWidget::setConfigurationWidget(RunConfiguration *rc) { if (rc == m_runConfiguration) return; delete m_runConfigurationWidget; m_runConfigurationWidget = nullptr; removeSubWidgets(); if (!rc) return; m_runConfigurationWidget = rc->createConfigurationWidget(); m_runConfiguration = rc; if (m_runConfigurationWidget) { m_runLayout->addWidget(m_runConfigurationWidget); updateEnabledState(); connect(m_runConfiguration, &RunConfiguration::enabledChanged, m_runConfigurationWidget, [this] { updateEnabledState(); }); } addRunControlWidgets(); } QString RunSettingsWidget::uniqueDCName(const QString &name) { QString result = name.trimmed(); if (!result.isEmpty()) { QStringList dcNames; const QList configurations = m_target->deployConfigurations(); for (DeployConfiguration *dc : configurations) { if (dc == m_target->activeDeployConfiguration()) continue; dcNames.append(dc->displayName()); } result = Utils::makeUniquelyNumbered(result, dcNames); } return result; } QString RunSettingsWidget::uniqueRCName(const QString &name) { QString result = name.trimmed(); if (!result.isEmpty()) { QStringList rcNames; const QList configurations = m_target->runConfigurations(); for (RunConfiguration *rc : configurations) { if (rc == m_target->activeRunConfiguration()) continue; rcNames.append(rc->displayName()); } result = Utils::makeUniquelyNumbered(result, rcNames); } return result; } void RunSettingsWidget::addRunControlWidgets() { for (Utils::BaseAspect *aspect : m_runConfiguration->aspects()) { if (QWidget *rcw = aspect->createConfigWidget()) { auto label = new QLabel(this); label->setText(aspect->displayName()); connect(aspect, &GlobalOrProjectAspect::changed, label, [label, aspect] { label->setText(aspect->displayName()); }); addSubWidget(rcw, label); } } } void RunSettingsWidget::addSubWidget(QWidget *widget, QLabel *label) { widget->setContentsMargins({}); label->setFont(Utils::StyleHelper::uiFont(Utils::StyleHelper::UiElementH4)); label->setContentsMargins(0, 18, 0, 0); QGridLayout *l = m_gridLayout; l->addWidget(label, l->rowCount(), 0, 1, -1); l->addWidget(widget, l->rowCount(), 0, 1, -1); m_subWidgets.push_back({widget, label}); } void RunSettingsWidget::removeSubWidgets() { for (const RunConfigItem &item : std::as_const(m_subWidgets)) { delete item.first; delete item.second; } m_subWidgets.clear(); } void RunSettingsWidget::updateEnabledState() { const bool enable = m_runConfiguration ? m_runConfiguration->isEnabled(Constants::NORMAL_RUN_MODE) : false; const QString reason = m_runConfiguration ? m_runConfiguration->disabledReason(Constants::NORMAL_RUN_MODE) : QString(); m_runConfigurationWidget->setEnabled(enable); m_disabledText->setVisible(!enable && !reason.isEmpty()); m_disabledText->setText(reason); } } // Internal } // ProjectExplorer