File indexing completed on 2024-05-12 04:38:19

0001 /*
0002     SPDX-FileCopyrightText: 2006 Adam Treat <treat@kde.org>
0003     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0004     SPDX-FileCopyrightText: 2008 Andreas Pakuat <apaku@gmx.de>
0005     SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "environmentwidget.h"
0011 
0012 #include <QDialog>
0013 #include <QDialogButtonBox>
0014 #include <QHeaderView>
0015 #include <QPushButton>
0016 #include <QSortFilterProxyModel>
0017 #include <QLineEdit>
0018 #include <QPlainTextEdit>
0019 #include <QVBoxLayout>
0020 #include <QHBoxLayout>
0021 #include <QValidator>
0022 
0023 #include <util/scopeddialog.h>
0024 
0025 #include <KLocalizedString>
0026 
0027 #include "environmentprofilelistmodel.h"
0028 #include "environmentprofilemodel.h"
0029 #include "placeholderitemproxymodel.h"
0030 #include "debug.h"
0031 
0032 using namespace KDevelop;
0033 
0034 
0035 class ProfileNameValidator : public QValidator
0036 {
0037     Q_OBJECT
0038 
0039 public:
0040     explicit ProfileNameValidator(EnvironmentProfileListModel* environmentProfileListModel, QObject* parent = nullptr);
0041     QValidator::State validate(QString& input, int& pos) const override;
0042 
0043 private:
0044     const EnvironmentProfileListModel* const m_environmentProfileListModel;
0045 };
0046 
0047 ProfileNameValidator::ProfileNameValidator(EnvironmentProfileListModel* environmentProfileListModel,
0048                                            QObject* parent)
0049     : QValidator(parent)
0050     , m_environmentProfileListModel(environmentProfileListModel)
0051 {
0052 }
0053 
0054 QValidator::State ProfileNameValidator::validate(QString& input, int& pos) const
0055 {
0056     Q_UNUSED(pos);
0057 
0058     if (input.isEmpty()) {
0059         return QValidator::Intermediate;
0060     }
0061     if (m_environmentProfileListModel->hasProfile(input)) {
0062         return QValidator::Intermediate;
0063     }
0064     return QValidator::Acceptable;
0065 }
0066 
0067 EnvironmentWidget::EnvironmentWidget( QWidget *parent )
0068     : QWidget(parent)
0069     , m_environmentProfileListModel(new EnvironmentProfileListModel(this))
0070     , m_environmentProfileModel(new EnvironmentProfileModel(m_environmentProfileListModel, this))
0071     , m_proxyModel(new QSortFilterProxyModel(this))
0072 {
0073     // setup ui
0074     ui.setupUi( this );
0075 
0076     ui.profileSelect->setModel(m_environmentProfileListModel);
0077     m_proxyModel->setSourceModel(m_environmentProfileModel);
0078 
0079     auto* topProxyModel  = new PlaceholderItemProxyModel(this);
0080     topProxyModel->setSourceModel(m_proxyModel);
0081     topProxyModel->setColumnHint(0, i18nc("@info:placeholder", "Enter variable..."));
0082     connect(topProxyModel, &PlaceholderItemProxyModel::dataInserted, this, &EnvironmentWidget::onVariableInserted);
0083 
0084     ui.variableTable->setModel( topProxyModel );
0085     ui.variableTable->horizontalHeader()->setSectionResizeMode( 0, QHeaderView::ResizeToContents );
0086     ui.variableTable->horizontalHeader()->setSectionResizeMode( 1, QHeaderView::Stretch );
0087     ui.removeVariableButton->setShortcut(Qt::Key_Delete);
0088 
0089     connect(ui.removeVariableButton, &QPushButton::clicked,
0090             this, &EnvironmentWidget::removeSelectedVariables);
0091     connect(ui.batchModeEditButton, &QPushButton::clicked,
0092             this, &EnvironmentWidget::batchModeEditButtonClicked);
0093 
0094     connect(ui.cloneProfileButton, &QPushButton::clicked, this, &EnvironmentWidget::cloneSelectedProfile);
0095     connect(ui.addProfileButton, &QPushButton::clicked, this, &EnvironmentWidget::addProfile);
0096     connect(ui.removeProfileButton, &QPushButton::clicked, this, &EnvironmentWidget::removeSelectedProfile);
0097     connect(ui.setAsDefaultProfileButton, &QPushButton::clicked, this, &EnvironmentWidget::setSelectedProfileAsDefault);
0098     connect(ui.profileSelect, QOverload<int>::of(&KComboBox::currentIndexChanged),
0099             this, &EnvironmentWidget::onSelectedProfileChanged);
0100 
0101     connect(m_environmentProfileListModel, &EnvironmentProfileListModel::defaultProfileChanged,
0102             this, &EnvironmentWidget::onDefaultProfileChanged);
0103     connect(m_environmentProfileListModel, &EnvironmentProfileListModel::rowsInserted,
0104             this, &EnvironmentWidget::changed);
0105     connect(m_environmentProfileListModel, &EnvironmentProfileListModel::rowsRemoved,
0106             this, &EnvironmentWidget::changed);
0107     connect(m_environmentProfileListModel, &EnvironmentProfileListModel::defaultProfileChanged,
0108             this, &EnvironmentWidget::changed);
0109 
0110     connect(ui.variableTable->selectionModel(), &QItemSelectionModel::selectionChanged,
0111             this, &EnvironmentWidget::updateDeleteVariableButton);
0112     connect(m_environmentProfileModel, &EnvironmentProfileModel::rowsInserted,
0113             this, &EnvironmentWidget::updateDeleteVariableButton);
0114     connect(m_environmentProfileModel, &EnvironmentProfileModel::rowsRemoved,
0115             this, &EnvironmentWidget::updateDeleteVariableButton);
0116     connect(m_environmentProfileModel, &EnvironmentProfileModel::modelReset,
0117             this, &EnvironmentWidget::updateDeleteVariableButton);
0118     connect(m_environmentProfileModel, &EnvironmentProfileModel::dataChanged,
0119             this, &EnvironmentWidget::changed);
0120     connect(m_environmentProfileModel, &EnvironmentProfileModel::rowsInserted,
0121             this, &EnvironmentWidget::changed);
0122     connect(m_environmentProfileModel, &EnvironmentProfileModel::rowsRemoved,
0123             this, &EnvironmentWidget::changed);
0124 }
0125 
0126 void EnvironmentWidget::selectProfile(const QString& profileName)
0127 {
0128     const int profileIndex = m_environmentProfileListModel->profileIndex(profileName);
0129     if (profileIndex < 0) {
0130         return;
0131     }
0132     ui.profileSelect->setCurrentIndex(profileIndex);
0133 }
0134 
0135 void EnvironmentWidget::updateDeleteVariableButton()
0136 {
0137     const auto selectedRows = ui.variableTable->selectionModel()->selectedRows();
0138     ui.removeVariableButton->setEnabled(!selectedRows.isEmpty());
0139 }
0140 
0141 void EnvironmentWidget::setSelectedProfileAsDefault()
0142 {
0143     const int selectedIndex = ui.profileSelect->currentIndex();
0144     m_environmentProfileListModel->setDefaultProfile(selectedIndex);
0145 }
0146 
0147 void EnvironmentWidget::loadSettings( KConfig* config )
0148 {
0149     qCDebug(SHELL) << "Loading profiles from config";
0150     m_environmentProfileListModel->loadFromConfig(config);
0151 
0152     const int defaultProfileIndex = m_environmentProfileListModel->defaultProfileIndex();
0153     ui.profileSelect->setCurrentIndex(defaultProfileIndex);
0154 }
0155 
0156 void EnvironmentWidget::saveSettings( KConfig* config )
0157 {
0158     m_environmentProfileListModel->saveToConfig(config);
0159 }
0160 
0161 void EnvironmentWidget::defaults( KConfig* config )
0162 {
0163     loadSettings( config );
0164 }
0165 
0166 QString EnvironmentWidget::askNewProfileName(const QString& defaultName)
0167 {
0168     ScopedDialog<QDialog> dialog(this);
0169     dialog->setWindowTitle(i18nc("@title:window", "Enter Name of New Environment Profile"));
0170 
0171     auto *layout = new QVBoxLayout(dialog);
0172 
0173     auto editLayout = new QHBoxLayout;
0174 
0175     auto label = new QLabel(i18nc("@label:textbox", "Name:"));
0176     editLayout->addWidget(label);
0177     auto edit = new QLineEdit;
0178     editLayout->addWidget(edit);
0179     layout->addLayout(editLayout);
0180 
0181     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0182     auto okButton = buttonBox->button(QDialogButtonBox::Ok);
0183     okButton->setEnabled(false);
0184     okButton->setDefault(true);
0185     dialog->connect(buttonBox, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept);
0186     dialog->connect(buttonBox, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject);
0187     layout->addWidget(buttonBox);
0188 
0189     auto validator = new ProfileNameValidator(m_environmentProfileListModel, dialog);
0190     connect(edit, &QLineEdit::textChanged, validator, [validator, okButton](const QString& text) {
0191         int pos;
0192         QString t(text);
0193         const bool isValidProfileName = (validator->validate(t, pos) == QValidator::Acceptable);
0194         okButton->setEnabled(isValidProfileName);
0195     });
0196 
0197     edit->setText(defaultName);
0198     edit->selectAll();
0199 
0200     if (dialog->exec() != QDialog::Accepted) {
0201         return {};
0202     }
0203 
0204     return edit->text();
0205 }
0206 
0207 void EnvironmentWidget::removeSelectedVariables()
0208 {
0209     const auto selectedRows = ui.variableTable->selectionModel()->selectedRows();
0210     if (selectedRows.isEmpty()) {
0211         return;
0212     }
0213 
0214     QStringList variables;
0215     variables.reserve(selectedRows.size());
0216     for (const auto& idx : selectedRows) {
0217         const QString variable = idx.data(EnvironmentProfileModel::VariableRole).toString();
0218         variables << variable;
0219     }
0220 
0221     m_environmentProfileModel->removeVariables(variables);
0222 }
0223 
0224 void EnvironmentWidget::onVariableInserted(int column, const QVariant& value)
0225 {
0226     Q_UNUSED(column);
0227     m_environmentProfileModel->addVariable(value.toString(), QString());
0228 }
0229 
0230 void EnvironmentWidget::batchModeEditButtonClicked()
0231 {
0232     ScopedDialog<QDialog> dialog(this);
0233     dialog->setWindowTitle( i18nc("@title:window", "Batch Edit Mode" ) );
0234 
0235     auto *layout = new QVBoxLayout(dialog);
0236 
0237     auto edit = new QPlainTextEdit;
0238     edit->setPlaceholderText(QStringLiteral("VARIABLE1=VALUE1\nVARIABLE2=VALUE2"));
0239     QString text;
0240     for (int i = 0; i < m_proxyModel->rowCount(); ++i) {
0241         const auto variable = m_proxyModel->index(i, EnvironmentProfileModel::VariableColumn).data().toString();
0242         const auto value = m_proxyModel->index(i, EnvironmentProfileModel::ValueColumn).data().toString();
0243         text.append(variable + QLatin1Char('=') + value + QLatin1Char('\n'));
0244     }
0245     edit->setPlainText(text);
0246     layout->addWidget( edit );
0247 
0248     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
0249     auto okButton = buttonBox->button(QDialogButtonBox::Ok);
0250     okButton->setDefault(true);
0251     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0252     dialog->connect(buttonBox, &QDialogButtonBox::accepted, dialog.data(), &QDialog::accept);
0253     dialog->connect(buttonBox, &QDialogButtonBox::rejected, dialog.data(), &QDialog::reject);
0254     layout->addWidget(buttonBox);
0255 
0256     dialog->resize(600, 400);
0257 
0258     if ( dialog->exec() != QDialog::Accepted ) {
0259         return;
0260     }
0261 
0262     m_environmentProfileModel->setVariablesFromString(edit->toPlainText());
0263 }
0264 
0265 void EnvironmentWidget::addProfile()
0266 {
0267     const auto profileName = askNewProfileName(QString());
0268     if (profileName.isEmpty()) {
0269         return;
0270     }
0271 
0272     const int profileIndex = m_environmentProfileListModel->addProfile(profileName);
0273 
0274     ui.profileSelect->setCurrentIndex(profileIndex);
0275     ui.variableTable->setFocus(Qt::OtherFocusReason);
0276 }
0277 
0278 void EnvironmentWidget::cloneSelectedProfile()
0279 {
0280     const int currentIndex = ui.profileSelect->currentIndex();
0281     const auto currentProfileName = m_environmentProfileListModel->profileName(currentIndex);
0282     // pass original name as starting name, as the user might want to enter a variant of it
0283     const auto profileName = askNewProfileName(currentProfileName);
0284     if (profileName.isEmpty()) {
0285         return;
0286     }
0287 
0288     const int profileIndex = m_environmentProfileListModel->cloneProfile(profileName, currentProfileName);
0289 
0290     ui.profileSelect->setCurrentIndex(profileIndex);
0291     ui.variableTable->setFocus(Qt::OtherFocusReason);
0292 }
0293 
0294 void EnvironmentWidget::removeSelectedProfile()
0295 {
0296     if (ui.profileSelect->count() <= 1) {
0297         return;
0298     }
0299 
0300     const int selectedProfileIndex = ui.profileSelect->currentIndex();
0301 
0302     m_environmentProfileListModel->removeProfile(selectedProfileIndex);
0303 
0304     const int defaultProfileIndex = m_environmentProfileListModel->defaultProfileIndex();
0305     ui.profileSelect->setCurrentIndex(defaultProfileIndex);
0306 }
0307 
0308 void EnvironmentWidget::onDefaultProfileChanged(int defaultProfileIndex)
0309 {
0310     const int selectedProfileIndex = ui.profileSelect->currentIndex();
0311     const bool isDefaultProfile = (defaultProfileIndex == selectedProfileIndex);
0312 
0313     ui.removeProfileButton->setEnabled(ui.profileSelect->count() > 1 && !isDefaultProfile);
0314     ui.setAsDefaultProfileButton->setEnabled(!isDefaultProfile);
0315 }
0316 
0317 void EnvironmentWidget::onSelectedProfileChanged(int selectedProfileIndex)
0318 {
0319     const auto selectedProfileName = m_environmentProfileListModel->profileName(selectedProfileIndex);
0320     m_environmentProfileModel->setCurrentProfile(selectedProfileName);
0321 
0322     const bool isDefaultProfile = (m_environmentProfileListModel->defaultProfileIndex() == selectedProfileIndex);
0323 
0324     ui.removeProfileButton->setEnabled(ui.profileSelect->count() > 1 && !isDefaultProfile);
0325     ui.setAsDefaultProfileButton->setEnabled(!isDefaultProfile);
0326 }
0327 
0328 #include "environmentwidget.moc"
0329 #include "moc_environmentwidget.cpp"