EnvironmentWidget: Add convenient way to add to path list

Sometimes, users want to add entries into environment variables such as
PATH, which can get a bit fiddly when doing it by hand.
So let's add convenience UI elements for adding directories to such
variables.

Change-Id: I81c71ea0052893ff0a8175508b71cd73d83dd849
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2019-03-12 17:56:24 +01:00
parent 632d64fdc7
commit 1fcb8a56e0
6 changed files with 113 additions and 5 deletions

View File

@@ -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,

View File

@@ -66,6 +66,8 @@ public:
using EnvironmentModifier = std::function<void(Utils::Environment &)>;
void addModifier(const EnvironmentModifier &);
bool isLocal() const { return m_isLocal; }
signals:
void baseEnvironmentChanged();
void userEnvironmentChangesChanged(const QList<Utils::EnvironmentItem> &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<Utils::EnvironmentItem> m_userChanges;
QList<EnvironmentModifier> m_modifiers;
QList<BaseEnvironment> m_baseEnvironments;
int m_base = -1;
bool m_isLocal = false;
};
} // namespace ProjectExplorer

View File

@@ -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());

View File

@@ -33,10 +33,13 @@
#include <utils/environmentmodel.h>
#include <utils/environmentdialog.h>
#include <utils/headerviewstretcher.h>
#include <utils/hostosinfo.h>
#include <utils/itemviews.h>
#include <utils/tooltip/tooltip.h>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QString>
#include <QPushButton>
#include <QTreeView>
@@ -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<EnvironmentWidgetPrivate>())
{
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 &current) 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<Utils::EnvironmentItem> changes = d->m_model->userChanges();
@@ -385,6 +471,10 @@ void EnvironmentWidget::environmentCurrentIndexChanged(const QModelIndex &curren
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()

View File

@@ -29,6 +29,7 @@
#include <QWidget>
#include <functional>
#include <memory>
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 &current);
@@ -73,6 +78,10 @@ private:
void focusIndex(const QModelIndex &index);
void updateButtons();
void linkActivated(const QString &link);
bool currentEntryIsPathList(const QModelIndex &current) const;
using PathListModifier = std::function<QString(const QString &oldList, const QString &newDir)>;
void amendPathList(const PathListModifier &modifier);
const std::unique_ptr<EnvironmentWidgetPrivate> d;
};

View File

@@ -38,6 +38,7 @@ namespace ProjectExplorer {
LocalEnvironmentAspect::LocalEnvironmentAspect(Target *target)
{
setIsLocal(true);
addSupportedBaseEnvironment(tr("Clean Environment"), {});
addSupportedBaseEnvironment(tr("System Environment"), [] {