File indexing completed on 2024-04-28 04:38:52

0001 /*
0002     SPDX-FileCopyrightText: 1999-2001 Bernd Gehrmann <bernd@kdevelop.org>
0003     SPDX-FileCopyrightText: 1999-2001 the KDevelop Team
0004     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0005     SPDX-FileCopyrightText: 2010 Julien Desgats <julien.desgats@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "grepdialog.h"
0011 
0012 #include <algorithm>
0013 
0014 #include <QCloseEvent>
0015 #include <QDialogButtonBox>
0016 #include <QDir>
0017 #include <QFileDialog>
0018 #include <QLineEdit>
0019 #include <QMenu>
0020 #include <QPushButton>
0021 #include <QShowEvent>
0022 #include <QStringList>
0023 #include <QTimer>
0024 
0025 #include <KComboBox>
0026 #include <KCompletion>
0027 #include <KConfigGroup>
0028 #include <KLocalizedString>
0029 #include <KUrlCompletion>
0030 
0031 #include <interfaces/icore.h>
0032 #include <interfaces/idocument.h>
0033 #include <interfaces/idocumentcontroller.h>
0034 #include <interfaces/iruncontroller.h>
0035 #include <interfaces/iproject.h>
0036 #include <interfaces/iprojectcontroller.h>
0037 #include <interfaces/isession.h>
0038 
0039 #include <util/path.h>
0040 #include <util/wildcardhelpers.h>
0041 
0042 #include "debug.h"
0043 #include "grepviewplugin.h"
0044 #include "grepoutputview.h"
0045 #include "grepfindthread.h"
0046 #include "greputil.h"
0047 
0048 #include <utility>
0049 
0050 using namespace KDevelop;
0051 
0052 namespace {
0053 
0054 inline QString allOpenFilesString() { return i18nc("@item:inlistbox", "All Open Files"); }
0055 inline QString allOpenProjectsString() { return i18nc("@item:inlistbox", "All Open Projects"); }
0056 
0057 inline QStringList template_desc()
0058 {
0059     return QStringList{
0060         QStringLiteral("verbatim"),
0061         QStringLiteral("word"),
0062         QStringLiteral("assignment"),
0063         QStringLiteral("->MEMBER("),
0064         QStringLiteral("class::MEMBER("),
0065         QStringLiteral("OBJECT->member("),
0066     };
0067 }
0068 
0069 inline QStringList template_str()
0070 {
0071     return QStringList{
0072         QStringLiteral("%s"),
0073         QStringLiteral("\\b%s\\b"),
0074         QStringLiteral("\\b%s\\b\\s*=[^=]"),
0075         QStringLiteral("\\->\\s*\\b%s\\b\\s*\\("),
0076         QStringLiteral("([a-z0-9_$]+)\\s*::\\s*\\b%s\\b\\s*\\("),
0077         QStringLiteral("\\b%s\\b\\s*\\->\\s*([a-z0-9_$]+)\\s*\\("),
0078     };
0079 }
0080 
0081 inline QStringList repl_template()
0082 {
0083     return QStringList{
0084         QStringLiteral("%s"),
0085         QStringLiteral("%s"),
0086         QStringLiteral("%s = "),
0087         QStringLiteral("->%s("),
0088         QStringLiteral("\\1::%s("),
0089         QStringLiteral("%s->\\1("),
0090     };
0091 }
0092 
0093 inline QStringList filepatterns()
0094 {
0095     return QStringList{
0096         QStringLiteral("*.h,*.hxx,*.hpp,*.hh,*.h++,*.H,*.tlh,*.cuh,*.cpp,*.cc,*.C,*.c++,*.cxx,*.ocl,*.inl,*.idl,*.c,*.cu,*.m,*.mm,*.M,*.y,*.ypp,*.yxx,*.y++,*.l,*.txt,*.xml,*.rc"),
0097         QStringLiteral("*.cpp,*.cc,*.C,*.c++,*.cxx,*.ocl,*.inl,*.c,*.cu,*.m,*.mm,*.M"),
0098         QStringLiteral("*.h,*.hxx,*.hpp,*.hh,*.h++,*.H,*.tlh,*.cuh,*.idl"),
0099         QStringLiteral("*.adb"),
0100         QStringLiteral("*.cs"),
0101         QStringLiteral("*.f"),
0102         QStringLiteral("*.html,*.htm"),
0103         QStringLiteral("*.hs"),
0104         QStringLiteral("*.java"),
0105         QStringLiteral("*.js"),
0106         QStringLiteral("*.php,*.php3,*.php4"),
0107         QStringLiteral("*.pl"),
0108         QStringLiteral("*.pp,*.pas"),
0109         QStringLiteral("*.py"),
0110         QStringLiteral("*.js,*.css,*.yml,*.rb,*.rhtml,*.html.erb,*.rjs,*.js.rjs,*.rxml,*.xml.builder"),
0111         QStringLiteral("CMakeLists.txt,*.cmake"),
0112         QStringLiteral("*"),
0113     };
0114 }
0115 
0116 inline QStringList excludepatterns()
0117 {
0118     return QStringList{
0119         QStringLiteral("/CVS/,/SCCS/,/.svn/,/_darcs/,/build/,/.git/"),
0120         QString(),
0121     };
0122 }
0123 
0124 KConfigGroup dialogConfigGroup()
0125 {
0126     return ICore::self()->activeSession()->config()->group("GrepDialog");
0127 }
0128 
0129 class DialogConfigReader
0130 {
0131 public:
0132     DialogConfigReader()
0133         : m_config{dialogConfigGroup()}
0134     {
0135     }
0136 
0137     QStringList patternList() const
0138     {
0139         return m_config.readEntry("LastSearchItems", QStringList{});
0140     }
0141     int templateIndex() const
0142     {
0143         return m_config.readEntry("LastUsedTemplateIndex", 0);
0144     }
0145     QStringList templateStringList() const
0146     {
0147         return m_config.readEntry("LastUsedTemplateString", template_str());
0148     }
0149     QStringList replacementTemplateStringList() const
0150     {
0151         return m_config.readEntry("LastUsedReplacementTemplateString", repl_template());
0152     }
0153     bool isRegex() const
0154     {
0155         return m_config.readEntry("regexp", false);
0156     }
0157     bool isCaseSensitive() const
0158     {
0159         return m_config.readEntry("case_sens", true);
0160     }
0161     QStringList searchPathsList(const GrepViewPlugin& plugin) const
0162     {
0163         const bool isAnyProjectOpen = plugin.core()->projectController()->projectCount() != 0;
0164         return m_config.readEntry("SearchPaths",
0165                                   QStringList{isAnyProjectOpen ? allOpenProjectsString() : QDir::homePath()});
0166     }
0167     int depth() const
0168     {
0169         return m_config.readEntry("depth", -1);
0170     }
0171     bool limitToProjectFiles() const
0172     {
0173         return m_config.readEntry("search_project_files", true);
0174     }
0175     QStringList filePatternsList() const
0176     {
0177         return m_config.readEntry("file_patterns", filepatterns());
0178     }
0179     QStringList excludePatternsList() const
0180     {
0181         return m_config.readEntry("exclude_patterns", excludepatterns());
0182     }
0183 
0184 private:
0185     KConfigGroup m_config;
0186 };
0187 
0188 QString searchTemplateFromTemplateString(const QString& templateString)
0189 {
0190     return templateString.isEmpty() ? QStringLiteral("%s") : templateString;
0191 }
0192 
0193 ///Separator used to separate search paths.
0194 inline QString pathsSeparator() { return (QStringLiteral(";")); }
0195 
0196 ///Returns the chosen directories or files (only the top directories, not subfiles)
0197 QList<QUrl> getDirectoryChoice(const QString& text)
0198 {
0199     QList<QUrl> ret;
0200     if (text == allOpenFilesString()) {
0201         const auto openDocuments = ICore::self()->documentController()->openDocuments();
0202         ret.reserve(openDocuments.size());
0203         for (auto* doc : openDocuments) {
0204             ret << doc->url();
0205         }
0206     } else if (text == allOpenProjectsString()) {
0207         const auto projects = ICore::self()->projectController()->projects();
0208         ret.reserve(projects.size());
0209         for (auto* project : projects) {
0210             ret << project->path().toUrl();
0211         }
0212     } else {
0213         const QStringList semicolonSeparatedFileList = text.split(pathsSeparator(), Qt::SkipEmptyParts);
0214         if (!semicolonSeparatedFileList.isEmpty() && QFileInfo::exists(semicolonSeparatedFileList[0])) {
0215             // We use QFileInfo to make sure this is really a semicolon-separated file list, not a file containing
0216             // a semicolon in the name.
0217             ret.reserve(semicolonSeparatedFileList.size());
0218             for (const QString& file : semicolonSeparatedFileList) {
0219                 ret << QUrl::fromLocalFile(file).adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments);
0220             }
0221         } else {
0222             auto url = QUrl::fromUserInput(text).adjusted(QUrl::StripTrailingSlash | QUrl::NormalizePathSegments);
0223             if (!url.isEmpty()) {
0224                 ret.push_back(std::move(url));
0225             }
0226         }
0227     }
0228     return ret;
0229 }
0230 
0231 ///Check if all directories are part of a project
0232 bool directoriesInProject(const QString& dir)
0233 {
0234     const auto urls = getDirectoryChoice(dir);
0235     return std::all_of(urls.begin(), urls.end(), [&](const QUrl& url) {
0236         IProject *proj = ICore::self()->projectController()->findProjectForUrl(url);
0237         return (proj && proj->path().toUrl().isLocalFile());
0238     });
0239 }
0240 
0241 ///Max number of items in paths combo box.
0242 const int pathsMaxCount = 25;
0243 }
0244 
0245 GrepDialog::GrepDialog(GrepViewPlugin* plugin, GrepOutputView* toolView, QWidget* parent, bool show)
0246     : QDialog(parent)
0247     , Ui::GrepWidget()
0248     , m_plugin(plugin)
0249     , m_toolView(toolView)
0250     , m_show(show)
0251 {
0252     setAttribute(Qt::WA_DeleteOnClose);
0253 
0254     // if we don't intend on showing the dialog, we can skip all UI setup
0255     if (!m_show) {
0256         return;
0257     }
0258 
0259     setWindowTitle(i18nc("@title:window", "Find/Replace in Files"));
0260 
0261     setupUi(this);
0262     patternCombo->lineEdit()->setClearButtonEnabled(true);
0263     adjustSize();
0264 
0265     auto searchButton = buttonBox->button(QDialogButtonBox::Ok);
0266     Q_ASSERT(searchButton);
0267     searchButton->setText(i18nc("@action:button", "Search..."));
0268     searchButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
0269     connect(searchButton, &QPushButton::clicked, this, &GrepDialog::startSearch);
0270     connect(buttonBox, &QDialogButtonBox::rejected, this, &GrepDialog::reject);
0271 
0272     const DialogConfigReader configReader;
0273 
0274     patternCombo->addItems(configReader.patternList());
0275     patternCombo->setInsertPolicy(QComboBox::InsertAtTop);
0276     patternCombo->setCompleter(nullptr);
0277 
0278     templateTypeCombo->addItems(template_desc());
0279     templateTypeCombo->setCurrentIndex(configReader.templateIndex());
0280     templateEdit->addItems(configReader.templateStringList());
0281     templateEdit->setEditable(true);
0282     templateEdit->setCompletionMode(KCompletion::CompletionPopup);
0283     KCompletion* comp = templateEdit->completionObject();
0284     connect(templateEdit, QOverload<const QString&>::of(&KComboBox::returnPressed),
0285             comp, QOverload<const QString&>::of(&KCompletion::addItem));
0286     for(int i=0; i<templateEdit->count(); i++)
0287         comp->addItem(templateEdit->itemText(i));
0288     replacementTemplateEdit->addItems(configReader.replacementTemplateStringList());
0289     replacementTemplateEdit->setEditable(true);
0290     replacementTemplateEdit->setCompletionMode(KCompletion::CompletionPopup);
0291     comp = replacementTemplateEdit->completionObject();
0292     connect(replacementTemplateEdit, QOverload<const QString&>::of(&KComboBox::returnPressed),
0293             comp, QOverload<const QString&>::of(&KCompletion::addItem));
0294     for(int i=0; i<replacementTemplateEdit->count(); i++)
0295         comp->addItem(replacementTemplateEdit->itemText(i));
0296 
0297     regexCheck->setChecked(configReader.isRegex());
0298 
0299     caseSensitiveCheck->setChecked(configReader.isCaseSensitive());
0300 
0301     searchPaths->setCompletionObject(new KUrlCompletion());
0302     searchPaths->setAutoDeleteCompletionObject(true);
0303     searchPaths->addItems(configReader.searchPathsList(*m_plugin));
0304     searchPaths->setInsertPolicy(QComboBox::InsertAtTop);
0305 
0306     syncButton->setIcon(QIcon::fromTheme(QStringLiteral("dirsync")));
0307     syncButton->setMenu(createSyncButtonMenu());
0308 
0309     depthSpin->setValue(configReader.depth());
0310     limitToProjectCheck->setChecked(configReader.limitToProjectFiles());
0311 
0312     filesCombo->addItems(configReader.filePatternsList());
0313     excludeCombo->addItems(configReader.excludePatternsList());
0314 
0315     connect(templateTypeCombo, QOverload<int>::of(&KComboBox::activated),
0316             this, &GrepDialog::templateTypeComboActivated);
0317     connect(patternCombo, &QComboBox::editTextChanged,
0318             this, &GrepDialog::patternComboEditTextChanged);
0319     patternComboEditTextChanged( patternCombo->currentText() );
0320     patternCombo->setFocus();
0321 
0322     connect(searchPaths, &KComboBox::textActivated, this, &GrepDialog::setSearchLocations);
0323 
0324     directorySelector->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
0325     connect(directorySelector, &QPushButton::clicked, this, &GrepDialog::selectDirectoryDialog );
0326 
0327     Q_ASSERT(searchPaths->lineEdit());
0328     connect(searchPaths->lineEdit(), &QLineEdit::editingFinished, this, &GrepDialog::updateLimitToProjectEnabled);
0329     updateLimitToProjectEnabled();
0330 }
0331 
0332 void GrepDialog::selectDirectoryDialog()
0333 {
0334     const QString dirName = QFileDialog::getExistingDirectory(
0335         this, i18nc("@title:window", "Select Directory to Search in"),
0336         searchPaths->lineEdit()->text());
0337     if (!dirName.isEmpty()) {
0338         setSearchLocations(dirName);
0339     }
0340 }
0341 
0342 void GrepDialog::addUrlToMenu(QMenu* menu, const QUrl& url)
0343 {
0344     QAction* action = menu->addAction(m_plugin->core()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain));
0345     action->setData(QVariant(url.toString(QUrl::PreferLocalFile)));
0346     connect(action, &QAction::triggered, this, &GrepDialog::synchronizeDirActionTriggered);
0347 }
0348 
0349 void GrepDialog::addStringToMenu(QMenu* menu, const QString& string)
0350 {
0351     QAction* action = menu->addAction(string);
0352     action->setData(QVariant(string));
0353     connect(action, &QAction::triggered, this, &GrepDialog::synchronizeDirActionTriggered);
0354 }
0355 
0356 void GrepDialog::synchronizeDirActionTriggered(bool)
0357 {
0358     auto* action = qobject_cast<QAction*>(sender());
0359     Q_ASSERT(action);
0360     setSearchLocations(action->data().toString());
0361 }
0362 
0363 QMenu* GrepDialog::createSyncButtonMenu()
0364 {
0365     auto* ret = new QMenu(this);
0366 
0367     QSet<Path> hadUrls;
0368 
0369     IDocument *doc = m_plugin->core()->documentController()->activeDocument();
0370     if ( doc )
0371     {
0372         Path url = Path(doc->url()).parent();
0373 
0374         // always add the current file's parent directory
0375         hadUrls.insert(url);
0376         addUrlToMenu(ret, url.toUrl());
0377 
0378         url = url.parent();
0379 
0380         while(m_plugin->core()->projectController()->findProjectForUrl(url.toUrl()))
0381         {
0382             if(hadUrls.contains(url))
0383                 break;
0384             hadUrls.insert(url);
0385             addUrlToMenu(ret, url.toUrl());
0386             url = url.parent();
0387         }
0388     }
0389 
0390     QVector<QUrl> otherProjectUrls;
0391     const auto projects = m_plugin->core()->projectController()->projects();
0392     for (IProject* project : projects) {
0393         if (!hadUrls.contains(project->path())) {
0394             otherProjectUrls.append(project->path().toUrl());
0395         }
0396     }
0397 
0398     // sort the remaining project URLs alphabetically
0399     std::sort(otherProjectUrls.begin(), otherProjectUrls.end());
0400     for (const QUrl& url : qAsConst(otherProjectUrls)) {
0401         addUrlToMenu(ret, url);
0402     }
0403 
0404     ret->addSeparator();
0405     addStringToMenu(ret, allOpenFilesString());
0406     addStringToMenu(ret, allOpenProjectsString());
0407     return ret;
0408 }
0409 
0410 GrepDialog::~GrepDialog()
0411 {
0412 }
0413 
0414 void GrepDialog::setLastUsedSettings()
0415 {
0416     Q_ASSERT_X(!m_show, Q_FUNC_INFO, "Precondition");
0417 
0418     const auto currentItem = [](const QStringList& itemList) {
0419         return itemList.value(0); // the first item (if any) in the list stored in config is current
0420     };
0421 
0422     const DialogConfigReader configReader;
0423 
0424     m_settings.pattern = currentItem(configReader.patternList());
0425     m_settings.searchTemplate = searchTemplateFromTemplateString(currentItem(configReader.templateStringList()));
0426     m_settings.replacementTemplate = currentItem(configReader.replacementTemplateStringList());
0427     m_settings.regexp = configReader.isRegex();
0428     m_settings.caseSensitive = configReader.isCaseSensitive();
0429     m_settings.searchPaths = currentItem(configReader.searchPathsList(*m_plugin));
0430     m_settings.depth = configReader.depth();
0431     m_settings.projectFilesOnly = configReader.limitToProjectFiles() && directoriesInProject(m_settings.searchPaths);
0432     m_settings.files = currentItem(configReader.filePatternsList());
0433     m_settings.exclude = currentItem(configReader.excludePatternsList());
0434 }
0435 
0436 void GrepDialog::setVisible(bool visible)
0437 {
0438     QDialog::setVisible(visible && m_show);
0439 }
0440 
0441 void GrepDialog::closeEvent(QCloseEvent* closeEvent)
0442 {
0443     Q_UNUSED(closeEvent);
0444 
0445     if (!m_show) {
0446         return;
0447     }
0448 
0449     auto cg = dialogConfigGroup();
0450     // memorize the last patterns and paths
0451     cg.writeEntry("LastSearchItems", qCombo2StringList(patternCombo));
0452     cg.writeEntry("regexp", regexCheck->isChecked());
0453     cg.writeEntry("depth", depthSpin->value());
0454     cg.writeEntry("search_project_files", limitToProjectCheck->isChecked());
0455     cg.writeEntry("case_sens", caseSensitiveCheck->isChecked());
0456     cg.writeEntry("exclude_patterns", qCombo2StringList(excludeCombo));
0457     cg.writeEntry("file_patterns", qCombo2StringList(filesCombo));
0458     cg.writeEntry("LastUsedTemplateIndex", templateTypeCombo->currentIndex());
0459     cg.writeEntry("LastUsedTemplateString", qCombo2StringList(templateEdit));
0460     cg.writeEntry("LastUsedReplacementTemplateString", qCombo2StringList(replacementTemplateEdit));
0461     cg.writeEntry("SearchPaths", qCombo2StringList(searchPaths));
0462     cg.sync();
0463 }
0464 
0465 void GrepDialog::templateTypeComboActivated(int index)
0466 {
0467     templateEdit->setCurrentItem( template_str().at(index), true );
0468     replacementTemplateEdit->setCurrentItem( repl_template().at(index), true );
0469 }
0470 
0471 void GrepDialog::setPattern(const QString& pattern)
0472 {
0473     if (m_show) {
0474         patternCombo->setEditText(pattern);
0475         patternComboEditTextChanged(pattern);
0476     }
0477     m_settings.pattern = pattern;
0478 }
0479 
0480 void GrepDialog::historySearch(QVector<GrepJobSettings> &settingsHistory)
0481 {
0482     // clear the current settings history and pass it to a job list
0483     m_historyJobSettings.clear();
0484     m_historyJobSettings.swap(settingsHistory);
0485 
0486     // check if anything is do be done and if all projects are loaded
0487     if (!m_historyJobSettings.empty() && !checkProjectsOpened()) {
0488         connect(KDevelop::ICore::self()->projectController(),
0489                 &KDevelop::IProjectController::projectOpened,
0490                 this, &GrepDialog::checkProjectsOpened);
0491     }
0492 }
0493 
0494 void GrepDialog::setSearchLocations(const QString& dir)
0495 {
0496     if (dir.isEmpty()) {
0497         return; // ignore an attempt to set invalid empty search location
0498     }
0499 
0500     if (!m_show) {
0501         m_settings.searchPaths = dir;
0502         return;
0503     }
0504 
0505     if (QDir::isAbsolutePath(dir)) {
0506         static_cast<KUrlCompletion*>(searchPaths->completionObject())->setDir(QUrl::fromLocalFile(dir));
0507     }
0508 
0509     if (searchPaths->contains(dir)) {
0510         searchPaths->removeItem(searchPaths->findText(dir));
0511     }
0512 
0513     searchPaths->insertItem(0, dir);
0514     searchPaths->setCurrentItem(dir);
0515 
0516     if (searchPaths->count() > pathsMaxCount) {
0517         searchPaths->removeItem(searchPaths->count() - 1);
0518     }
0519 
0520     updateLimitToProjectEnabled();
0521 }
0522 
0523 void GrepDialog::patternComboEditTextChanged( const QString& text)
0524 {
0525     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!text.isEmpty());
0526 }
0527 
0528 bool GrepDialog::checkProjectsOpened()
0529 {
0530     // only proceed if all projects have been opened
0531     if (KDevelop::ICore::self()->activeSession()->config()->group("General Options").readEntry("Open Projects", QList<QUrl>()).count() !=
0532         KDevelop::ICore::self()->projectController()->projects().count())
0533         return false;
0534 
0535     const auto projects = KDevelop::ICore::self()->projectController()->projects();
0536     for (IProject* p : projects) {
0537         if (!p->isReady())
0538             return false;
0539     }
0540 
0541     // do the grep jobs one by one
0542     connect(m_plugin, &GrepViewPlugin::grepJobFinished, this, &GrepDialog::nextHistory);
0543     QTimer::singleShot(0, this, [=]() {nextHistory(true);});
0544 
0545     return true;
0546 }
0547 
0548 void GrepDialog::nextHistory(bool next)
0549 {
0550     if (next && !m_historyJobSettings.empty()) {
0551         m_settings = m_historyJobSettings.takeFirst();
0552         startSearch();
0553     } else {
0554         close();
0555     }
0556 }
0557 
0558 bool GrepDialog::isPartOfChoice(const QUrl& url) const
0559 {
0560     const auto choices = getDirectoryChoice(m_settings.searchPaths);
0561     for (const QUrl& choice : choices) {
0562         if(choice.isParentOf(url) || choice == url)
0563             return true;
0564     }
0565     return false;
0566 }
0567 
0568 void GrepDialog::startSearch()
0569 {
0570     // if m_show is false, all settings are fixed in m_settings
0571     if (m_show)
0572         updateSettings();
0573 
0574     const QStringList include = GrepFindFilesThread::parseInclude(m_settings.files);
0575     const QStringList exclude = GrepFindFilesThread::parseExclude(m_settings.exclude);
0576 
0577     // search for unsaved documents
0578     QList<IDocument*> unsavedFiles;
0579     const auto documents = ICore::self()->documentController()->openDocuments();
0580     for (IDocument* doc : documents) {
0581         QUrl docUrl = doc->url();
0582         if (doc->state() != IDocument::Clean &&
0583             isPartOfChoice(docUrl) &&
0584             QDir::match(include, docUrl.fileName()) &&
0585             !WildcardHelpers::match(exclude, docUrl.toLocalFile())
0586         ) {
0587             unsavedFiles << doc;
0588         }
0589     }
0590 
0591     if(!ICore::self()->documentController()->saveSomeDocuments(unsavedFiles))
0592     {
0593         close();
0594         return;
0595     }
0596 
0597     const QString descriptionOrUrl(m_settings.searchPaths);
0598     QList<QUrl> choice = getDirectoryChoice(descriptionOrUrl);
0599     QString description = descriptionOrUrl;
0600 
0601     // Shorten the description
0602     if(descriptionOrUrl != allOpenFilesString() && descriptionOrUrl != allOpenProjectsString()) {
0603         auto prettyFileName = [](const QUrl& url) {
0604             return ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain);
0605         };
0606 
0607         if (choice.size() > 1) {
0608             description = i18np("%2, and %1 more item", "%2, and %1 more items", choice.size() - 1, prettyFileName(choice[0]));
0609         } else if (!choice.isEmpty()) {
0610             description = prettyFileName(choice[0]);
0611         }
0612     }
0613 
0614     GrepOutputView* toolView = m_toolView;
0615     if (!toolView) {
0616         toolView = static_cast<GrepOutputView*>(ICore::self()->uiController()->findToolView(
0617             i18nc("@title:window", "Find/Replace in Files"), m_plugin->toolViewFactory(),
0618             m_settings.fromHistory ? IUiController::Create : IUiController::CreateAndRaise));
0619         Q_ASSERT_X(toolView, Q_FUNC_INFO, "This branch may be taken only after UiController::loadAllAreas() returns.");
0620     }
0621 
0622     if (m_settings.fromHistory) {
0623         // when restored from history, only display the parameters
0624         toolView->renewModel(m_settings, i18nc("@item search result", "Search \"%1\" in %2", m_settings.pattern, description));
0625         emit m_plugin->grepJobFinished(true);
0626     } else {
0627         GrepOutputModel* outputModel =
0628             toolView->renewModel(m_settings,
0629                                 i18nc("@item search result", "Search \"%1\" in %2 (at time %3)", m_settings.pattern, description,
0630                                     QTime::currentTime().toString(QStringLiteral("hh:mm"))));
0631 
0632         GrepJob* job = m_plugin->newGrepJob();
0633         connect(job, &GrepJob::showErrorMessage,
0634                 toolView, &GrepOutputView::showErrorMessage);
0635         //the GrepOutputModel gets the 'showMessage' signal to store it and forward
0636         //it to toolView
0637         connect(job, &GrepJob::showMessage,
0638                 outputModel, &GrepOutputModel::showMessageSlot);
0639         connect(outputModel, &GrepOutputModel::showMessage,
0640                 toolView, &GrepOutputView::showMessage);
0641 
0642         connect(toolView, &GrepOutputView::outputViewIsClosed, job, [=]() {job->kill();});
0643 
0644         qCDebug(PLUGIN_GREPVIEW) << "starting search with settings" << m_settings;
0645         job->setOutputModel(outputModel);
0646         job->setDirectoryChoice(choice);
0647         job->setSettings(m_settings);
0648 
0649         ICore::self()->runController()->registerJob(job);
0650     }
0651 
0652     m_plugin->rememberSearchDirectory(descriptionOrUrl);
0653 
0654     // If m_show is false, the dialog is closed somewhere else,
0655     // or not closed but still destroyed via deleteLater() in GrepViewPlugin::showDialog().
0656     if (m_show)
0657         close();
0658 }
0659 
0660 void GrepDialog::updateSettings()
0661 {
0662     m_settings.projectFilesOnly = limitToProjectCheck->isEnabled() && limitToProjectCheck->isChecked();
0663 
0664     m_settings.caseSensitive = caseSensitiveCheck->isChecked();
0665     m_settings.regexp = regexCheck->isChecked();
0666 
0667     m_settings.depth = depthSpin->value();
0668 
0669     m_settings.pattern = patternCombo->currentText();
0670     m_settings.searchTemplate = searchTemplateFromTemplateString(templateEdit->currentText());
0671     m_settings.replacementTemplate = replacementTemplateEdit->currentText();
0672     m_settings.files = filesCombo->currentText();
0673     m_settings.exclude = excludeCombo->currentText();
0674 
0675     m_settings.searchPaths = searchPaths->currentText();
0676 }
0677 
0678 void GrepDialog::updateLimitToProjectEnabled()
0679 {
0680     Q_ASSERT_X(m_show, Q_FUNC_INFO, "The UI must be initialized.");
0681     const bool enabled = directoriesInProject(searchPaths->currentText());
0682     limitToProjectLabel->setEnabled(enabled);
0683     limitToProjectCheck->setEnabled(enabled);
0684 }
0685 
0686 #include "moc_grepdialog.cpp"