diff --git a/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp b/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp index 6edeb9b6f4..256727cfcc 100644 --- a/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp +++ b/kdevplatform/vcs/widgets/vcsdiffpatchsources.cpp @@ -1,329 +1,329 @@ /* Copyright 2009 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vcsdiffpatchsources.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vcsdiff.h" #include "vcsjob.h" #include "debug.h" using namespace KDevelop; VCSCommitDiffPatchSource::VCSCommitDiffPatchSource(VCSDiffUpdater* updater) : VCSDiffPatchSource(updater), m_vcs(updater->vcs()) { Q_ASSERT(m_vcs); m_commitMessageWidget = new QWidget; auto* layout = new QVBoxLayout(m_commitMessageWidget.data()); layout->setMargin(0); m_commitMessageEdit = new KTextEdit; m_commitMessageEdit.data()->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_commitMessageEdit.data()->setLineWrapMode(QTextEdit::NoWrap); m_vcs->setupCommitMessageEditor(updater->url(), m_commitMessageEdit.data()); auto* titleLayout = new QHBoxLayout; titleLayout->addWidget(new QLabel(i18n("Commit Message:"))); m_oldMessages = new KComboBox(m_commitMessageWidget.data()); m_oldMessages->addItem(i18n("Old Messages")); const auto oldMessages = this->oldMessages(); for (const QString& message : oldMessages) { m_oldMessages->addItem(message, message); } m_oldMessages->setMaximumWidth(200); - connect(m_oldMessages, QOverload::of(&KComboBox::currentIndexChanged), + connect(m_oldMessages, &QComboBox::currentTextChanged, this, &VCSCommitDiffPatchSource::oldMessageChanged); titleLayout->addWidget(m_oldMessages); layout->addLayout(titleLayout); layout->addWidget(m_commitMessageEdit.data()); connect(this, &VCSCommitDiffPatchSource::reviewCancelled, this, &VCSCommitDiffPatchSource::addMessageToHistory); connect(this, &VCSCommitDiffPatchSource::reviewFinished, this, &VCSCommitDiffPatchSource::addMessageToHistory); } QStringList VCSCommitDiffPatchSource::oldMessages() const { KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); return vcsGroup.readEntry("OldCommitMessages", QStringList()); } void VCSCommitDiffPatchSource::addMessageToHistory(const QString& message) { if(ICore::self()->shuttingDown()) return; KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); const int maxMessages = 10; QStringList oldMessages = vcsGroup.readEntry("OldCommitMessages", QStringList()); oldMessages.removeAll(message); oldMessages.push_front(message); oldMessages = oldMessages.mid(0, maxMessages); vcsGroup.writeEntry("OldCommitMessages", oldMessages); } void VCSCommitDiffPatchSource::oldMessageChanged(const QString& text) { if(m_oldMessages->currentIndex() != 0) { m_oldMessages->setCurrentIndex(0); m_commitMessageEdit.data()->setText(text); } } void VCSCommitDiffPatchSource::jobFinished(KJob *job) { if (!job || job->error() != 0 ) { QString details = job ? job->errorText() : QString(); if (details.isEmpty()) { //errorText may be empty details = i18n("For more detailed information please see the Version Control tool view."); } KMessageBox::detailedError(nullptr, i18n("Unable to commit"), details, i18n("Commit unsuccessful")); } deleteLater(); } VCSDiffPatchSource::VCSDiffPatchSource(VCSDiffUpdater* updater) : m_updater(updater) { update(); KDevelop::IBasicVersionControl* vcs = m_updater->vcs(); QUrl url = m_updater->url(); QScopedPointer statusJob(vcs->status(QList() << url)); QVariant varlist; if( statusJob->exec() && statusJob->status() == VcsJob::JobSucceeded ) { varlist = statusJob->fetchResults(); const auto vars = varlist.toList(); m_infos.reserve(m_infos.size() + vars.size()); for (const auto& var : vars) { VcsStatusInfo info = var.value(); m_infos += info; if(info.state()!=VcsStatusInfo::ItemUpToDate) m_selectable[info.url()] = info.state(); } } else qCDebug(VCS) << "Couldn't get status for urls: " << url; } VCSDiffPatchSource::VCSDiffPatchSource(const KDevelop::VcsDiff& diff) : m_updater(nullptr) { updateFromDiff(diff); } VCSDiffPatchSource::~VCSDiffPatchSource() { QFile::remove(m_file.toLocalFile()); delete m_updater; } QUrl VCSDiffPatchSource::baseDir() const { return m_base; } QUrl VCSDiffPatchSource::file() const { return m_file; } QString VCSDiffPatchSource::name() const { return m_name; } uint VCSDiffPatchSource::depth() const { return m_depth; } void VCSDiffPatchSource::updateFromDiff(const VcsDiff& vcsdiff) { if(!m_file.isValid()) { QTemporaryFile temp2(QDir::tempPath() + QLatin1String("/kdevelop_XXXXXX.patch")); temp2.setAutoRemove(false); temp2.open(); QTextStream t2(&temp2); t2 << vcsdiff.diff(); qCDebug(VCS) << "filename:" << temp2.fileName(); m_file = QUrl::fromLocalFile(temp2.fileName()); temp2.close(); }else{ QFile file(m_file.path()); file.open(QIODevice::WriteOnly); QTextStream t2(&file); t2 << vcsdiff.diff(); } qCDebug(VCS) << "using file" << m_file << vcsdiff.diff() << "base" << vcsdiff.baseDiff(); m_name = QStringLiteral("VCS Diff"); m_base = vcsdiff.baseDiff(); m_depth = vcsdiff.depth(); emit patchChanged(); } void VCSDiffPatchSource::update() { if(!m_updater) return; updateFromDiff(m_updater->update()); } VCSCommitDiffPatchSource::~VCSCommitDiffPatchSource() { delete m_commitMessageWidget.data(); } bool VCSCommitDiffPatchSource::canSelectFiles() const { return true; } QMap< QUrl, KDevelop::VcsStatusInfo::State> VCSDiffPatchSource::additionalSelectableFiles() const { return m_selectable; } QWidget* VCSCommitDiffPatchSource::customWidget() const { return m_commitMessageWidget.data(); } QString VCSCommitDiffPatchSource::finishReviewCustomText() const { return i18nc("@action:button To make a commit", "Commit"); } bool VCSCommitDiffPatchSource::canCancel() const { return true; } void VCSCommitDiffPatchSource::cancelReview() { QString message; if (m_commitMessageEdit) message = m_commitMessageEdit.data()->toPlainText(); emit reviewCancelled(message); deleteLater(); } bool VCSCommitDiffPatchSource::finishReview(const QList& selection) { QString message; if (m_commitMessageEdit) message = m_commitMessageEdit.data()->toPlainText(); qCDebug(VCS) << "Finishing with selection" << selection; QString files; files.reserve(selection.size()); for (const QUrl& url : selection) { files += QLatin1String("
  • ") + ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain) + QLatin1String("
  • "); } QString text = i18n("Files will be committed:\n
      %1
    \nWith message:\n
    %2
    ", files, message); int res = KMessageBox::warningContinueCancel(nullptr, text, i18n("About to commit to repository"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("ShouldAskConfirmCommit")); if (res != KMessageBox::Continue) { return false; } emit reviewFinished(message, selection); VcsJob* job = m_vcs->commit(message, selection, KDevelop::IBasicVersionControl::NonRecursive); if (!job) { return false; } connect (job, &VcsJob::finished, this, &VCSCommitDiffPatchSource::jobFinished); ICore::self()->runController()->registerJob(job); return true; } bool showVcsDiff(IPatchSource* vcsDiff) { auto* patchReview = ICore::self()->pluginController()->extensionForPlugin(QStringLiteral("org.kdevelop.IPatchReview")); if( patchReview ) { patchReview->startReview(vcsDiff); return true; } else { qCWarning(VCS) << "Patch review plugin not found"; return false; } } VcsDiff VCSStandardDiffUpdater::update() const { QScopedPointer diffJob(m_vcs->diff(m_url, KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Base), KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Working))); const bool success = diffJob ? diffJob->exec() : false; if (!success) { KMessageBox::error(nullptr, i18n("Could not create a patch for the current version.")); return {}; } return diffJob->fetchResults().value(); } VCSStandardDiffUpdater::VCSStandardDiffUpdater(IBasicVersionControl* vcs, const QUrl& url) : m_vcs(vcs) , m_url(url) { } VCSStandardDiffUpdater::~VCSStandardDiffUpdater() { } VCSDiffUpdater::~VCSDiffUpdater() { } diff --git a/plugins/cmake/cmakebuilddirchooser.cpp b/plugins/cmake/cmakebuilddirchooser.cpp index 2471b36f8d..42ea5c9129 100644 --- a/plugins/cmake/cmakebuilddirchooser.cpp +++ b/plugins/cmake/cmakebuilddirchooser.cpp @@ -1,351 +1,351 @@ /* 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 #include using namespace KDevelop; CMakeBuildDirChooser::CMakeBuildDirChooser(QWidget* parent) : QDialog(parent) { setWindowTitle(i18n("Configure a build directory - %1", ICore::self()->runtimeController()->currentRuntime()->name())); 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); setShowAvailableBuildDirs(false); mainLayout->addWidget(m_buttonBox); m_chooserUi->buildFolder->setMode(KFile::Directory|KFile::ExistingOnly); m_chooserUi->installPrefix->setMode(KFile::Directory|KFile::ExistingOnly); // configure the extraArguments widget to span the widget width but not // expand the dialog to the width of the longest element in the argument history. // static_cast needed because KComboBox::minimumSizeHint() override by mistake made it protected m_chooserUi->extraArguments->setMinimumWidth(static_cast(m_chooserUi->extraArguments)->minimumSizeHint().width()); m_extraArgumentsHistory = new CMakeExtraArgumentsHistory(m_chooserUi->extraArguments); connect(m_chooserUi->buildFolder, &KUrlRequester::textChanged, this, &CMakeBuildDirChooser::updated); - connect(m_chooserUi->buildType, QOverload::of(&QComboBox::currentIndexChanged), + connect(m_chooserUi->buildType, &QComboBox::currentTextChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->extraArguments, &KComboBox::editTextChanged, this, &CMakeBuildDirChooser::updated); connect(m_chooserUi->availableBuildDirs, QOverload::of(&QComboBox::currentIndexChanged), this, &CMakeBuildDirChooser::adoptPreviousBuildDirectory); const auto defaultInstallPrefix = ICore::self()->runtimeController()->currentRuntime()->getenv("KDEV_DEFAULT_INSTALL_PREFIX"); if (!defaultInstallPrefix.isEmpty()) { m_chooserUi->installPrefix->setUrl(QUrl::fromLocalFile(QFile::decodeName(defaultInstallPrefix))); } updated(); } CMakeBuildDirChooser::~CMakeBuildDirChooser() { delete m_extraArgumentsHistory; delete m_chooserUi; } void CMakeBuildDirChooser::setProject( IProject* project ) { m_project = project; KDevelop::Path folder = m_project->path(); QString relative=CMake::projectRootRelative(m_project); folder.cd(relative); m_srcFolder = folder; m_chooserUi->buildFolder->setUrl(KDevelop::proposedBuildFolder(m_srcFolder).toUrl()); setWindowTitle(i18n("Configure a build directory for %1", project->name())); update(); } void CMakeBuildDirChooser::buildDirSettings( const KDevelop::Path& buildDir, QString& srcDir, QString& installDir, QString& buildType) { const QByteArray srcLine = QByteArrayLiteral("CMAKE_HOME_DIRECTORY:INTERNAL="); const QByteArray installLine = QByteArrayLiteral("CMAKE_INSTALL_PREFIX:PATH="); const QByteArray buildLine = QByteArrayLiteral("CMAKE_BUILD_TYPE:STRING="); const Path cachePath(buildDir, QStringLiteral("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()) { const auto rawLine = file.readLine(); auto match = [&rawLine](const QByteArray& prefix, QString* target) -> bool { if (rawLine.startsWith(prefix)) { // note: CMakeCache.txt is UTF8-encoded, also see bug 329305 *target = QString::fromUtf8(rawLine.constData() + prefix.size(), rawLine.size() - prefix.size()); return true; } return false; }; if (match(srcLine, &srcDir) || match(installLine, &installDir) || match(buildLine, &buildType)) { ++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() { StatusTypes st; 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, QStringLiteral("CMakeCache.txt")).toLocalFile()); if(hasCache) { QString proposed=m_srcFolder.toLocalFile(); buildDirSettings(chosenBuildFolder, srcDir, installDir, buildType); if(!srcDir.isEmpty()) { auto rt = ICore::self()->runtimeController()->currentRuntime(); if(QDir(rt->pathInHost(Path(srcDir)).toLocalFile()).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()) && !m_chooserUi->availableBuildDirs->isEnabled()) { 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::setCMakeExecutable(const Path& path) { m_chooserUi->cmakeExecutable->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_chooserUi->availableBuildDirs->addItems(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(QStringLiteral("%2").arg(scheme.foreground(role).color().name(), message)); auto okButton = m_buttonBox->button(QDialogButtonBox::Ok); okButton->setEnabled(canApply); if (canApply) { auto cancelButton = m_buttonBox->button(QDialogButtonBox::Cancel); cancelButton->clearFocus(); } } void CMakeBuildDirChooser::adoptPreviousBuildDirectory(int index) { if (index > 0) { Q_ASSERT(m_project); m_chooserUi->cmakeExecutable->setUrl(CMake::currentCMakeExecutable(m_project, index -1).toUrl()); m_chooserUi->buildFolder->setUrl(CMake::currentBuildDir(m_project, index -1).toUrl()); m_chooserUi->installPrefix->setUrl(CMake::currentInstallDir(m_project, index -1).toUrl()); m_chooserUi->buildType->setCurrentText(CMake::currentBuildType(m_project, index -1)); m_chooserUi->extraArguments->setCurrentText(CMake::currentExtraArguments(m_project, index -1)); } m_chooserUi->label_5->setEnabled(index == 0); m_chooserUi->cmakeExecutable->setEnabled(index == 0); m_chooserUi->label_3->setEnabled(index == 0); m_chooserUi->buildFolder->setEnabled(index == 0); m_chooserUi->label->setEnabled(index == 0); m_chooserUi->installPrefix->setEnabled(index == 0); m_chooserUi->label_2->setEnabled(index == 0); m_chooserUi->buildType->setEnabled(index == 0); m_chooserUi->status->setEnabled(index == 0); m_chooserUi->extraArguments->setEnabled(index == 0); m_chooserUi->label_4->setEnabled(index == 0); } bool CMakeBuildDirChooser::reuseBuilddir() { return m_chooserUi->availableBuildDirs->currentIndex() > 0; } int CMakeBuildDirChooser::alreadyUsedIndex() const { return m_chooserUi->availableBuildDirs->currentIndex() - 1; } void CMakeBuildDirChooser::setShowAvailableBuildDirs(bool show) { m_chooserUi->availableLabel->setVisible(show); m_chooserUi->availableBuildDirs->setVisible(show); } Path CMakeBuildDirChooser::cmakeExecutable() const { return Path(m_chooserUi->cmakeExecutable->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(); } diff --git a/plugins/execute/nativeappconfig.cpp b/plugins/execute/nativeappconfig.cpp index 0f174ebd6a..7e9c5d0c88 100644 --- a/plugins/execute/nativeappconfig.cpp +++ b/plugins/execute/nativeappconfig.cpp @@ -1,392 +1,392 @@ /* This file is part of KDevelop Copyright 2009 Andreas Pakulat Copyright 2010 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "nativeappconfig.h" #include #include #include #include #include #include "nativeappjob.h" #include #include #include #include #include "executeplugin.h" #include "debug.h" #include #include "projecttargetscombobox.h" #include #include #include #include #include #include using namespace KDevelop; QIcon NativeAppConfigPage::icon() const { return QIcon::fromTheme(QStringLiteral("system-run")); } static KDevelop::ProjectBaseItem* itemForPath(const QStringList& path, KDevelop::ProjectModel* model) { return model->itemFromIndex(model->pathToIndex(path)); } //TODO: Make sure to auto-add the executable target to the dependencies when its used. void NativeAppConfigPage::loadFromConfiguration(const KConfigGroup& cfg, KDevelop::IProject* project ) { QSignalBlocker blocker(this); projectTarget->setBaseItem( project ? project->projectItem() : nullptr, true); projectTarget->setCurrentItemPath( cfg.readEntry( ExecutePlugin::projectTargetEntry, QStringList() ) ); QUrl exe = cfg.readEntry( ExecutePlugin::executableEntry, QUrl()); if( !exe.isEmpty() || project ){ executablePath->setUrl( !exe.isEmpty() ? exe : project->path().toUrl() ); }else{ KDevelop::IProjectController* pc = KDevelop::ICore::self()->projectController(); if( pc ){ executablePath->setUrl( pc->projects().isEmpty() ? QUrl() : pc->projects().at(0)->path().toUrl() ); } } dependencies->setSuggestion(project); //executablePath->setFilter("application/x-executable"); executableRadio->setChecked( true ); if ( !cfg.readEntry( ExecutePlugin::isExecutableEntry, false ) && projectTarget->count() ){ projectTargetRadio->setChecked( true ); } arguments->setClearButtonEnabled( true ); arguments->setText( cfg.readEntry( ExecutePlugin::argumentsEntry, "" ) ); workingDirectory->setUrl( cfg.readEntry( ExecutePlugin::workingDirEntry, QUrl() ) ); environment->setCurrentProfile(cfg.readEntry(ExecutePlugin::environmentProfileEntry, QString())); runInTerminal->setChecked( cfg.readEntry( ExecutePlugin::useTerminalEntry, false ) ); terminal->setEditText( cfg.readEntry( ExecutePlugin::terminalEntry, terminal->itemText(0) ) ); dependencies->setDependencies(KDevelop::stringToQVariant( cfg.readEntry( ExecutePlugin::dependencyEntry, QString() ) ).toList()); dependencyAction->setCurrentIndex( dependencyAction->findData( cfg.readEntry( ExecutePlugin::dependencyActionEntry, "Nothing" ) ) ); } NativeAppConfigPage::NativeAppConfigPage( QWidget* parent ) : LaunchConfigurationPage( parent ) { setupUi(this); //Setup data info for combobox dependencyAction->setItemData(0, QStringLiteral("Nothing")); dependencyAction->setItemData(1, QStringLiteral("Build")); dependencyAction->setItemData(2, QStringLiteral("Install")); dependencyAction->setItemData(3, QStringLiteral("SudoInstall")); //Set workingdirectory widget to ask for directories rather than files workingDirectory->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); configureEnvironment->setSelectionWidget(environment); //connect signals to changed signal - connect( projectTarget, QOverload::of(&ProjectTargetsComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); + connect( projectTarget, &QComboBox::currentTextChanged, this, &NativeAppConfigPage::changed ); connect( projectTargetRadio, &QRadioButton::toggled, this, &NativeAppConfigPage::changed ); connect( executableRadio, &QRadioButton::toggled, this, &NativeAppConfigPage::changed ); connect( executablePath->lineEdit(), &KLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( executablePath, &KUrlRequester::urlSelected, this, &NativeAppConfigPage::changed ); connect( arguments, &QLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( workingDirectory, &KUrlRequester::urlSelected, this, &NativeAppConfigPage::changed ); connect( workingDirectory->lineEdit(), &KLineEdit::textEdited, this, &NativeAppConfigPage::changed ); connect( environment, &EnvironmentSelectionWidget::currentProfileChanged, this, &NativeAppConfigPage::changed ); connect( dependencyAction, QOverload::of(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); connect( runInTerminal, &QCheckBox::toggled, this, &NativeAppConfigPage::changed ); connect( terminal, &KComboBox::editTextChanged, this, &NativeAppConfigPage::changed ); connect( terminal, QOverload::of(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::changed ); connect( dependencyAction, QOverload::of(&KComboBox::currentIndexChanged), this, &NativeAppConfigPage::activateDeps ); connect( dependencies, &DependenciesWidget::changed, this, &NativeAppConfigPage::changed ); } void NativeAppConfigPage::activateDeps( int idx ) { dependencies->setEnabled( dependencyAction->itemData( idx ).toString() != QLatin1String("Nothing") ); } void NativeAppConfigPage::saveToConfiguration( KConfigGroup cfg, KDevelop::IProject* project ) const { Q_UNUSED( project ); cfg.writeEntry( ExecutePlugin::isExecutableEntry, executableRadio->isChecked() ); cfg.writeEntry( ExecutePlugin::executableEntry, executablePath->url() ); cfg.writeEntry( ExecutePlugin::projectTargetEntry, projectTarget->currentItemPath() ); cfg.writeEntry( ExecutePlugin::argumentsEntry, arguments->text() ); cfg.writeEntry( ExecutePlugin::workingDirEntry, workingDirectory->url() ); cfg.writeEntry( ExecutePlugin::environmentProfileEntry, environment->currentProfile() ); cfg.writeEntry( ExecutePlugin::useTerminalEntry, runInTerminal->isChecked() ); cfg.writeEntry( ExecutePlugin::terminalEntry, terminal->currentText() ); cfg.writeEntry( ExecutePlugin::dependencyActionEntry, dependencyAction->itemData( dependencyAction->currentIndex() ).toString() ); QVariantList deps = dependencies->dependencies(); cfg.writeEntry( ExecutePlugin::dependencyEntry, KDevelop::qvariantToString( QVariant( deps ) ) ); } QString NativeAppConfigPage::title() const { return i18n("Configure Native Application"); } QList< KDevelop::LaunchConfigurationPageFactory* > NativeAppLauncher::configPages() const { return QList(); } QString NativeAppLauncher::description() const { return i18n("Executes Native Applications"); } QString NativeAppLauncher::id() { return QStringLiteral("nativeAppLauncher"); } QString NativeAppLauncher::name() const { return i18n("Native Application"); } NativeAppLauncher::NativeAppLauncher() { } KJob* NativeAppLauncher::start(const QString& launchMode, KDevelop::ILaunchConfiguration* cfg) { Q_ASSERT(cfg); if( !cfg ) { return nullptr; } if( launchMode == QLatin1String("execute") ) { auto* iface = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IExecutePlugin"), QStringLiteral("kdevexecute"))->extension(); Q_ASSERT(iface); KJob* depjob = iface->dependencyJob( cfg ); QList l; if( depjob ) { l << depjob; } l << new NativeAppJob( KDevelop::ICore::self()->runController(), cfg ); return new KDevelop::ExecuteCompositeJob( KDevelop::ICore::self()->runController(), l ); } qCWarning(PLUGIN_EXECUTE) << "Unknown launch mode " << launchMode << "for config:" << cfg->name(); return nullptr; } QStringList NativeAppLauncher::supportedModes() const { return QStringList() << QStringLiteral("execute"); } KDevelop::LaunchConfigurationPage* NativeAppPageFactory::createWidget(QWidget* parent) { return new NativeAppConfigPage( parent ); } NativeAppPageFactory::NativeAppPageFactory() { } NativeAppConfigType::NativeAppConfigType() { factoryList.append( new NativeAppPageFactory() ); } NativeAppConfigType::~NativeAppConfigType() { qDeleteAll(factoryList); factoryList.clear(); } QString NativeAppConfigType::name() const { return i18n("Compiled Binary"); } QList NativeAppConfigType::configPages() const { return factoryList; } QString NativeAppConfigType::id() const { return ExecutePlugin::_nativeAppConfigTypeId; } QIcon NativeAppConfigType::icon() const { return QIcon::fromTheme(QStringLiteral("application-x-executable")); } bool NativeAppConfigType::canLaunch ( KDevelop::ProjectBaseItem* item ) const { if( item->target() && item->target()->executable() ) { return canLaunch( item->target()->executable()->builtUrl() ); } return false; } bool NativeAppConfigType::canLaunch ( const QUrl& file ) const { return ( file.isLocalFile() && QFileInfo( file.toLocalFile() ).isExecutable() ); } void NativeAppConfigType::configureLaunchFromItem ( KConfigGroup cfg, KDevelop::ProjectBaseItem* item ) const { cfg.writeEntry( ExecutePlugin::isExecutableEntry, false ); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); cfg.writeEntry( ExecutePlugin::projectTargetEntry, model->pathFromIndex( model->indexFromItem( item ) ) ); cfg.writeEntry( ExecutePlugin::workingDirEntry, item->executable()->builtUrl().adjusted(QUrl::RemoveFilename) ); cfg.sync(); } void NativeAppConfigType::configureLaunchFromCmdLineArguments ( KConfigGroup cfg, const QStringList& args ) const { cfg.writeEntry( ExecutePlugin::isExecutableEntry, true ); Q_ASSERT(QFile::exists(args.first())); // TODO: we probably want to flexibilize, but at least we won't be accepting wrong values anymore cfg.writeEntry( ExecutePlugin::executableEntry, QUrl::fromLocalFile(args.first()) ); QStringList a(args); a.removeFirst(); cfg.writeEntry( ExecutePlugin::argumentsEntry, KShell::joinArgs(a) ); cfg.sync(); } QList targetsInFolder(KDevelop::ProjectFolderItem* folder) { QList ret; const auto folders = folder->folderList(); for (KDevelop::ProjectFolderItem* f : folders) { ret += targetsInFolder(f); } ret += folder->targetList(); return ret; } bool actionLess(QAction* a, QAction* b) { return a->text() < b->text(); } bool menuLess(QMenu* a, QMenu* b) { return a->title() < b->title(); } QMenu* NativeAppConfigType::launcherSuggestions() { QMenu* ret = new QMenu(i18n("Project Executables")); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); const QList projects = KDevelop::ICore::self()->projectController()->projects(); for (KDevelop::IProject* project : projects) { if(project->projectFileManager()->features() & KDevelop::IProjectFileManager::Targets) { const QList targets = targetsInFolder(project->projectItem()); QHash > targetsContainer; QMenu* projectMenu = ret->addMenu(QIcon::fromTheme(QStringLiteral("project-development")), project->name()); for (KDevelop::ProjectTargetItem* target : targets) { if(target->executable()) { QStringList path = model->pathFromIndex(target->index()); if(!path.isEmpty()){ auto* act = new QAction(projectMenu); act->setData(KDevelop::joinWithEscaping(path, QLatin1Char('/'), QLatin1Char('\\'))); act->setProperty("name", target->text()); path.removeFirst(); act->setText(path.join(QLatin1Char('/'))); act->setIcon(QIcon::fromTheme(QStringLiteral("system-run"))); connect(act, &QAction::triggered, this, &NativeAppConfigType::suggestionTriggered); targetsContainer[target->parent()].append(act); } } } QList separateActions; QList submenus; for (auto it = targetsContainer.constBegin(), end = targetsContainer.constEnd(); it != end; ++it) { KDevelop::ProjectBaseItem* folder = it.key(); QList actions = it.value(); if(actions.size()==1 || !folder->parent()) { separateActions.append(actions); } else { for (QAction* a : qAsConst(actions)) { a->setText(a->property("name").toString()); } QStringList path = model->pathFromIndex(folder->index()); path.removeFirst(); QMenu* submenu = new QMenu(path.join(QLatin1Char('/')), projectMenu); std::sort(actions.begin(), actions.end(), actionLess); submenu->addActions(actions); submenus += submenu; } } std::sort(separateActions.begin(), separateActions.end(), actionLess); std::sort(submenus.begin(), submenus.end(), menuLess); for (QMenu* m : qAsConst(submenus)) { projectMenu->addMenu(m); } projectMenu->addActions(separateActions); projectMenu->setEnabled(!projectMenu->isEmpty()); } } return ret; } void NativeAppConfigType::suggestionTriggered() { auto* action = qobject_cast(sender()); KDevelop::ProjectModel* model = KDevelop::ICore::self()->projectController()->projectModel(); KDevelop::ProjectTargetItem* pitem = dynamic_cast(itemForPath(KDevelop::splitWithEscaping(action->data().toString(), QLatin1Char('/'), QLatin1Char('\\')), model)); if(pitem) { QPair launcher = qMakePair( launchers().at( 0 )->supportedModes().at(0), launchers().at( 0 )->id() ); KDevelop::IProject* p = pitem->project(); KDevelop::ILaunchConfiguration* config = KDevelop::ICore::self()->runController()->createLaunchConfiguration(this, launcher, p, pitem->text()); KConfigGroup cfg = config->config(); QStringList splitPath = model->pathFromIndex(pitem->index()); // QString path = KDevelop::joinWithEscaping(splitPath,'/','\\'); cfg.writeEntry( ExecutePlugin::projectTargetEntry, splitPath ); cfg.writeEntry( ExecutePlugin::dependencyEntry, KDevelop::qvariantToString( QVariantList() << splitPath ) ); cfg.writeEntry( ExecutePlugin::dependencyActionEntry, "Build" ); cfg.sync(); emit signalAddLaunchConfiguration(config); } } diff --git a/plugins/qmakebuilder/qmakebuilderpreferences.cpp b/plugins/qmakebuilder/qmakebuilderpreferences.cpp index ebaf342b7d..0be61249c3 100644 --- a/plugins/qmakebuilder/qmakebuilderpreferences.cpp +++ b/plugins/qmakebuilder/qmakebuilderpreferences.cpp @@ -1,162 +1,162 @@ /* KDevelop QMake Support * * Copyright 2007 Andreas Pakulat * Copyright 2014 Kevin Funk * * 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 "qmakebuilderpreferences.h" #include #include #include #include #include #include "ui_qmakeconfig.h" #include "qmakebuilddirchooser.h" #include "qmakebuilddirchooserdialog.h" #include "qmakeconfig.h" #include "qmakebuilderconfig.h" #include #include QMakeBuilderPreferences::QMakeBuilderPreferences(KDevelop::IPlugin* plugin, const KDevelop::ProjectConfigOptions& options, QWidget* parent) : KDevelop::ConfigPage(plugin, nullptr, parent) , m_project(options.project) { m_prefsUi = new Ui::QMakeConfig; m_prefsUi->setupUi(this); m_chooserUi = new QMakeBuildDirChooser(m_project); auto groupBoxLayout = new QVBoxLayout(m_prefsUi->groupBox); groupBoxLayout->addWidget(m_chooserUi); m_chooserUi->kcfg_buildDir->setEnabled(false); // build directory MUST NOT be changed here connect(m_chooserUi, &QMakeBuildDirChooser::changed, this, &QMakeBuilderPreferences::changed); connect(m_chooserUi, &QMakeBuildDirChooser::changed, this, &QMakeBuilderPreferences::validate); - connect(m_prefsUi->buildDirCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(loadOtherConfig(QString))); + connect(m_prefsUi->buildDirCombo, &QComboBox::currentTextChanged, this, &QMakeBuilderPreferences::loadOtherConfig); connect(m_prefsUi->buildDirCombo, QOverload::of(&QComboBox::currentIndexChanged), this, &QMakeBuilderPreferences::changed); connect(m_prefsUi->addButton, &QAbstractButton::pressed, this, &QMakeBuilderPreferences::addBuildConfig); connect(m_prefsUi->removeButton, &QAbstractButton::pressed, this, &QMakeBuilderPreferences::removeBuildConfig); reset(); // load initial values } QMakeBuilderPreferences::~QMakeBuilderPreferences() { // not a QObject ! delete m_chooserUi; } void QMakeBuilderPreferences::reset() { qCDebug(KDEV_QMAKEBUILDER) << "loading data"; // refresh combobox KConfigGroup cg(m_project->projectConfiguration(), QMakeConfig::CONFIG_GROUP); const QString buildPath = cg.readEntry(QMakeConfig::BUILD_FOLDER, QString()); // update build list (this will trigger loadOtherConfig if signals are still connected) - disconnect(m_prefsUi->buildDirCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(loadOtherConfig(QString))); + disconnect(m_prefsUi->buildDirCombo, &QComboBox::currentTextChanged, this, &QMakeBuilderPreferences::loadOtherConfig); m_prefsUi->buildDirCombo->clear(); m_prefsUi->buildDirCombo->insertItems(0, cg.groupList()); if (m_prefsUi->buildDirCombo->contains(buildPath)) { m_prefsUi->buildDirCombo->setCurrentItem(buildPath); m_chooserUi->loadConfig(buildPath); } qCDebug(KDEV_QMAKEBUILDER) << "Loaded" << cg.groupList() << buildPath; m_prefsUi->removeButton->setEnabled(m_prefsUi->buildDirCombo->count() > 1); - connect(m_prefsUi->buildDirCombo, SIGNAL(currentIndexChanged(QString)), this, SLOT(loadOtherConfig(QString))); + connect(m_prefsUi->buildDirCombo, &QComboBox::currentTextChanged, this, &QMakeBuilderPreferences::loadOtherConfig); validate(); } QString QMakeBuilderPreferences::name() const { return i18n("QMake"); } void QMakeBuilderPreferences::apply() { qCDebug(KDEV_QMAKEBUILDER) << "Saving data"; QString errormsg; if (m_chooserUi->validate(&errormsg)) { // data is valid: save, once in the build dir's data and also as current data m_chooserUi->saveConfig(); KConfigGroup config(m_project->projectConfiguration(), QMakeConfig::CONFIG_GROUP); m_chooserUi->saveConfig(config); config.writeEntry(QMakeConfig::BUILD_FOLDER, m_chooserUi->buildDir()); } else { // invalid data: message box KMessageBox::error(nullptr, errormsg, QStringLiteral("Data is invalid!")); // FIXME dialog behaves like if save really happened (dialog closes if user click ok) even if changed signal is // emitted } } void QMakeBuilderPreferences::validate() { m_chooserUi->validate(); } void QMakeBuilderPreferences::loadOtherConfig(const QString& config) { qCDebug(KDEV_QMAKEBUILDER) << "Loading config " << config; m_chooserUi->loadConfig(config); apply(); // since current config has changed, it must be saved immediately } void QMakeBuilderPreferences::addBuildConfig() { qCDebug(KDEV_QMAKEBUILDER) << "Adding a new config."; // for more simplicity, just launch regular dialog auto dlg = new QMakeBuildDirChooserDialog(m_project); if (dlg->exec() == QDialog::Accepted) { m_prefsUi->buildDirCombo->setCurrentItem(dlg->buildDir(), true); m_prefsUi->removeButton->setEnabled(m_prefsUi->buildDirCombo->count() > 1); // TODO run qmake } } void QMakeBuilderPreferences::removeBuildConfig() { qCDebug(KDEV_QMAKEBUILDER) << "Removing config" << m_prefsUi->buildDirCombo->currentText(); QString removed = m_prefsUi->buildDirCombo->currentText(); KConfigGroup cg(m_project->projectConfiguration(), QMakeConfig::CONFIG_GROUP); m_prefsUi->buildDirCombo->removeItem(m_prefsUi->buildDirCombo->currentIndex()); m_prefsUi->removeButton->setEnabled(m_prefsUi->buildDirCombo->count() > 1); cg.group(removed).deleteGroup(KConfigBase::Persistent); if (QDir(removed).exists()) { int 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) { auto deleteJob = KIO::del(QUrl::fromLocalFile(removed)); KJobWidgets::setWindow(deleteJob, this); if (!deleteJob->exec()) KMessageBox::error(this, i18n("Could not remove: %1.", removed)); } } }