diff --git a/src/plugins/projectexplorer/buildenvironmentwidget.cpp b/src/plugins/projectexplorer/buildenvironmentwidget.cpp index 3177f7e6b19..047412fbf35 100644 --- a/src/plugins/projectexplorer/buildenvironmentwidget.cpp +++ b/src/plugins/projectexplorer/buildenvironmentwidget.cpp @@ -43,7 +43,8 @@ BuildEnvironmentWidget::BuildEnvironmentWidget(BuildConfiguration *bc) : m_clearSystemEnvironmentCheckBox = new QCheckBox(this); m_clearSystemEnvironmentCheckBox->setText(tr("Clear system environment")); - m_buildEnvironmentWidget = new EnvironmentWidget(this, m_clearSystemEnvironmentCheckBox); + m_buildEnvironmentWidget = new EnvironmentWidget(this, EnvironmentWidget::TypeLocal, + m_clearSystemEnvironmentCheckBox); vbox->addWidget(m_buildEnvironmentWidget); connect(m_buildEnvironmentWidget, &EnvironmentWidget::userChangesChanged, diff --git a/src/plugins/projectexplorer/environmentaspect.h b/src/plugins/projectexplorer/environmentaspect.h index 943c6d2d2aa..6657d1dfe8b 100644 --- a/src/plugins/projectexplorer/environmentaspect.h +++ b/src/plugins/projectexplorer/environmentaspect.h @@ -66,6 +66,8 @@ public: using EnvironmentModifier = std::function; void addModifier(const EnvironmentModifier &); + bool isLocal() const { return m_isLocal; } + signals: void baseEnvironmentChanged(); void userEnvironmentChangesChanged(const QList &diff); @@ -75,6 +77,8 @@ protected: void fromMap(const QVariantMap &map) override; void toMap(QVariantMap &map) const override; + void setIsLocal(bool local) { m_isLocal = local; } + private: // One possible choice in the Environment aspect. struct BaseEnvironment { @@ -84,10 +88,11 @@ private: QString displayName; }; - int m_base = -1; QList m_userChanges; QList m_modifiers; QList m_baseEnvironments; + int m_base = -1; + bool m_isLocal = false; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/environmentaspectwidget.cpp b/src/plugins/projectexplorer/environmentaspectwidget.cpp index 69d889f0e87..0898a3f1b60 100644 --- a/src/plugins/projectexplorer/environmentaspectwidget.cpp +++ b/src/plugins/projectexplorer/environmentaspectwidget.cpp @@ -72,7 +72,9 @@ EnvironmentAspectWidget::EnvironmentAspectWidget(EnvironmentAspect *aspect, QWid if (additionalWidget) baseLayout->addWidget(additionalWidget); - m_environmentWidget = new EnvironmentWidget(this, baseEnvironmentWidget); + const EnvironmentWidget::Type widgetType = aspect->isLocal() + ? EnvironmentWidget::TypeLocal : EnvironmentWidget::TypeRemote; + m_environmentWidget = new EnvironmentWidget(this, widgetType, baseEnvironmentWidget); m_environmentWidget->setBaseEnvironment(m_aspect->currentUnmodifiedBaseEnvironment()); m_environmentWidget->setBaseEnvironmentText(m_aspect->currentDisplayName()); m_environmentWidget->setUserChanges(m_aspect->userEnvironmentChanges()); diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index bcc7e4174b7..d3384a8f298 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -33,10 +33,13 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -131,10 +134,12 @@ public: QPushButton *m_resetButton; QPushButton *m_unsetButton; QPushButton *m_batchEditButton; + QPushButton *m_appendPathButton = nullptr; + QPushButton *m_prependPathButton = nullptr; QPushButton *m_terminalButton; }; -EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetailsWidget) +EnvironmentWidget::EnvironmentWidget(QWidget *parent, Type type, QWidget *additionalDetailsWidget) : QWidget(parent), d(std::make_unique()) { d->m_model = new Utils::EnvironmentModel(); @@ -200,6 +205,21 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, QWidget *additionalDetails d->m_unsetButton->setText(tr("&Unset")); buttonLayout->addWidget(d->m_unsetButton); + if (type == TypeLocal) { + d->m_appendPathButton = new QPushButton(this); + d->m_appendPathButton->setEnabled(false); + d->m_appendPathButton->setText(tr("Append Path...")); + buttonLayout->addWidget(d->m_appendPathButton); + d->m_prependPathButton = new QPushButton(this); + d->m_prependPathButton->setEnabled(false); + d->m_prependPathButton->setText(tr("Prepend Path...")); + buttonLayout->addWidget(d->m_prependPathButton); + connect(d->m_appendPathButton, &QAbstractButton::clicked, + this, &EnvironmentWidget::appendPathButtonClicked); + connect(d->m_prependPathButton, &QAbstractButton::clicked, + this, &EnvironmentWidget::prependPathButtonClicked); + } + d->m_batchEditButton = new QPushButton(this); d->m_batchEditButton->setText(tr("&Batch Edit...")); buttonLayout->addWidget(d->m_batchEditButton); @@ -318,6 +338,36 @@ void EnvironmentWidget::linkActivated(const QString &link) focusIndex(idx); } +bool EnvironmentWidget::currentEntryIsPathList(const QModelIndex ¤t) const +{ + if (!current.isValid()) + return false; + + // Look at the name first and check it against some well-known path variables. Extend as needed. + const QString varName = d->m_model->indexToVariable(current); + if (varName.compare("PATH", Utils::HostOsInfo::fileNameCaseSensitivity()) == 0) + return true; + if (Utils::HostOsInfo::isMacHost() && varName == "DYLD_LIBRARY_PATH") + return true; + if (Utils::HostOsInfo::isAnyUnixHost() && varName == "LD_LIBRARY_PATH") + return true; + + // Now check the value: If it's a list of strings separated by the platform's path separator + // and at least one of the strings is an existing directory, then that's enough proof for us. + QModelIndex valueIndex = current; + if (valueIndex.column() == 0) + valueIndex = valueIndex.siblingAtColumn(1); + const QStringList entries = d->m_model->data(valueIndex).toString() + .split(Utils::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + if (entries.length() < 2) + return false; + for (const QString &potentialDir : entries) { + if (QFileInfo(potentialDir).isDir()) + return true; + } + return false; +} + void EnvironmentWidget::updateButtons() { environmentCurrentIndexChanged(d->m_environmentView->currentIndex()); @@ -352,6 +402,42 @@ void EnvironmentWidget::unsetEnvironmentButtonClicked() d->m_model->unsetVariable(name); } +void EnvironmentWidget::amendPathList(const PathListModifier &modifier) +{ + const QString varName = d->m_model->indexToVariable(d->m_environmentView->currentIndex()); + const QString dir = QDir::toNativeSeparators( + QFileDialog::getExistingDirectory(this, tr("Choose a directory"))); + if (dir.isEmpty()) + return; + QModelIndex index = d->m_model->variableToIndex(varName); + if (!index.isValid()) + return; + if (index.column() == 0) + index = index.siblingAtColumn(1); + const QString value = d->m_model->data(index).toString(); + d->m_model->setData(index, modifier(value, dir)); +} + +void EnvironmentWidget::appendPathButtonClicked() +{ + amendPathList([](const QString &pathList, const QString &dir) { + QString newPathList = dir; + if (!pathList.isEmpty()) + newPathList.prepend(Utils::HostOsInfo::pathListSeparator()).prepend(pathList); + return newPathList; + }); +} + +void EnvironmentWidget::prependPathButtonClicked() +{ + amendPathList([](const QString &pathList, const QString &dir) { + QString newPathList = dir; + if (!pathList.isEmpty()) + newPathList.append(Utils::HostOsInfo::pathListSeparator()).append(pathList); + return newPathList; + }); +} + void EnvironmentWidget::batchEditEnvironmentButtonClicked() { const QList changes = d->m_model->userChanges(); @@ -385,6 +471,10 @@ void EnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex ¤ d->m_resetButton->setEnabled(false); d->m_unsetButton->setEnabled(false); } + if (d->m_appendPathButton) { + d->m_appendPathButton->setEnabled(currentEntryIsPathList(current)); + d->m_prependPathButton->setEnabled(currentEntryIsPathList(current)); + } } void EnvironmentWidget::invalidateCurrentIndex() diff --git a/src/plugins/projectexplorer/environmentwidget.h b/src/plugins/projectexplorer/environmentwidget.h index 3eb85bcc8d7..55e617a4739 100644 --- a/src/plugins/projectexplorer/environmentwidget.h +++ b/src/plugins/projectexplorer/environmentwidget.h @@ -29,6 +29,7 @@ #include +#include #include QT_FORWARD_DECLARE_CLASS(QModelIndex) @@ -47,7 +48,9 @@ class PROJECTEXPLORER_EXPORT EnvironmentWidget : public QWidget Q_OBJECT public: - explicit EnvironmentWidget(QWidget *parent, QWidget *additionalDetailsWidget = nullptr); + enum Type { TypeLocal, TypeRemote }; + explicit EnvironmentWidget(QWidget *parent, Type type, + QWidget *additionalDetailsWidget = nullptr); ~EnvironmentWidget() override; void setBaseEnvironmentText(const QString &text); @@ -65,6 +68,8 @@ private: void addEnvironmentButtonClicked(); void removeEnvironmentButtonClicked(); void unsetEnvironmentButtonClicked(); + void appendPathButtonClicked(); + void prependPathButtonClicked(); void batchEditEnvironmentButtonClicked(); void openTerminal(); void environmentCurrentIndexChanged(const QModelIndex ¤t); @@ -73,6 +78,10 @@ private: void focusIndex(const QModelIndex &index); void updateButtons(); void linkActivated(const QString &link); + bool currentEntryIsPathList(const QModelIndex ¤t) const; + + using PathListModifier = std::function; + void amendPathList(const PathListModifier &modifier); const std::unique_ptr d; }; diff --git a/src/plugins/projectexplorer/localenvironmentaspect.cpp b/src/plugins/projectexplorer/localenvironmentaspect.cpp index 550c49a8375..366b938ed47 100644 --- a/src/plugins/projectexplorer/localenvironmentaspect.cpp +++ b/src/plugins/projectexplorer/localenvironmentaspect.cpp @@ -38,6 +38,7 @@ namespace ProjectExplorer { LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target) { + setIsLocal(true); addSupportedBaseEnvironment(tr("Clean Environment"), {}); addSupportedBaseEnvironment(tr("System Environment"), [] {