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"