diff --git a/projectmanagers/cmake/CMakeLists.txt b/projectmanagers/cmake/CMakeLists.txt index 5097f358ec..876b0c8ecb 100644 --- a/projectmanagers/cmake/CMakeLists.txt +++ b/projectmanagers/cmake/CMakeLists.txt @@ -1,116 +1,117 @@ project(cmakemanager) add_definitions(-DTRANSLATION_DOMAIN=\"kdevcmake\") include_directories(${CMAKE_CURRENT_SOURCE_DIR}/parser) add_subdirectory(tests) add_subdirectory(icons) # enable this if you want to have the cmake debug visitor run on each CMakeLists.txt # the debug visitor prints out the Ast for the CMakeLists.txt file. #add_definitions( -DCMAKEDEBUGVISITOR ) add_definitions( -DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) set( cmakecommon_SRCS parser/cmListFileLexer.c # parser/astfactory.cpp # parser/cmakelistsparser.cpp # parser/cmakeast.cpp # parser/cmakecondition.cpp # parser/cmakeprojectvisitor.cpp # parser/variablemap.cpp # parser/cmakedebugvisitor.cpp parser/cmakecachereader.cpp parser/cmakelistsparser.cpp parser/cmakeduchaintypes.cpp # parser/cmakeparserutils.cpp # parser/cmakeduchaintypes.cpp # parser/generationexpressionsolver.cpp cmakeutils.cpp + cmakeextraargumentshistory.cpp cmakebuilddirchooser.cpp debug.cpp ) set( cmakecommon_UI cmakebuilddirchooser.ui ) set( cmakemanager_SRCS testing/ctestutils.cpp testing/ctestfindjob.cpp testing/ctestrunjob.cpp testing/ctestsuite.cpp testing/qttestdelegate.cpp cmakeimportjsonjob.cpp cmakenavigationwidget.cpp cmakemanager.cpp cmakemodelitems.cpp duchain/cmakeparsejob.cpp duchain/usebuilder.cpp duchain/declarationbuilder.cpp duchain/contextbuilder.cpp cmakecodecompletionmodel.cpp # cmakecommitchangesjob.cpp # cmakeimportjob.cpp # cmakeedit.cpp debug.cpp ) set( cmakemanager_UI cmakepossibleroots.ui ) set( cmakesettings_SRCS settings/cmakepreferences.cpp settings/cmakecachemodel.cpp settings/cmakecachedelegate.cpp settings/cmakecachemodel.cpp debug.cpp ) ki18n_wrap_ui(cmakesettings_SRCS settings/cmakebuildsettings.ui) set( cmakedoc_SRCS cmakedocumentation.cpp cmakehelpdocumentation.cpp ) remove_definitions( -DQT_NO_STL ) if(WIN32) add_definitions(-DYY_NO_UNISTD_H) endif() # Note: This library doesn't follow API/ABI/BC rules and shouldn't have a SOVERSION # Its only purpose is to support the plugin without needing to add all source files # to the plugin target ki18n_wrap_ui( cmakecommon_SRCS ${cmakecommon_UI} ) add_library( kdevcmakecommon SHARED ${cmakecommon_SRCS} ) target_link_libraries( kdevcmakecommon KF5::TextEditor KDev::Interfaces KDev::Project KDev::Util KDev::Language ) generate_export_header(kdevcmakecommon EXPORT_FILE_NAME cmakecommonexport.h) ki18n_wrap_ui( cmakemanager_SRCS ${cmakemanager_UI} ) add_library( kdevcmakemanagernosettings STATIC ${cmakemanager_SRCS}) target_link_libraries( kdevcmakemanagernosettings KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakemanager JSON kdevcmakemanager.json SOURCES ${cmakemanager_SRCS} ${cmakesettings_SRCS}) target_link_libraries( kdevcmakemanager KF5::KIOWidgets KDev::Util KDev::Interfaces kdevcmakecommon kdevmakefileresolver KDev::Project KDev::Language KDev::OutputView KF5::TextEditor Qt5::Concurrent) kdevplatform_add_plugin(kdevcmakedocumentation JSON kdevcmakedocumentation.json SOURCES ${cmakedoc_SRCS}) target_link_libraries( kdevcmakedocumentation KDev::Interfaces kdevcmakecommon KDev::Project KDev::Language KDev::Documentation KF5::TextEditor) install(TARGETS kdevcmakecommon ${INSTALL_TARGETS_DEFAULT_ARGS} ) diff --git a/projectmanagers/cmake/cmakebuilddirchooser.cpp b/projectmanagers/cmake/cmakebuilddirchooser.cpp index 2376bd393b..e55ed81201 100644 --- a/projectmanagers/cmake/cmakebuilddirchooser.cpp +++ b/projectmanagers/cmake/cmakebuilddirchooser.cpp @@ -1,343 +1,311 @@ /* KDevelop CMake Support * * Copyright 2007 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakebuilddirchooser.h" #include "ui_cmakebuilddirchooser.h" #include "cmakeutils.h" #include "debug.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; -namespace { - -const int maxExtraArgumentsInHistory = 15; - -} - CMakeBuildDirChooser::CMakeBuildDirChooser(QWidget* parent) : QDialog(parent) { setWindowTitle(i18n("Configure a build directory")); m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); auto mainWidget = new QWidget(this); auto mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(mainWidget); m_chooserUi = new Ui::CMakeBuildDirChooser; m_chooserUi->setupUi(mainWidget); mainLayout->addWidget(m_buttonBox); m_chooserUi->buildFolder->setMode(KFile::Directory|KFile::ExistingOnly); m_chooserUi->installPrefix->setMode(KFile::Directory|KFile::ExistingOnly); setCMakeBinary(Path(CMake::findExecutable())); - KConfigGroup config = KSharedConfig::openConfig()->group("CMakeBuildDirChooser"); - QStringList lastExtraArguments = config.readEntry("LastExtraArguments", QStringList());; - m_chooserUi->extraArguments->addItem(""); - m_chooserUi->extraArguments->addItems(lastExtraArguments); - m_chooserUi->extraArguments->setInsertPolicy(QComboBox::InsertAtTop); - KCompletion *comp = m_chooserUi->extraArguments->completionObject(); - connect(m_chooserUi->extraArguments, static_cast(&KComboBox::returnPressed), comp, static_cast(&KCompletion::addItem)); - comp->insertItems(lastExtraArguments); + m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_chooserUi->extraArguments); connect(m_chooserUi->cmakeBin, &KUrlRequester::textChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->buildFolder, &KUrlRequester::textChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->buildType, static_cast(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->extraArguments, &KComboBox::editTextChanged, this, &CMakeBuildDirChooser::updated); updated(); } CMakeBuildDirChooser::~CMakeBuildDirChooser() { - KConfigGroup config = KSharedConfig::openConfig()->group("CMakeBuildDirChooser"); - config.writeEntry("LastExtraArguments", extraArgumentsHistory()); - config.sync(); + delete m_extraArgumentsHistory; delete m_chooserUi; } void CMakeBuildDirChooser::setSourceFolder( const Path &srcFolder ) { m_srcFolder = srcFolder; m_chooserUi->buildFolder->setUrl(KDevelop::proposedBuildFolder(srcFolder).toUrl()); setWindowTitle(i18n("Configure a build directory for %1", srcFolder.toLocalFile())); update(); } void CMakeBuildDirChooser::buildDirSettings( const KDevelop::Path& buildDir, QString& srcDir, QString& installDir, QString& buildType) { static const QString srcLine = QStringLiteral("CMAKE_HOME_DIRECTORY:INTERNAL="); static const QString installLine = QStringLiteral("CMAKE_INSTALL_PREFIX:PATH="); static const QString buildLine = QStringLiteral("CMAKE_BUILD_TYPE:STRING="); const Path cachePath(buildDir, "CMakeCache.txt"); QFile file(cachePath.toLocalFile()); srcDir.clear(); installDir.clear(); buildType.clear(); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(CMAKE) << "Something really strange happened reading" << cachePath; return; } int cnt = 0; while (cnt != 3 && !file.atEnd()) { // note: CMakeCache.txt is UTF8-encoded, also see bug 329305 QString line = QString::fromUtf8(file.readLine().trimmed()); if (line.startsWith(srcLine)) { srcDir = line.mid(srcLine.count()); ++cnt; } if (line.startsWith(installLine)) { installDir = line.mid(installLine.count()); ++cnt; } if (line.startsWith(buildLine)) { buildType = line.mid(buildLine.count()); ++cnt; } } qCDebug(CMAKE) << "The source directory for " << file.fileName() << "is" << srcDir; qCDebug(CMAKE) << "The install directory for " << file.fileName() << "is" << installDir; qCDebug(CMAKE) << "The build type for " << file.fileName() << "is" << buildType; } void CMakeBuildDirChooser::updated() { bool haveCMake=QFile::exists(m_chooserUi->cmakeBin->url().toLocalFile()); StatusTypes st; if( haveCMake ) st |= HaveCMake; m_chooserUi->buildFolder->setEnabled(haveCMake); m_chooserUi->installPrefix->setEnabled(haveCMake); m_chooserUi->buildType->setEnabled(haveCMake); // m_chooserUi->generator->setEnabled(haveCMake); if(!haveCMake) { setStatus(i18n("You need to select a cmake binary."), false); return; } Path chosenBuildFolder(m_chooserUi->buildFolder->url()); bool emptyUrl = chosenBuildFolder.isEmpty(); if( emptyUrl ) st |= BuildFolderEmpty; bool dirEmpty = false, dirExists= false, dirRelative = false; QString srcDir; QString installDir; QString buildType; if(!emptyUrl) { QDir d(chosenBuildFolder.toLocalFile()); dirExists = d.exists(); dirEmpty = dirExists && d.count()<=2; dirRelative = d.isRelative(); if(!dirEmpty && dirExists && !dirRelative) { bool hasCache=QFile::exists(Path(chosenBuildFolder, "CMakeCache.txt").toLocalFile()); if(hasCache) { QString proposed=m_srcFolder.toLocalFile(); buildDirSettings(chosenBuildFolder, srcDir, installDir, buildType); if(!srcDir.isEmpty()) { if(QDir(srcDir).canonicalPath()==QDir(proposed).canonicalPath()) { st |= CorrectBuildDir | BuildDirCreated; } } else { qCWarning(CMAKE) << "maybe you are trying a damaged CMakeCache.txt file. Proper: "; } if(!installDir.isEmpty() && QDir(installDir).exists()) { m_chooserUi->installPrefix->setUrl(QUrl::fromLocalFile(installDir)); } m_chooserUi->buildType->setCurrentText(buildType); } } if(m_alreadyUsed.contains(chosenBuildFolder.toLocalFile())) { st=DirAlreadyCreated; } } else { setStatus(i18n("You need to specify a build directory."), false); return; } if(st & (BuildDirCreated | CorrectBuildDir)) { setStatus(i18n("Using an already created build directory."), true); m_chooserUi->installPrefix->setEnabled(false); m_chooserUi->buildType->setEnabled(false); } else { bool correct = (dirEmpty || !dirExists) && !(st & DirAlreadyCreated) && !dirRelative; if(correct) { st |= CorrectBuildDir; setStatus(i18n("Creating a new build directory."), true); } else { //Useful to explain what's going wrong if(st & DirAlreadyCreated) setStatus(i18n("Build directory already configured."), false); else if (!srcDir.isEmpty()) setStatus(i18n("This build directory is for %1, " "but the project directory is %2.", srcDir, m_srcFolder.toLocalFile()), false); else if(dirRelative) setStatus(i18n("You may not select a relative build directory."), false); else if(!dirEmpty) setStatus(i18n("The selected build directory is not empty."), false); } m_chooserUi->installPrefix->setEnabled(correct); m_chooserUi->buildType->setEnabled(correct); } } void CMakeBuildDirChooser::setCMakeBinary(const Path& path) { m_chooserUi->cmakeBin->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setInstallPrefix(const Path& path) { m_chooserUi->installPrefix->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setBuildFolder(const Path& path) { m_chooserUi->buildFolder->setUrl(path.toUrl()); updated(); } void CMakeBuildDirChooser::setBuildType(const QString& s) { m_chooserUi->buildType->addItem(s); m_chooserUi->buildType->setCurrentIndex(m_chooserUi->buildType->findText(s)); updated(); } void CMakeBuildDirChooser::setAlreadyUsed (const QStringList & used) { m_alreadyUsed = used; updated(); } void CMakeBuildDirChooser::setExtraArguments(const QString& args) { m_chooserUi->extraArguments->setEditText(args); updated(); } void CMakeBuildDirChooser::setStatus(const QString& message, bool canApply) { KColorScheme scheme(QPalette::Normal); KColorScheme::ForegroundRole role; if (canApply) { role = KColorScheme::PositiveText; } else { role = KColorScheme::NegativeText; } m_chooserUi->status->setText(QString("%2").arg(scheme.foreground(role).color().name()).arg(message)); auto okButton = m_buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(canApply); if (canApply) { auto cancelButton = m_buttonBox->button(QDialogButtonBox::Cancel); cancelButton->clearFocus(); } } Path CMakeBuildDirChooser::cmakeBinary() const { return Path(m_chooserUi->cmakeBin->url()); } Path CMakeBuildDirChooser::installPrefix() const { return Path(m_chooserUi->installPrefix->url()); } Path CMakeBuildDirChooser::buildFolder() const { return Path(m_chooserUi->buildFolder->url()); } QString CMakeBuildDirChooser::buildType() const { return m_chooserUi->buildType->currentText(); } QString CMakeBuildDirChooser::extraArguments() const { return m_chooserUi->extraArguments->currentText(); } - -QStringList CMakeBuildDirChooser::extraArgumentsHistory() const -{ - QStringList list; - KComboBox* extraArguments = m_chooserUi->extraArguments; - if (!extraArguments->currentText().isEmpty()) { - list << extraArguments->currentText(); - } - for (int i = 0; i < qMin(maxExtraArgumentsInHistory, extraArguments->count()); ++i) { - if (!extraArguments->itemText(i).isEmpty() && - (extraArguments->currentText() != extraArguments->itemText(i))) { - list << extraArguments->itemText(i); - } - } - return list; -} - diff --git a/projectmanagers/cmake/cmakebuilddirchooser.h b/projectmanagers/cmake/cmakebuilddirchooser.h index 186cc248ae..6970b5cc37 100644 --- a/projectmanagers/cmake/cmakebuilddirchooser.h +++ b/projectmanagers/cmake/cmakebuilddirchooser.h @@ -1,89 +1,91 @@ /* KDevelop CMake Support * * Copyright 2007 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef CMAKEBUILDDIRCHOOSER_H #define CMAKEBUILDDIRCHOOSER_H #include #include #include +#include "cmakeextraargumentshistory.h" #include "cmakecommonexport.h" class QDialogButtonBox; namespace Ui { class CMakeBuildDirChooser; } class KDEVCMAKECOMMON_EXPORT CMakeBuildDirChooser : public QDialog { Q_OBJECT public: enum StatusType { BuildDirCreated = 1, CorrectProject = 2, BuildFolderEmpty = 4, HaveCMake = 8, CorrectBuildDir = 16, DirAlreadyCreated = 32 //Error message in case it's already configured }; Q_DECLARE_FLAGS( StatusTypes, StatusType ) explicit CMakeBuildDirChooser(QWidget* parent = nullptr); ~CMakeBuildDirChooser() override; KDevelop::Path cmakeBinary() const; KDevelop::Path installPrefix() const; KDevelop::Path buildFolder() const; QString buildType() const; QString extraArguments() const; void setCMakeBinary(const KDevelop::Path& path); void setInstallPrefix(const KDevelop::Path& path); void setBuildFolder(const KDevelop::Path& path); void setBuildType(const QString& buildType); void setSourceFolder( const KDevelop::Path& srcFolder ); void setAlreadyUsed(const QStringList& used); void setStatus(const QString& message, bool canApply); void setExtraArguments(const QString& args); private slots: void updated(); private: QStringList m_alreadyUsed; void buildDirSettings( const KDevelop::Path& buildDir, QString& srcDir, QString& installDir, QString& buildType); - QStringList extraArgumentsHistory() const; + + CMakeExtraArgumentsHistory* m_extraArgumentsHistory; Ui::CMakeBuildDirChooser* m_chooserUi; QDialogButtonBox* m_buttonBox; KDevelop::Path m_srcFolder; }; Q_DECLARE_OPERATORS_FOR_FLAGS( CMakeBuildDirChooser::StatusTypes ) #endif diff --git a/projectmanagers/cmake/cmakeextraargumentshistory.cpp b/projectmanagers/cmake/cmakeextraargumentshistory.cpp new file mode 100644 index 0000000000..72c1eef7ef --- /dev/null +++ b/projectmanagers/cmake/cmakeextraargumentshistory.cpp @@ -0,0 +1,71 @@ +/* KDevelop CMake Support + * + * Copyright 2016 René J.V. Bertin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include + +#include "cmakeextraargumentshistory.h" + +namespace { + +const int maxExtraArgumentsInHistory = 15; + +} + +CMakeExtraArgumentsHistory::CMakeExtraArgumentsHistory(KComboBox* widget) + : m_arguments(widget) +{ + if (m_arguments) { + KConfigGroup config = KSharedConfig::openConfig()->group("CMakeBuildDirChooser"); + QStringList lastExtraArguments = config.readEntry("LastExtraArguments", QStringList());; + m_arguments->addItem(""); + m_arguments->addItems(lastExtraArguments); + m_arguments->setInsertPolicy(QComboBox::InsertAtTop); + KCompletion *comp = m_arguments->completionObject(); + KComboBox::connect(m_arguments, static_cast(&KComboBox::returnPressed), + comp, static_cast(&KCompletion::addItem)); + comp->insertItems(lastExtraArguments); + } else { + qFatal("CMakeExtraArgumentsHistory initialised with invalid widget"); + } +} + +CMakeExtraArgumentsHistory::~CMakeExtraArgumentsHistory() +{ + KConfigGroup config = KSharedConfig::openConfig()->group("CMakeBuildDirChooser"); + config.writeEntry("LastExtraArguments", list()); + config.sync(); +} + +QStringList CMakeExtraArgumentsHistory::list() const +{ + QStringList list; + if (!m_arguments->currentText().isEmpty()) { + list << m_arguments->currentText(); + } + for (int i = 0; i < qMin(maxExtraArgumentsInHistory, m_arguments->count()); ++i) { + if (!m_arguments->itemText(i).isEmpty() && + (m_arguments->currentText() != m_arguments->itemText(i))) { + list << m_arguments->itemText(i); + } + } + return list; +} diff --git a/projectmanagers/cmake/cmakeextraargumentshistory.h b/projectmanagers/cmake/cmakeextraargumentshistory.h new file mode 100644 index 0000000000..347422d96f --- /dev/null +++ b/projectmanagers/cmake/cmakeextraargumentshistory.h @@ -0,0 +1,51 @@ +/* KDevelop CMake Support + * + * Copyright 2016 René J.V. Bertin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef CMAKEEXTRAARGUMENTS_H +#define CMAKEEXTRAARGUMENTS_H + +#include +#include + +#include + +#include "cmakecommonexport.h" + +namespace Ui { + class CMakeExtraArgumentsHistory; +} + +/** +* This class augments CMake's extra arguments widget with a history feature. +* @author René Bertin +*/ +class KDEVCMAKECOMMON_EXPORT CMakeExtraArgumentsHistory +{ + public: + CMakeExtraArgumentsHistory(KComboBox* widget); + ~CMakeExtraArgumentsHistory(); + + QStringList list() const; + + private: + KComboBox* m_arguments; +}; + +#endif diff --git a/projectmanagers/cmake/settings/cmakebuildsettings.ui b/projectmanagers/cmake/settings/cmakebuildsettings.ui index 96362dddca..0d5abbfc54 100644 --- a/projectmanagers/cmake/settings/cmakebuildsettings.ui +++ b/projectmanagers/cmake/settings/cmakebuildsettings.ui @@ -1,171 +1,250 @@ CMakeBuildSettings 0 0 605 - 537 + 547 0 0 0 0 Add 0 0 Remove Cache Values 75 true Comment: Show Advanced true false 0 0 Advanced QFormLayout::ExpandingFieldsGrow + + + + Installation Prefix + + + + + + + + + Build Type + + + + + + + true + + + + Debug + + + + + Release + + + + + RelWithDebInfo + + + + + MinSizeRel + + + + + + + + Extra Arguments + + + + + + + + 0 + 0 + + + + true + + + + Configure Environment - + 0 0 Select an environment to be used - + Show Advanced Values + + + + + + + CMake Binary + + + KComboBox QComboBox
kcombobox.h
+ + KUrlRequester + QWidget +
kurlrequester.h
+
KDevelop::EnvironmentSelectionWidget KComboBox
util/environmentselectionwidget.h
KDevelop::EnvironmentConfigureButton QToolButton
shell/environmentconfigurebutton.h
diff --git a/projectmanagers/cmake/settings/cmakepreferences.cpp b/projectmanagers/cmake/settings/cmakepreferences.cpp index 29e456bc3d..ab5b02cd03 100644 --- a/projectmanagers/cmake/settings/cmakepreferences.cpp +++ b/projectmanagers/cmake/settings/cmakepreferences.cpp @@ -1,367 +1,410 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * Copyright 2007-2008 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #include "cmakepreferences.h" #include #include #include #include #include #include #include +#include #include "ui_cmakebuildsettings.h" #include "cmakecachedelegate.h" #include "cmakebuilddirchooser.h" #include "../debug.h" #include #include #include #include #include #include #include #include #include using namespace KDevelop; CMakePreferences::CMakePreferences(IPlugin* plugin, const ProjectConfigOptions& options, QWidget* parent) : ConfigPage(plugin, nullptr, parent), m_project(options.project), m_currentModel(nullptr) { QVBoxLayout* l = new QVBoxLayout( this ); QWidget* w = new QWidget; m_prefsUi = new Ui::CMakeBuildSettings; m_prefsUi->setupUi( w ); l->addWidget( w ); m_prefsUi->addBuildDir->setIcon(QIcon::fromTheme( "list-add" )); m_prefsUi->removeBuildDir->setIcon(QIcon::fromTheme( "list-remove" )); m_prefsUi->addBuildDir->setText(QString()); m_prefsUi->removeBuildDir->setText(QString()); m_prefsUi->cacheList->setItemDelegate(new CMakeCacheDelegate(m_prefsUi->cacheList)); m_prefsUi->cacheList->setSelectionMode(QAbstractItemView::SingleSelection); m_prefsUi->cacheList->horizontalHeader()->setStretchLastSection(true); m_prefsUi->cacheList->verticalHeader()->hide(); + // configure the extraArguments widget to span the advanced box width but not + // expand the dialog to the width of the longest element in the argument history. + m_prefsUi->extraArguments->setMinimumWidth(dynamic_cast(m_prefsUi->extraArguments)->minimumSizeHint().width()); + m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_prefsUi->extraArguments); + connect(m_prefsUi->buildDirs, static_cast(&KComboBox::currentIndexChanged), this, &CMakePreferences::buildDirChanged); connect(m_prefsUi->showInternal, &QCheckBox::stateChanged, this, &CMakePreferences::showInternal); connect(m_prefsUi->addBuildDir, &QPushButton::pressed, this, &CMakePreferences::createBuildDir); connect(m_prefsUi->removeBuildDir, &QPushButton::pressed, this, &CMakePreferences::removeBuildDir); connect(m_prefsUi->showAdvanced, &QPushButton::toggled, this, &CMakePreferences::showAdvanced); connect(m_prefsUi->environment, &EnvironmentSelectionWidget::currentProfileChanged, this, &CMakePreferences::changed); connect(m_prefsUi->configureEnvironment, &EnvironmentConfigureButton::environmentConfigured, this, &CMakePreferences::changed); + connect(m_prefsUi->installationPrefix, &KUrlRequester::textChanged, + this, &CMakePreferences::changed); + connect(m_prefsUi->buildType, static_cast(&QComboBox::currentIndexChanged), + this, &CMakePreferences::changed); + connect(m_prefsUi->buildType, &QComboBox::currentTextChanged, + this, &CMakePreferences::changed); + connect(m_prefsUi->extraArguments, &KComboBox::currentTextChanged, + this, &CMakePreferences::changed); + connect(m_prefsUi->extraArguments, &KComboBox::editTextChanged, + this, &CMakePreferences::changed); + connect(m_prefsUi->cMakeBinary, &KUrlRequester::textChanged, + this, &CMakePreferences::changed); + showInternal(m_prefsUi->showInternal->checkState()); m_subprojFolder = Path(options.projectTempFile).parent(); qCDebug(CMAKE) << "Source folder: " << m_srcFolder << options.projectTempFile; // foreach(const QVariant &v, args) // { // qCDebug(CMAKE) << "arg: " << v.toString(); // } m_prefsUi->configureEnvironment->setSelectionWidget(m_prefsUi->environment); m_prefsUi->showAdvanced->setChecked(false); showAdvanced(false); reset(); // load the initial values } CMakePreferences::~CMakePreferences() { CMake::removeOverrideBuildDirIndex(m_project); + delete m_extraArgumentsHistory; delete m_prefsUi; } +void CMakePreferences::initAdvanced() +{ + m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment(m_project) ); + m_prefsUi->installationPrefix->setText(CMake::currentInstallDir(m_project).toLocalFile()); + const QString buildType = CMake::currentBuildType(m_project); + if (m_prefsUi->buildType->findText(buildType) == -1) { + m_prefsUi->buildType->addItem(buildType); + } + m_prefsUi->buildType->setCurrentIndex(m_prefsUi->buildType->findText(buildType)); + m_prefsUi->extraArguments->setEditText(CMake::currentExtraArguments(m_project)); + m_prefsUi->cMakeBinary->setText(CMake::currentCMakeBinary(m_project).toLocalFile()); +} + void CMakePreferences::reset() { qCDebug(CMAKE) << "********loading"; m_prefsUi->buildDirs->clear(); m_prefsUi->buildDirs->addItems( CMake::allBuildDirs(m_project) ); CMake::removeOverrideBuildDirIndex(m_project); // addItems() triggers buildDirChanged(), compensate for it m_prefsUi->buildDirs->setCurrentIndex( CMake::currentBuildDirIndex(m_project) ); - m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment(m_project) ); + + initAdvanced(); m_srcFolder = m_project->path(); m_prefsUi->removeBuildDir->setEnabled(m_prefsUi->buildDirs->count()!=0); // QString cmDir=group.readEntry("CMakeDirectory"); // m_prefsUi->kcfg_cmakeDir->setUrl(QUrl(cmDir)); // qCDebug(CMAKE) << "cmakedir" << cmDir; } void CMakePreferences::apply() { qCDebug(CMAKE) << "*******saving"; // the build directory list is incrementally maintained through createBuildDir() and removeBuildDir(). // We won't rewrite it here based on the data from m_prefsUi->buildDirs. CMake::removeOverrideBuildDirIndex( m_project, true ); // save current selection int savedBuildDir = CMake::currentBuildDirIndex(m_project); if (savedBuildDir < 0) { // no build directory exists: skip any writing to config file as well as configuring return; } CMake::setCurrentEnvironment( m_project, m_prefsUi->environment->currentProfile() ); + CMake::setCurrentInstallDir( m_project, Path(m_prefsUi->installationPrefix->text()) ); + const QString buildType = m_prefsUi->buildType->currentText(); + if (m_prefsUi->buildType->findText(buildType) == -1) { + m_prefsUi->buildType->addItem(buildType); + } + CMake::setCurrentBuildType( m_project, buildType ); + CMake::setCurrentExtraArguments( m_project, m_prefsUi->extraArguments->currentText() ); + CMake::setCurrentCMakeBinary( m_project, Path(m_prefsUi->cMakeBinary->text()) ); + qCDebug(CMAKE) << "writing to cmake config: using builddir " << CMake::currentBuildDirIndex(m_project); qCDebug(CMAKE) << "writing to cmake config: builddir path " << CMake::currentBuildDir(m_project); qCDebug(CMAKE) << "writing to cmake config: installdir " << CMake::currentInstallDir(m_project); qCDebug(CMAKE) << "writing to cmake config: build type " << CMake::currentBuildType(m_project); qCDebug(CMAKE) << "writing to cmake config: cmake binary " << CMake::currentCMakeBinary(m_project); qCDebug(CMAKE) << "writing to cmake config: environment " << CMake::currentEnvironment(m_project); //We run cmake on the builddir to generate it configure(); } void CMakePreferences::defaults() { // do nothing } void CMakePreferences::configureCacheView() { // Sets up the cache view after model re-creation/reset. // Emits changed(false) because model re-creation probably means // mass programmatical invocation of itemChanged(), which invokes changed(true) - which is not what we want. m_prefsUi->cacheList->setModel(m_currentModel); m_prefsUi->cacheList->hideColumn(1); m_prefsUi->cacheList->hideColumn(3); m_prefsUi->cacheList->hideColumn(4); m_prefsUi->cacheList->horizontalHeader()->resizeSection(0, 200); if( m_currentModel ) { m_prefsUi->cacheList->setEnabled( true ); foreach(const QModelIndex & idx, m_currentModel->persistentIndices()) { m_prefsUi->cacheList->openPersistentEditor(idx); } } else { m_prefsUi->cacheList->setEnabled( false ); } showInternal(m_prefsUi->showInternal->checkState()); } void CMakePreferences::updateCache(const Path &newBuildDir) { const Path file = newBuildDir.isValid() ? Path(newBuildDir, "CMakeCache.txt") : Path(); if(QFile::exists(file.toLocalFile())) { if (m_currentModel) { m_currentModel->deleteLater(); } m_currentModel = new CMakeCacheModel(this, file); configureCacheView(); connect(m_currentModel, &CMakeCacheModel::itemChanged, this, &CMakePreferences::cacheEdited); connect(m_currentModel, &CMakeCacheModel::modelReset, this, &CMakePreferences::configureCacheView); connect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, &CMakePreferences::listSelectionChanged); } else { disconnect(m_prefsUi->cacheList->selectionModel(), &QItemSelectionModel::currentChanged, this, nullptr); if (m_currentModel) { m_currentModel->deleteLater(); m_currentModel = nullptr; } configureCacheView(); } if( !m_currentModel ) emit changed(); } void CMakePreferences::listSelectionChanged(const QModelIndex & index, const QModelIndex& ) { qCDebug(CMAKE) << "item " << index << " selected"; QModelIndex idx = index.sibling(index.row(), 3); QModelIndex idxType = index.sibling(index.row(), 1); QString comment=QString("%1. %2") .arg(m_currentModel->itemFromIndex(idxType)->text()) .arg(m_currentModel->itemFromIndex(idx)->text()); m_prefsUi->commentText->setText(comment); } void CMakePreferences::showInternal(int state) { if(!m_currentModel) return; bool showAdv=(state == Qt::Checked); for(int i=0; irowCount(); i++) { bool hidden=m_currentModel->isInternal(i) || (!showAdv && m_currentModel->isAdvanced(i)); m_prefsUi->cacheList->setRowHidden(i, hidden); } } void CMakePreferences::buildDirChanged(int index) { CMake::setOverrideBuildDirIndex( m_project, index ); const Path buildDir = CMake::currentBuildDir(m_project); - m_prefsUi->environment->setCurrentProfile( CMake::currentEnvironment( m_project ) ); + initAdvanced(); updateCache(buildDir); qCDebug(CMAKE) << "builddir Changed" << buildDir; emit changed(); } void CMakePreferences::cacheUpdated() { const Path buildDir = CMake::currentBuildDir(m_project); updateCache(buildDir); qCDebug(CMAKE) << "cache updated for" << buildDir; } void CMakePreferences::createBuildDir() { CMakeBuildDirChooser bdCreator; bdCreator.setSourceFolder( m_srcFolder ); // NOTE: (on removing the trailing slashes) // Generally, we have no clue about how shall a trailing slash look in the current system. // Moreover, the slash may be a part of the filename. // It may be '/' or '\', so maybe should we rely on CMake::allBuildDirs() for returning well-formed pathes? QStringList used = CMake::allBuildDirs( m_project ); bdCreator.setAlreadyUsed(used); bdCreator.setCMakeBinary(Path(CMake::findExecutable())); if(bdCreator.exec()) { QString newbuilddir = bdCreator.buildFolder().toLocalFile(); m_prefsUi->buildDirs->addItem(newbuilddir); int buildDirCount = m_prefsUi->buildDirs->count(); int addedBuildDirIndex = buildDirCount - 1; m_prefsUi->buildDirs->setCurrentIndex(addedBuildDirIndex); m_prefsUi->removeBuildDir->setEnabled(true); // Initialize the kconfig items with the values from the dialog, this ensures the settings // end up in the config file once the changes are saved qCDebug(CMAKE) << "adding to cmake config: new builddir index" << addedBuildDirIndex; qCDebug(CMAKE) << "adding to cmake config: builddir path " << bdCreator.buildFolder(); qCDebug(CMAKE) << "adding to cmake config: installdir " << bdCreator.installPrefix(); qCDebug(CMAKE) << "adding to cmake config: extra args" << bdCreator.extraArguments(); qCDebug(CMAKE) << "adding to cmake config: build type " << bdCreator.buildType(); qCDebug(CMAKE) << "adding to cmake config: cmake binary " << bdCreator.cmakeBinary(); qCDebug(CMAKE) << "adding to cmake config: environment empty"; CMake::setBuildDirCount( m_project, buildDirCount ); CMake::setCurrentBuildDir( m_project, bdCreator.buildFolder() ); CMake::setCurrentInstallDir( m_project, bdCreator.installPrefix() ); CMake::setCurrentExtraArguments( m_project, bdCreator.extraArguments() ); CMake::setCurrentBuildType( m_project, bdCreator.buildType() ); CMake::setCurrentCMakeBinary( m_project, bdCreator.cmakeBinary() ); CMake::setCurrentEnvironment( m_project, QString() ); qCDebug(CMAKE) << "Emitting changed signal for cmake kcm"; emit changed(); } //TODO: Save it for next runs } void CMakePreferences::removeBuildDir() { int curr=m_prefsUi->buildDirs->currentIndex(); if(curr < 0) return; Path removedPath = CMake::currentBuildDir( m_project ); QString removed = removedPath.toLocalFile(); if(QDir(removed).exists()) { KMessageBox::ButtonCode ret = KMessageBox::warningYesNo(this, i18n("The %1 directory is about to be removed in KDevelop's list.\n" "Do you want KDevelop to remove it in the file system as well?", removed)); if(ret == KMessageBox::Yes) { bool correct = KIO::del(removedPath.toUrl())->exec(); if(!correct) KMessageBox::error(this, i18n("Could not remove: %1", removed)); } } qCDebug(CMAKE) << "removing from cmake config: using builddir " << curr; qCDebug(CMAKE) << "removing from cmake config: builddir path " << removedPath; qCDebug(CMAKE) << "removing from cmake config: installdir " << CMake::currentInstallDir( m_project ); qCDebug(CMAKE) << "removing from cmake config: extra args" << CMake::currentExtraArguments( m_project ); qCDebug(CMAKE) << "removing from cmake config: buildtype " << CMake::currentBuildType( m_project ); qCDebug(CMAKE) << "removing from cmake config: cmake binary " << CMake::currentCMakeBinary( m_project ); qCDebug(CMAKE) << "removing from cmake config: environment " << CMake::currentEnvironment( m_project ); CMake::removeBuildDirConfig(m_project); m_prefsUi->buildDirs->removeItem( curr ); // this triggers buildDirChanged() if(m_prefsUi->buildDirs->count()==0) m_prefsUi->removeBuildDir->setEnabled(false); emit changed(); } void CMakePreferences::configure() { IProjectBuilder *b=m_project->buildSystemManager()->builder(); KJob* job=b->configure(m_project); if( m_currentModel ) { QVariantMap map = m_currentModel->changedValues(); job->setProperty("extraCMakeCacheValues", map); connect(job, &KJob::finished, m_currentModel, &CMakeCacheModel::reset); } else { connect(job, &KJob::finished, this, &CMakePreferences::cacheUpdated); } connect(job, &KJob::finished, m_project, &IProject::reloadModel); ICore::self()->runController()->registerJob(job); } void CMakePreferences::showAdvanced(bool v) { qCDebug(CMAKE) << "toggle pressed: " << v; m_prefsUi->advancedBox->setHidden(!v); } QString CMakePreferences::name() const { return i18n("CMake"); } QString CMakePreferences::fullName() const { return i18n("Configure CMake settings"); } QIcon CMakePreferences::icon() const { return QIcon::fromTheme("cmake"); } diff --git a/projectmanagers/cmake/settings/cmakepreferences.h b/projectmanagers/cmake/settings/cmakepreferences.h index d653927293..cc1dac3f9a 100644 --- a/projectmanagers/cmake/settings/cmakepreferences.h +++ b/projectmanagers/cmake/settings/cmakepreferences.h @@ -1,75 +1,78 @@ /* KDevelop CMake Support * * Copyright 2006 Matt Rogers * Copyright 2007 Aleix Pol * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ #ifndef CMAKEPREFERENCES_H #define CMAKEPREFERENCES_H #include "cmakecachemodel.h" +#include "cmakeextraargumentshistory.h" #include #include class QItemSelection; class CMakeSettings; namespace Ui { class CMakeBuildSettings; } /** * @author Matt Rogers * @author Aleix Pol */ class CMakePreferences : public KDevelop::ConfigPage { Q_OBJECT public: explicit CMakePreferences(KDevelop::IPlugin* plugin, const KDevelop::ProjectConfigOptions& options, QWidget* parent = nullptr); ~CMakePreferences() override; QString name() const override; QString fullName() const override; QIcon icon() const override; void apply() override; void reset() override; void defaults() override; private slots: void listSelectionChanged ( const QModelIndex& current, const QModelIndex& ); void showInternal(int state); void cacheEdited(QStandardItem * ) { emit changed(); } void buildDirChanged(int index); void cacheUpdated(); void createBuildDir(); void removeBuildDir(); void showAdvanced(bool v); void configureCacheView(); private: void configure(); + void initAdvanced(); KDevelop::IProject* m_project; KDevelop::Path m_srcFolder; KDevelop::Path m_subprojFolder; void updateCache( const KDevelop::Path & ); Ui::CMakeBuildSettings* m_prefsUi; CMakeCacheModel* m_currentModel; + CMakeExtraArgumentsHistory* m_extraArgumentsHistory; }; #endif