File indexing completed on 2024-05-12 04:39:40
0001 /* 0002 SPDX-FileCopyrightText: 2010 Andreas Pakulat <apaku@gmx.de> 0003 SPDX-FileCopyrightText: 2014 Sergey Kalinichev <kalinichev.so.0@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later 0006 */ 0007 0008 #include "projectpathswidget.h" 0009 0010 #include <QFileDialog> 0011 #include <QPointer> 0012 #include <QRegExp> 0013 0014 #include <interfaces/iproject.h> 0015 0016 #include "../compilerprovider/compilerprovider.h" 0017 #include "../compilerprovider/settingsmanager.h" 0018 0019 #include "ui_projectpathswidget.h" 0020 #include "ui_batchedit.h" 0021 #include "projectpathsmodel.h" 0022 #include <debug.h> 0023 0024 #include <KMessageBox> 0025 #include <KMessageBox_KDevCompat> 0026 #include <KLocalizedString> 0027 0028 0029 using namespace KDevelop; 0030 0031 namespace 0032 { 0033 enum PageType { 0034 IncludesPage, 0035 DefinesPage, 0036 ParserArgumentsPage 0037 }; 0038 } 0039 0040 ProjectPathsWidget::ProjectPathsWidget( QWidget* parent ) 0041 : QWidget(parent), 0042 ui(new Ui::ProjectPathsWidget), 0043 pathsModel(new ProjectPathsModel(this)) 0044 { 0045 ui->setupUi( this ); 0046 0047 // hack taken from kurlrequester, make the buttons a bit less in height so they better match the url-requester 0048 ui->addPath->setFixedHeight( ui->projectPaths->sizeHint().height() ); 0049 ui->removePath->setFixedHeight( ui->projectPaths->sizeHint().height() ); 0050 0051 connect( ui->addPath, &QPushButton::clicked, this, &ProjectPathsWidget::addProjectPath ); 0052 connect( ui->removePath, &QPushButton::clicked, this, &ProjectPathsWidget::deleteProjectPath ); 0053 connect( ui->batchEdit, &QPushButton::clicked, this, &ProjectPathsWidget::batchEdit ); 0054 0055 ui->projectPaths->setModel( pathsModel ); 0056 connect( ui->projectPaths, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &ProjectPathsWidget::projectPathSelected ); 0057 connect( pathsModel, &ProjectPathsModel::dataChanged, this, &ProjectPathsWidget::changed ); 0058 connect( pathsModel, &ProjectPathsModel::rowsInserted, this, &ProjectPathsWidget::changed ); 0059 connect(pathsModel, &ProjectPathsModel::rowsRemoved, this, &ProjectPathsWidget::changed); 0060 connect(ui->compiler, &QComboBox::textActivated, this, &ProjectPathsWidget::changed); 0061 connect(ui->compiler, &QComboBox::textActivated, this, &ProjectPathsWidget::changeCompilerForPath); 0062 0063 connect( ui->includesWidget, QOverload<const QStringList&>::of(&IncludesWidget::includesChanged), this, &ProjectPathsWidget::includesChanged ); 0064 connect( ui->definesWidget, QOverload<const KDevelop::Defines&>::of(&DefinesWidget::definesChanged), this, &ProjectPathsWidget::definesChanged ); 0065 0066 connect(ui->languageParameters, &QTabWidget::currentChanged, this, &ProjectPathsWidget::tabChanged); 0067 0068 connect(ui->parserWidget, &ParserWidget::changed, this, &ProjectPathsWidget::parserArgumentsChanged); 0069 0070 tabChanged(IncludesPage); 0071 } 0072 0073 ProjectPathsWidget::~ProjectPathsWidget() 0074 { 0075 } 0076 0077 QVector<ConfigEntry> ProjectPathsWidget::paths() const 0078 { 0079 return pathsModel->paths(); 0080 } 0081 0082 void ProjectPathsWidget::setPaths( const QVector<ConfigEntry>& paths ) 0083 { 0084 bool b = blockSignals( true ); 0085 clear(); 0086 pathsModel->setPaths( paths ); 0087 blockSignals( b ); 0088 ui->projectPaths->setCurrentIndex(0); // at least a project root item is present 0089 ui->languageParameters->setCurrentIndex(0); 0090 0091 // Set compilers 0092 ui->compiler->clear(); 0093 auto settings = SettingsManager::globalInstance(); 0094 auto compilers = settings->provider()->compilers(); 0095 for (int i = 0 ; i < compilers.count(); ++i) { 0096 Q_ASSERT(compilers[i]); 0097 if (!compilers[i]) { 0098 continue; 0099 } 0100 ui->compiler->addItem(compilers[i]->name()); 0101 QVariant val; val.setValue(compilers[i]); 0102 ui->compiler->setItemData(i, val); 0103 } 0104 0105 projectPathSelected(0); 0106 updateEnablements(); 0107 } 0108 0109 void ProjectPathsWidget::definesChanged( const Defines& defines ) 0110 { 0111 qCDebug(DEFINESANDINCLUDES) << "defines changed"; 0112 updatePathsModel( QVariant::fromValue(defines), ProjectPathsModel::DefinesDataRole ); 0113 } 0114 0115 void ProjectPathsWidget::includesChanged( const QStringList& includes ) 0116 { 0117 qCDebug(DEFINESANDINCLUDES) << "includes changed"; 0118 updatePathsModel( includes, ProjectPathsModel::IncludesDataRole ); 0119 } 0120 0121 void ProjectPathsWidget::parserArgumentsChanged() 0122 { 0123 updatePathsModel(QVariant::fromValue(ui->parserWidget->parserArguments()), ProjectPathsModel::ParserArgumentsRole); 0124 } 0125 0126 void ProjectPathsWidget::updatePathsModel(const QVariant& newData, int role) 0127 { 0128 QModelIndex idx = pathsModel->index( ui->projectPaths->currentIndex(), 0, QModelIndex() ); 0129 if( idx.isValid() ) { 0130 bool b = pathsModel->setData( idx, newData, role ); 0131 if( b ) { 0132 emit changed(); 0133 } 0134 } 0135 } 0136 0137 void ProjectPathsWidget::projectPathSelected( int index ) 0138 { 0139 if( index < 0 && pathsModel->rowCount() > 0 ) { 0140 index = 0; 0141 } 0142 Q_ASSERT(index >= 0); 0143 const QModelIndex midx = pathsModel->index( index, 0 ); 0144 ui->includesWidget->setIncludes( pathsModel->data( midx, ProjectPathsModel::IncludesDataRole ).toStringList() ); 0145 ui->definesWidget->setDefines( pathsModel->data( midx, ProjectPathsModel::DefinesDataRole ).value<Defines>() ); 0146 0147 Q_ASSERT(pathsModel->data(midx, ProjectPathsModel::CompilerDataRole).value<CompilerPointer>()); 0148 0149 ui->compiler->setCurrentText(pathsModel->data(midx, ProjectPathsModel::CompilerDataRole).value<CompilerPointer>()->name()); 0150 0151 ui->parserWidget->setParserArguments(pathsModel->data(midx, ProjectPathsModel::ParserArgumentsRole).value<ParserArguments>()); 0152 0153 updateEnablements(); 0154 } 0155 0156 void ProjectPathsWidget::clear() 0157 { 0158 bool sigDisabled = ui->projectPaths->blockSignals( true ); 0159 pathsModel->setPaths({}); 0160 ui->includesWidget->clear(); 0161 ui->definesWidget->clear(); 0162 updateEnablements(); 0163 ui->projectPaths->blockSignals( sigDisabled ); 0164 } 0165 0166 void ProjectPathsWidget::addProjectPath() 0167 { 0168 const QUrl directory = pathsModel->data(pathsModel->index(0, 0), ProjectPathsModel::FullUrlDataRole).toUrl(); 0169 QPointer<QFileDialog> dlg = new QFileDialog(this, i18nc("@title:window", "Select Project Path"), directory.toLocalFile()); 0170 dlg->setFileMode(QFileDialog::Directory); 0171 dlg->setOption(QFileDialog::ShowDirsOnly); 0172 if (dlg->exec()) { 0173 pathsModel->addPath(dlg->selectedUrls().value(0)); 0174 ui->projectPaths->setCurrentIndex(pathsModel->rowCount() - 1); 0175 updateEnablements(); 0176 } 0177 delete dlg; 0178 } 0179 0180 void ProjectPathsWidget::deleteProjectPath() 0181 { 0182 const QModelIndex idx = pathsModel->index( ui->projectPaths->currentIndex(), 0 ); 0183 if (KMessageBox::questionTwoActions(this, 0184 i18n("Are you sure you want to delete the configuration for the path '%1'?", 0185 pathsModel->data(idx, Qt::DisplayRole).toString()), 0186 i18nc("@title:window", "Delete Path Configuration"), KStandardGuiItem::del(), 0187 KStandardGuiItem::cancel()) 0188 == KMessageBox::PrimaryAction) { 0189 pathsModel->removeRows( ui->projectPaths->currentIndex(), 1 ); 0190 } 0191 updateEnablements(); 0192 } 0193 0194 void ProjectPathsWidget::setProject(KDevelop::IProject* w_project) 0195 { 0196 pathsModel->setProject( w_project ); 0197 ui->includesWidget->setProject( w_project ); 0198 } 0199 0200 void ProjectPathsWidget::updateEnablements() { 0201 // Disable removal of the project root entry which is always first in the list 0202 ui->removePath->setEnabled( ui->projectPaths->currentIndex() > 0 ); 0203 } 0204 0205 void ProjectPathsWidget::batchEdit() 0206 { 0207 Ui::BatchEdit be; 0208 QPointer<QDialog> dialog = new QDialog(this); 0209 be.setupUi(dialog); 0210 0211 const int index = qMax(ui->projectPaths->currentIndex(), 0); 0212 0213 const QModelIndex midx = pathsModel->index(index, 0); 0214 0215 if (!midx.isValid()) { 0216 return; 0217 } 0218 0219 bool includesTab = ui->languageParameters->currentIndex() == 0; 0220 if (includesTab) { 0221 auto includes = pathsModel->data(midx, ProjectPathsModel::IncludesDataRole).toStringList(); 0222 be.textEdit->setPlainText(includes.join(QLatin1Char('\n'))); 0223 0224 dialog->setWindowTitle(i18nc("@title:window", "Edit Include Directories/Files")); 0225 } else { 0226 auto defines = pathsModel->data(midx, ProjectPathsModel::DefinesDataRole).value<Defines>(); 0227 0228 for (auto it = defines.constBegin(); it != defines.constEnd(); it++) { 0229 be.textEdit->appendPlainText(it.key() + QLatin1Char('=') + it.value()); 0230 } 0231 0232 dialog->setWindowTitle(i18nc("@title:window", "Edit Defined Macros")); 0233 } 0234 0235 if (dialog->exec() != QDialog::Accepted) { 0236 delete dialog; 0237 return; 0238 } 0239 0240 if (includesTab) { 0241 auto includes = be.textEdit->toPlainText().split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0242 for (auto& s : includes) { 0243 s = s.trimmed(); 0244 } 0245 0246 pathsModel->setData(midx, includes, ProjectPathsModel::IncludesDataRole); 0247 } else { 0248 auto list = be.textEdit->toPlainText().split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0249 Defines defines; 0250 0251 for (auto& d : list) { 0252 //This matches: a=b, a=, a 0253 QRegExp r(QStringLiteral("^([^=]+)(=(.*))?$")); 0254 0255 if (!r.exactMatch(d)) { 0256 continue; 0257 } 0258 defines[r.cap(1).trimmed()] = r.cap(3).trimmed(); 0259 } 0260 0261 pathsModel->setData(midx, QVariant::fromValue(defines), ProjectPathsModel::DefinesDataRole); 0262 } 0263 0264 projectPathSelected(index); 0265 delete dialog; 0266 } 0267 0268 void ProjectPathsWidget::setCurrentCompiler(const QString& name) 0269 { 0270 for (int i = 0 ; i < ui->compiler->count(); ++i) { 0271 if(ui->compiler->itemText(i) == name) 0272 { 0273 ui->compiler->setCurrentIndex(i); 0274 } 0275 } 0276 } 0277 0278 CompilerPointer ProjectPathsWidget::currentCompiler() const 0279 { 0280 return ui->compiler->itemData(ui->compiler->currentIndex()).value<CompilerPointer>(); 0281 } 0282 0283 void ProjectPathsWidget::tabChanged(int idx) 0284 { 0285 if (idx == ParserArgumentsPage) { 0286 ui->batchEdit->setVisible(false); 0287 ui->compilerBox->setVisible(true); 0288 ui->configureLabel->setText(i18n("Configure C/C++ parser")); 0289 } else { 0290 ui->batchEdit->setVisible(true); 0291 ui->compilerBox->setVisible(false); 0292 ui->configureLabel->setText(i18n("Configure which macros and include directories/files will be added to the parser during project parsing:")); 0293 } 0294 } 0295 0296 void ProjectPathsWidget::changeCompilerForPath() 0297 { 0298 for (int idx = 0; idx < pathsModel->rowCount(); idx++) { 0299 const QModelIndex midx = pathsModel->index(idx, 0); 0300 if (pathsModel->data(midx, Qt::DisplayRole) == ui->projectPaths->currentText()) { 0301 pathsModel->setData(midx, QVariant::fromValue(currentCompiler()), ProjectPathsModel::CompilerDataRole); 0302 break; 0303 } 0304 } 0305 } 0306 0307 #include "moc_projectpathswidget.cpp"