File indexing completed on 2024-05-05 17:15:09

0001 /* This file is part of the kile project
0002    Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org>
0003    Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org>
0004    Copyright (C) 2001 Anders Lund <anders.lund@lund.tdcadsl.dk>
0005    Copyright (C) 2003 Jan-Marek Glogowski <glogow@stud.fbi.fh-darmstadt.de>
0006    Copyright (C) 2005 Holger Danielsson <holger.danielsson@versanet.de>
0007    Copyright (C) 2008-2022 Michel Ludwig <michel.ludwig@kdemail.net>
0008 
0009    This program is free software; you can redistribute it and/or
0010    modify it under the terms of the GNU General Public License as
0011    published by the Free Software Foundation; either version 2 of
0012    the License, or (at your option) any later version.
0013 
0014    This program is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017    GNU General Public License for more details.
0018 
0019    You should have received a copy of the GNU General Public License
0020    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 
0022    Original from kdebase / kate
0023 
0024    changes: 2005-11-27 (dani)
0025     - add a search for all files of a Kile project
0026       (done with one grep command for each file)
0027     - dialog is now based on KDialogBase
0028     - an item of the resultbox ist opened when it's highlightened
0029       (no double click is needed anymore)
0030     - dialog is deleted after work to minimize resources
0031     - added additional search modes for environments, labels etc.
0032     - fixed some bugs (f.e. two slashes at the end of directory
0033       names, jumping to the wrong line, wrong pattern lists)
0034     - add some editable template modes to search for LaTeX commands
0035     - add some predined modes to search for environments, graphics,
0036       labels, and references, either all of them or some special ones
0037 
0038     (in other words: most parts have changed to work perfectly with Kile ...)
0039 */
0040 
0041 // 2007-03-12 dani
0042 //  - use KileDocument::Extensions
0043 
0044 #include "dialogs/findfilesdialog.h"
0045 
0046 #include <QCheckBox>
0047 #include <QCursor>
0048 #include <QDialogButtonBox>
0049 #include <QEvent>
0050 #include <QGridLayout>
0051 #include <QGroupBox>
0052 #include <QHBoxLayout>
0053 #include <QLabel>
0054 #include <QLayout>
0055 #include <QLineEdit>
0056 #include <QListWidget>
0057 #include <QObject>
0058 #include <QPushButton>
0059 #include <QRegularExpression>
0060 #include <QVBoxLayout>
0061 
0062 #include <KProcess>
0063 #include <KAcceleratorManager>
0064 #include <KComboBox>
0065 #include <KLocalizedString>
0066 #include <KMessageBox>
0067 #include <KShell>
0068 #include <KUrlCompletion>
0069 #include <KUrlRequester>
0070 #include <KConfigGroup>
0071 
0072 #include "kiledebug.h"
0073 #include "kileconfig.h"
0074 #include "kileproject.h"
0075 #include "kiledocmanager.h"
0076 #include "kileextensions.h"
0077 
0078 namespace KileDialog {
0079 
0080 FindFilesDialog::FindFilesDialog(QWidget *parent, KileInfo *ki, KileGrep::Mode mode, const char *name)
0081     : QDialog(parent)
0082     , m_ki(ki)
0083     , m_mode(mode)
0084     , m_proc(Q_NULLPTR)
0085     , m_grepJobs(0)
0086 {
0087     setObjectName(name);
0088     setWindowTitle(QString());
0089     setModal(false);
0090     QVBoxLayout *mainLayout = new QVBoxLayout;
0091     setLayout(mainLayout);
0092 
0093     // project groupbox
0094     QGroupBox *projectgroup = new QGroupBox(i18n("Project"), this);
0095     mainLayout->addWidget(projectgroup);
0096     QGridLayout *projectgrouplayout = new QGridLayout();
0097     projectgrouplayout->setAlignment(Qt::AlignTop);
0098     projectgroup->setLayout(projectgrouplayout);
0099 
0100     QLabel *project_label = new QLabel(i18n("Name:"), projectgroup);
0101     int labelwidth = project_label->sizeHint().width();
0102 
0103     QLabel *projectdir_label = new QLabel(i18n("Directory:"), projectgroup);
0104     if(projectdir_label->sizeHint().width() > labelwidth) {
0105         labelwidth = projectdir_label->sizeHint().width();
0106     }
0107 
0108     projectname_label = new QLabel(projectgroup);
0109     projectdirname_label = new QLabel(projectgroup);
0110 
0111     projectgrouplayout->addWidget(project_label, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
0112     projectgrouplayout->addWidget(projectname_label, 0, 1, Qt::AlignLeft | Qt::AlignVCenter);
0113     projectgrouplayout->addWidget(projectdir_label, 1, 0, Qt::AlignLeft | Qt::AlignVCenter);
0114     projectgrouplayout->addWidget(projectdirname_label, 1, 1, Qt::AlignLeft | Qt::AlignVCenter);
0115     projectgrouplayout->setColumnStretch(1, 1);
0116 
0117     // search groupbox
0118     QGroupBox *searchgroup = new QGroupBox(i18n("Search"), this);
0119     mainLayout->addWidget(searchgroup);
0120     QGridLayout *searchgrouplayout = new QGridLayout();
0121     searchgrouplayout->setAlignment(Qt::AlignTop);
0122     searchgroup->setLayout(searchgrouplayout);
0123 
0124     QLabel *pattern_label = new QLabel(i18n("Pattern:"), searchgroup);
0125     if(pattern_label->sizeHint().width() > labelwidth) {
0126         labelwidth = pattern_label->sizeHint().width();
0127     }
0128 
0129     pattern_combo = new KComboBox(true, searchgroup);
0130     pattern_combo->setInsertPolicy(KComboBox::NoInsert);
0131     pattern_combo->setFocus();
0132     pattern_combo->setMinimumSize(pattern_combo->sizeHint());
0133     pattern_label->setBuddy(pattern_combo);
0134 
0135     QLabel *template_label = new QLabel(i18n("Template:"), searchgroup);
0136     if(template_label->sizeHint().width() > labelwidth) {
0137         labelwidth = template_label->sizeHint().width();
0138     }
0139 
0140     QStringList templatemode_list;
0141     templatemode_list << i18n("Normal")
0142                       << i18n("Command")
0143                       << i18n("Command[]")
0144                       << i18n("Environment")
0145                       << i18n("Image")
0146                       << i18n("Label")
0147                       << i18n("Reference")
0148                       << i18n("File");
0149 
0150     QHBoxLayout *template_layout = new QHBoxLayout();
0151     template_layout->setContentsMargins(0, 0, 0, 0);
0152     template_combo = new KComboBox(false, searchgroup);
0153     template_combo->addItems(templatemode_list);
0154     template_combo->adjustSize();
0155     template_combo->setFixedSize(template_combo->size());
0156     template_layout->addWidget(template_combo);
0157     m_lastTemplateIndex = 0;
0158 
0159     template_edit = new QLineEdit(searchgroup);
0160     template_edit->setText("%s");
0161     template_edit->setMinimumSize(template_edit->sizeHint());
0162     template_label->setBuddy(template_edit);
0163     template_layout->addWidget(template_edit);
0164 
0165     searchgrouplayout->addWidget(pattern_label, 0, 0, Qt::AlignLeft | Qt::AlignVCenter);
0166     searchgrouplayout->addWidget(pattern_combo, 0, 1);
0167     searchgrouplayout->addWidget(template_label, 1, 0, Qt::AlignLeft | Qt::AlignVCenter);
0168     searchgrouplayout->addLayout(template_layout, 1, 1);
0169 
0170     // filter groupbox
0171     QGroupBox *filtergroup = new QGroupBox(i18n("Directory Options"), this);
0172     mainLayout->addWidget(filtergroup);
0173     QGridLayout *filtergrouplayout = new QGridLayout();
0174     filtergrouplayout->setAlignment(Qt::AlignTop);
0175     filtergroup->setLayout(filtergrouplayout);
0176 
0177     QLabel *files_label = new QLabel(i18n("Filter:"), filtergroup);
0178     if (files_label->sizeHint().width() > labelwidth) {
0179         labelwidth = files_label->sizeHint().width();
0180     }
0181 
0182     filter_combo = new KComboBox(true, filtergroup);
0183     filter_combo->setMinimumSize(filter_combo->sizeHint());
0184     files_label->setBuddy(filter_combo->focusProxy());
0185 
0186     QLabel *dir_label = new QLabel(i18n("Directory:"), filtergroup);
0187     if (dir_label->sizeHint().width() > labelwidth) {
0188         labelwidth = dir_label->sizeHint().width();
0189     }
0190 
0191     dir_combo = new KUrlRequester(new KComboBox(true, filtergroup), filtergroup);
0192     dir_combo->setObjectName("dir combo");
0193     dir_combo->completionObject()->setMode(KUrlCompletion::DirCompletion);
0194     dir_combo->setMode(KFile::Directory | KFile::LocalOnly | KFile::ExistingOnly);
0195     dir_label->setBuddy(dir_combo);
0196 
0197     recursive_box = new QCheckBox(i18n("Scan directories recursively"), filtergroup);
0198     recursive_box->setMinimumWidth(recursive_box->sizeHint().width());
0199 
0200     filtergrouplayout->addWidget(files_label, 0, 0);
0201     filtergrouplayout->addWidget(filter_combo, 0, 1);
0202     filtergrouplayout->addWidget(dir_label, 1, 0);
0203     filtergrouplayout->addWidget(dir_combo, 1, 1);
0204     filtergrouplayout->addWidget(recursive_box, 2, 1);
0205     filtergrouplayout->setColumnStretch(1, 1);
0206 
0207     // result box
0208     resultbox = new QListWidget(this);
0209     mainLayout->addWidget(resultbox);
0210     resultbox->setMinimumHeight(150);
0211 
0212     // button box
0213     QDialogButtonBox *actionbox = new QDialogButtonBox(this);
0214     mainLayout->addWidget(actionbox);
0215     search_button = new QPushButton(i18n("&Search"));
0216     search_button->setDefault(true);
0217     search_button->setEnabled(false);
0218     search_button->setIcon(QIcon::fromTheme("edit-find"));
0219     connect(search_button, &QPushButton::clicked, this, &FindFilesDialog::slotSearch);
0220     actionbox->addButton(search_button, QDialogButtonBox::ActionRole);
0221     clear_button = new QPushButton(i18n("&Clear"));
0222     clear_button->setEnabled(false);
0223     clear_button->setIcon(QIcon::fromTheme("edit-clear-locationbar"));
0224     connect(clear_button, &QPushButton::clicked, this, &FindFilesDialog::slotClear);
0225     actionbox->addButton(clear_button, QDialogButtonBox::ActionRole);
0226     close_button = actionbox->addButton(QDialogButtonBox::Close);
0227     connect(close_button, &QPushButton::clicked, this, &FindFilesDialog::slotClose);
0228 
0229     // adjust labels
0230     project_label->setFixedWidth(labelwidth);
0231     projectdir_label->setFixedWidth(labelwidth);
0232     pattern_label->setFixedWidth(labelwidth);
0233     template_label->setFixedWidth(labelwidth);
0234     files_label->setFixedWidth(labelwidth);
0235     dir_label->setFixedWidth(labelwidth);
0236 
0237     if (m_mode == KileGrep::Project) {
0238         filtergroup->hide();
0239         mainLayout->addWidget(projectgroup);
0240         mainLayout->addWidget(searchgroup);
0241     }
0242     else {
0243         projectgroup->hide();
0244         mainLayout->addWidget(searchgroup);
0245         mainLayout->addWidget(filtergroup);
0246     }
0247     mainLayout->addWidget(resultbox);
0248     mainLayout->addWidget(actionbox);
0249 
0250     // Produces error messages like
0251     // QListBox::property( "text" ) failed:
0252     //  property invalid or does not exist
0253     // Anyone an idea?
0254     KAcceleratorManager::manage(this);
0255 
0256     pattern_combo->setWhatsThis(
0257         i18n("Enter the regular expression you want to search for here.<br>"
0258              "Possible meta characters are:<br>"
0259              "<ul>"
0260              "<li>&nbsp;<b>.</b> - Matches any character</li>"
0261              "<li>&nbsp;<b>^</b> - Matches the beginning of a line</li>"
0262              "<li>&nbsp;<b>$</b> - Matches the end of a line</li>"
0263              "<li>&nbsp;<b>\\\\\\&lt;</b> - Matches the beginning of a word</li>"
0264              "<li>&nbsp;<b>\\\\\\&gt;</b> - Matches the end of a word</li>"
0265              "</ul>"
0266              "The following repetition operators exist:"
0267              "<ul>"
0268              "<li>&nbsp;<b>?</b> - The preceding item is matched at most once</li>"
0269              "<li>&nbsp;<b>*</b> - The preceding item is matched zero or more times</li>"
0270              "<li>&nbsp;<b>+</b> - The preceding item is matched one or more times</li>"
0271              "<li>&nbsp;<b>{<i>n</i>}</b> - The preceding item is matched exactly <i>n</i> times</li>"
0272              "<li>&nbsp;<b>{<i>n</i>,}</b> - The preceding item is matched <i>n</i> or more times</li>"
0273              "<li>&nbsp;<b>{,<i>n</i>}</b> - The preceding item is matched at most <i>n</i> times</li>"
0274              "<li>&nbsp;<b>{<i>n</i>,<i>m</i>}</b> - The preceding item is matched at least <i>n</i>, "
0275              "but at most <i>m</i> times.</li>"
0276              "</ul>"
0277              "Furthermore, backreferences to bracketed subexpressions are "
0278              "available via the notation \\\\<i>n</i>."
0279             ));
0280     filter_combo->setWhatsThis(
0281         i18n("Enter the file name pattern of the files to search here. "
0282              "You may give several patterns separated by commas."));
0283     template_combo->setWhatsThis(
0284         i18n("Choose one search mode. For the first modes, the search pattern is "
0285              "built from the editable template, where '%s' is replaced by the given pattern.<br><br>"
0286              "There are additional fixed predefined modes for environments, graphics, labels, references "
0287              "and input files. If the pattern is empty, Kile will search for all commands of this mode. "
0288              "If a pattern is given, it will be inserted as a parameter. For example, in environment mode with "
0289              "pattern 'center', Kile will search for '\\begin{center}', and in graphics mode with "
0290              "pattern '.*\\.png', Kile will search for all png files."));
0291     template_edit->setWhatsThis(
0292         i18n("For the first three modes you can choose a template for the pattern from the combo box "
0293              "and edit it here. The string %s in the template is replaced "
0294              "by the pattern input field, resulting in the regular expression "
0295              "to search for. In all other modes this template is ignored."));
0296     dir_combo->setWhatsThis(
0297         i18n("Enter the directory which contains the files you want to search in."));
0298     recursive_box->setWhatsThis(
0299         i18n("Check this box to search in all subdirectories."));
0300     resultbox->setWhatsThis(
0301         i18n("The results of the grep run are listed here. Select a "
0302              "filename/line number combination with a mouse click on the item "
0303              "or with the cursor to show the respective line in the editor."));
0304 
0305     // read config and setup dialog for both modes
0306     readConfig();
0307     if (m_mode == KileGrep::Directory) {
0308         setWindowTitle(i18n("Find in Files"));
0309         setupDirectory();
0310     }
0311     else {
0312         setWindowTitle(i18n("Find in Project"));
0313         setupProject();
0314     }
0315 
0316     pattern_combo->setEditText(QString());
0317     template_edit->setText(m_TemplateList[0]);
0318     slotPatternTextChanged(QString());
0319 
0320     connect(pattern_combo->lineEdit(), &QLineEdit::textChanged,
0321             this, &FindFilesDialog::slotPatternTextChanged);
0322     connect(template_combo, static_cast<void (KComboBox::*)(int)>(&KComboBox::activated),
0323             this, &FindFilesDialog::slotTemplateActivated);
0324     connect(resultbox, &QListWidget::currentTextChanged,
0325             this, &FindFilesDialog::slotItemSelected);
0326 
0327     QDialogButtonBox *buttonBox = new QDialogButtonBox();
0328     connect(buttonBox, &QDialogButtonBox::accepted, this, &FindFilesDialog::accept);
0329     connect(buttonBox, &QDialogButtonBox::rejected, this, &FindFilesDialog::reject);
0330     mainLayout->addWidget(buttonBox);
0331 
0332     connect(this, &FindFilesDialog::finished, this, &FindFilesDialog::slotClose);
0333 
0334     resize(450, sizeHint().height());
0335     KILE_DEBUG_MAIN << "==FindFilesDialog (create dialog)=============================";
0336 }
0337 
0338 FindFilesDialog::~FindFilesDialog()
0339 {
0340     KILE_DEBUG_MAIN << "==FindFilesDialog (delete dialog)=============================";
0341     writeConfig();
0342 }
0343 
0344 ///////////////////// config /////////////////////
0345 
0346 void FindFilesDialog::readConfig()
0347 {
0348     pattern_combo->addItems(readList(KileGrep::SearchItems));
0349 
0350     QString labels = getCommandList(KileDocument::CmdAttrLabel);
0351     QString references = getCommandList(KileDocument::CmdAttrReference);
0352     m_TemplateList = readList(KileGrep::SearchTemplates) ;
0353     if(m_TemplateList.count() != 3) {
0354         m_TemplateList.clear();
0355         m_TemplateList << "%s" << "\\\\%s\\{" << "\\\\%s(\\[[^]]*\\])?\\{";
0356     }
0357     m_TemplateList << "\\\\begin\\{"                             // to be closed with "%s\\}"
0358                    << "\\\\includegraphics(\\[[^]]*\\])?\\{"
0359                    << "\\\\(label" + labels + ")\\{"
0360                    << "\\\\(ref|pageref|vref|vpageref|fref|Fref|eqref" + references + ")(\\[[^]]*\\])?\\{"
0361                    << "\\\\(input|include)\\{"
0362                    ;
0363 
0364     if (m_mode == KileGrep::Directory) {
0365         dir_combo->comboBox()->addItems(readList(KileGrep::SearchPaths));
0366         recursive_box->setChecked(KileConfig::grepRecursive());
0367     }
0368 }
0369 
0370 void FindFilesDialog::writeConfig()
0371 {
0372     KileConfig::setLastSearchItems(getListItems(pattern_combo));
0373 
0374     QStringList list;
0375     list << m_TemplateList[0] << m_TemplateList[1] << m_TemplateList[2];
0376     KileConfig::setLastSearchTemplates(list);
0377 
0378     if(m_mode == KileGrep::Directory) {
0379         KileConfig::setLastSearchPaths(getListItems(dir_combo->comboBox()));
0380         KileConfig::setGrepRecursive(recursive_box->isChecked());
0381     }
0382 }
0383 
0384 ///////////////////// setup search modes /////////////////////
0385 
0386 void FindFilesDialog::setupDirectory()
0387 {
0388     setDirName(QDir::home().absolutePath());
0389     // use a filter for 'find in files' dialog
0390     KileDocument::Extensions *extensions = m_ki->extensions();
0391     QString filter = extensions->fileFilterKDEStyle(true, {KileDocument::Extensions::TEX,
0392                      KileDocument::Extensions::PACKAGES,
0393                      KileDocument::Extensions::BIB,
0394                      KileDocument::Extensions::METAPOST
0395                                                           });
0396     setFilter(filter);
0397 }
0398 
0399 void FindFilesDialog::setupProject()
0400 {
0401     KileProject *project = m_ki->docManager()->activeProject();
0402     if(project) {
0403         m_projectOpened = true;
0404         m_projectdir = project->baseURL().toLocalFile();
0405         projectname_label->setText(project->name());
0406         projectdirname_label->setText(m_projectdir);
0407 
0408         m_projectfiles.clear();
0409         m_projectfiles = m_ki->docManager()->getProjectFiles();
0410     }
0411     else {
0412         m_projectOpened = false;
0413         projectname_label->setText(i18n("no project opened"));
0414         projectdirname_label->setText(QString());
0415     }
0416 }
0417 
0418 ///////////////////// read entries /////////////////////
0419 
0420 QStringList FindFilesDialog::readList(KileGrep::List listtype)
0421 {
0422     QStringList strings, result;
0423 
0424     bool stripSlash = false;
0425     switch (listtype) {
0426     case KileGrep::SearchItems:
0427         strings = KileConfig::lastSearchItems();
0428         break;
0429     case KileGrep::SearchPaths:
0430         strings = KileConfig::lastSearchPaths();
0431         stripSlash = true;
0432         break;
0433     case KileGrep::SearchTemplates:
0434         strings = KileConfig::lastSearchTemplates();
0435         break;
0436     }
0437 
0438     while (strings.count() > 0) {
0439         if(stripSlash && strings[0].right(1) == "/") {
0440             strings[0].truncate(strings[0].length() - 1);
0441         }
0442         if(!strings[0].isEmpty()) {
0443             result.append(strings[0]);
0444         }
0445         strings.removeAll(strings[0]);
0446     }
0447     return result;
0448 }
0449 
0450 ///////////////////// item selected /////////////////////
0451 
0452 void FindFilesDialog::slotItemSelected(const QString& item)
0453 {
0454     KILE_DEBUG_MAIN << "\tgrep: start item selected";
0455     int pos;
0456 
0457     QString str = item;
0458     if((pos = str.indexOf(':')) != -1) {
0459         QString filename = str.left(pos);
0460         str = str.right(str.length() - 1 - pos);
0461         if((pos = str.indexOf(':')) != -1) {
0462             QString linenumber = str.left(pos);
0463             QFileInfo fileInfo(filename);
0464             if(fileInfo.isAbsolute()) {
0465                 emit itemSelected(filename, linenumber.toInt());
0466             }
0467             else if(m_mode == KileGrep::Project) {
0468                 emit itemSelected(m_projectdir + QDir::separator() + filename, linenumber.toInt());
0469             }
0470             else {
0471                 emit itemSelected(dir_combo->comboBox()->itemText(0) + QDir::separator() + filename, linenumber.toInt());
0472             }
0473         }
0474     }
0475 }
0476 
0477 ///////////////////// grep /////////////////////
0478 
0479 void FindFilesDialog::startGrep()
0480 {
0481     m_proc = new KProcess(this);
0482     m_proc->setOutputChannelMode(KProcess::SeparateChannels);
0483 
0484     m_buf.clear();
0485     m_errbuf.clear();
0486     QString command;
0487     if (m_mode == KileGrep::Project) {
0488         command = buildProjectCommand() + ' ' + KShell::quoteArg(m_projectfiles[m_grepJobs-1]);
0489     }
0490     else {
0491         command = buildFilesCommand();
0492     }
0493     KILE_DEBUG_MAIN << "\tgrep (project): " <<  command;
0494     (*m_proc) << KShell::splitArgs(command);
0495 
0496     m_grepJobs--;
0497 
0498     connect(m_proc, static_cast<void (KProcess::*)(int, QProcess::ExitStatus)>(&KProcess::finished),
0499             this, &FindFilesDialog::processExited);
0500     connect(m_proc, &KProcess::readyReadStandardOutput,
0501             this, &FindFilesDialog::processStandardOutputReady);
0502     connect(m_proc, &KProcess::readyReadStandardError,
0503             this, &FindFilesDialog::processErrorOutputReady);
0504 
0505     m_proc->start();
0506 }
0507 
0508 void FindFilesDialog::processOutput(bool forceAll)
0509 {
0510     // NOTE: it isn't possible to use 'kapp->processEvents()' in this method as
0511     //       this will trigger the 'readAllStandardOutput()' signal, and call this
0512     //       method again.
0513     int pos;
0514     int n = 0;
0515     while((pos = m_buf.indexOf('\n')) != -1) {
0516         QString item = m_buf.left(pos);
0517         if(!item.isEmpty()) {
0518             if(m_mode == KileGrep::Project) {
0519                 if (item.indexOf(m_projectdir) == 0) {
0520                     new QListWidgetItem(item.mid(m_projectdir.length()), resultbox);
0521                 }
0522                 else {
0523                     new QListWidgetItem(item, resultbox);
0524                 }
0525             }
0526             else {
0527                 new QListWidgetItem(item.mid(dir_combo->url().toLocalFile().length() + 1), resultbox);
0528             }
0529         }
0530         m_buf = m_buf.right(m_buf.length() - pos - 1);
0531         if(!forceAll) {
0532             ++n;
0533             if(n == 100) {
0534                 break;
0535             }
0536         }
0537     }
0538 }
0539 
0540 void FindFilesDialog::processStandardOutputReady()
0541 {
0542     QByteArray outputBuffer = m_proc->readAllStandardOutput();
0543     m_buf += QString::fromLocal8Bit(outputBuffer.data(), outputBuffer.size());
0544     processOutput();
0545 }
0546 
0547 void FindFilesDialog::processErrorOutputReady()
0548 {
0549     QByteArray outputBuffer = m_proc->readAllStandardError();
0550     m_errbuf += QString::fromLocal8Bit(outputBuffer.data(), outputBuffer.size());
0551 }
0552 
0553 void FindFilesDialog::processExited(int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/)
0554 {
0555     if(!m_errbuf.isEmpty()) {
0556         KMessageBox::information(parentWidget(), i18n("<strong>Error:</strong><p>") + m_errbuf, i18n("Grep Tool Error"));
0557         m_errbuf.clear();
0558     }
0559     else {
0560         finish();
0561     }
0562 }
0563 
0564 void FindFilesDialog::finish()
0565 {
0566     if(m_proc) {
0567         m_proc->kill();
0568         m_proc->disconnect();
0569         m_proc->deleteLater();
0570         m_proc = Q_NULLPTR;
0571     }
0572     m_buf += '\n';
0573     // we process all the remaining output
0574     processOutput(true);
0575 
0576     if (shouldRestart()) {
0577         startGrep();
0578     }
0579     else {
0580         updateLists();
0581 
0582         resultbox->unsetCursor();
0583         clear_button->setEnabled(resultbox->count() > 0);
0584         search_button->setText(i18n("&Search"));
0585         slotPatternTextChanged(pattern_combo->lineEdit()->text());
0586     }
0587 }
0588 
0589 void FindFilesDialog::updateLists()
0590 {
0591     updateListItems(pattern_combo);
0592     if(m_mode == KileGrep::Directory) {
0593         updateListItems(dir_combo->comboBox());
0594     }
0595 }
0596 
0597 ///////////////////// build commands /////////////////////
0598 
0599 QString FindFilesDialog::getPattern()
0600 {
0601     QString pattern;
0602     int template_mode = template_combo->currentIndex();
0603     if (template_mode < KileGrep::tmEnv) {
0604         pattern = template_edit->text();
0605         if (pattern.isEmpty()) {
0606             pattern = pattern_combo->currentText();
0607         }
0608         else {
0609             pattern.replace("%s", pattern_combo->currentText());
0610         }
0611     }
0612     else {
0613         pattern = m_TemplateList[template_mode];
0614         if (!pattern_combo->currentText().isEmpty()) {
0615             pattern += pattern_combo->currentText()  + "\\}";
0616         }
0617     }
0618 
0619     return pattern;
0620 }
0621 
0622 QString FindFilesDialog::getShellPattern()
0623 {
0624     QString pattern = getPattern();
0625     pattern.replace('\'', "'\\''");
0626     return KShell::quoteArg(pattern);
0627 }
0628 
0629 
0630 QString FindFilesDialog::buildFilesCommand()
0631 {
0632     QString files, files_temp;
0633 
0634     if(filter_combo->currentIndex() >= 0) {
0635         files_temp = m_filterList[filter_combo->currentIndex()];
0636     }
0637     else {
0638         files_temp = filter_combo->currentText();
0639     }
0640 
0641     QStringList tokens = files_temp.split(' ', Qt::SkipEmptyParts);
0642     QStringList::Iterator it = tokens.begin();
0643     if (it != tokens.end()) {
0644         files = " '" + (*it) + '\'';
0645         ++it;
0646     }
0647 
0648     for(; it != tokens.end(); ++it) {
0649         files = files + " -o -name " + '\'' + (*it) + '\'';
0650     }
0651 
0652     QString shell_command;
0653     shell_command += "find ";
0654     shell_command += KShell::quoteArg(dir_combo->url().path());
0655     shell_command += " \\( -name ";
0656     shell_command += files;
0657     shell_command += " \\)";
0658     if (!recursive_box->isChecked()) {
0659         shell_command += " -maxdepth 1";
0660     }
0661     shell_command += " -exec grep -n -E -I -H -e " + getShellPattern() + " {} \\;";
0662     KILE_DEBUG_MAIN << "shell command" << shell_command;
0663     return shell_command;
0664 }
0665 
0666 QString FindFilesDialog::buildProjectCommand()
0667 {
0668     return "grep -n -E -I -H -e " + getShellPattern();
0669 }
0670 
0671 ///////////////////// Search /////////////////////
0672 
0673 void FindFilesDialog::slotSearch()
0674 {
0675     KILE_DEBUG_MAIN << "\tgrep: start slot search" << m_proc;
0676 
0677     if (m_proc) {
0678         clearGrepJobs();
0679         finish();
0680         return;
0681     }
0682 
0683     if (template_combo->currentIndex() < KileGrep::tmEnv && pattern_combo->currentText().isEmpty()) {
0684         return;
0685     }
0686 
0687     KILE_DEBUG_MAIN << "\tgrep: start new search";
0688     QRegularExpression re(getPattern());
0689     if(!re.isValid()) {
0690         KMessageBox::error(m_ki->mainWindow(), i18n("Invalid regular expression: %1", re.errorString()), i18n("Grep Tool Error"));
0691         return;
0692     }
0693 
0694     resultbox->setCursor(QCursor(Qt::WaitCursor));
0695     search_button->setText(i18n("&Cancel"));
0696     if (template_combo->currentIndex() < KileGrep::tmEnv) {
0697         m_TemplateList[m_lastTemplateIndex] = template_edit->text();
0698     }
0699 
0700     // start grep command
0701     m_grepJobs = (m_mode == KileGrep::Project) ? m_projectfiles.count() : 1;
0702     startGrep();
0703 }
0704 
0705 void FindFilesDialog::slotSearchFor(const QString &pattern)
0706 {
0707     slotClear();
0708     pattern_combo->setEditText(pattern);
0709     slotSearch();
0710 }
0711 
0712 void FindFilesDialog::slotClear()
0713 {
0714     KILE_DEBUG_MAIN << "\tgrep: slot clear";
0715     clearGrepJobs();
0716     finish();
0717     resultbox->clear();
0718 }
0719 
0720 void FindFilesDialog::slotClose()
0721 {
0722     KILE_DEBUG_MAIN << "\tgrep: slot close";
0723     clearGrepJobs();
0724     finish();
0725     hide();
0726     deleteLater();
0727 }
0728 
0729 ///////////////////// templates /////////////////////
0730 
0731 void FindFilesDialog::slotPatternTextChanged(const QString &)
0732 {
0733     updateWidgets();
0734 }
0735 
0736 void FindFilesDialog::slotTemplateActivated(int index)
0737 {
0738     if (index < KileGrep::tmEnv) {
0739         m_TemplateList[m_lastTemplateIndex] = template_edit->text();
0740         template_edit->setText(m_TemplateList[index]);
0741     }
0742     else {
0743         template_edit->setText(QString());
0744     }
0745     m_lastTemplateIndex = index;
0746 
0747     updateWidgets();
0748 }
0749 
0750 void FindFilesDialog::updateWidgets()
0751 {
0752     bool search_state = (m_mode == KileGrep::Directory) || (m_mode == KileGrep::Project && m_projectOpened);
0753 
0754     if (template_combo->currentIndex() < KileGrep::tmEnv) {
0755         template_edit->setEnabled(true);
0756         search_button->setEnabled(search_state && !pattern_combo->currentText().isEmpty());
0757     }
0758     else {
0759         template_edit->setEnabled(false);
0760         search_button->setEnabled(search_state);
0761     }
0762 }
0763 
0764 ///////////////////// directory /////////////////////
0765 
0766 void FindFilesDialog::setDirName(const QString &dir)
0767 {
0768     KComboBox *combo = dir_combo->comboBox();
0769 
0770     if (findListItem(combo, dir) < 0) {
0771         combo->addItem(dir);
0772     }
0773     if (combo->itemText(0) != dir) {
0774         slotClear();
0775     }
0776 }
0777 
0778 ///////////////////// filter /////////////////////
0779 
0780 void FindFilesDialog::setFilter(const QString &filter)
0781 {
0782     m_filterList.clear();
0783     filter_combo->clear();
0784     if (!filter.isEmpty()) {
0785         QStringList filter_lst = filter.split('\n');
0786         for (QStringList::Iterator it = filter_lst.begin(); it != filter_lst.end(); ++it) {
0787             QStringList filter_split = (*it).split('|');
0788             m_filterList.append(filter_split[0]);
0789             filter_combo->addItem(filter_split[1]);
0790         }
0791     }
0792 }
0793 
0794 void FindFilesDialog::appendFilter(const QString &name, const QString &filter)
0795 {
0796     filter_combo->addItem(name);
0797     m_filterList.append(filter);
0798 }
0799 
0800 ///////////////////// template /////////////////////
0801 
0802 void FindFilesDialog::appendTemplate(const QString &name, const QString &regexp)
0803 {
0804     template_combo->addItem(name);
0805     m_TemplateList.append(regexp);
0806 }
0807 
0808 void FindFilesDialog::clearTemplates()
0809 {
0810     template_combo->clear();
0811     m_TemplateList.clear();
0812 }
0813 
0814 ///////////////////// KComboBox /////////////////////
0815 
0816 QStringList FindFilesDialog::getListItems(KComboBox *combo)
0817 {
0818     QStringList list;
0819     for (int i = 0; i < combo->count() && i < KILEGREP_MAX; ++i) {
0820         list.append(combo->itemText(i));
0821     }
0822     return list;
0823 }
0824 
0825 int FindFilesDialog::findListItem(KComboBox *combo, const QString &s)
0826 {
0827     for (int i = 0; i < combo->count(); ++i) {
0828         if (combo->itemText(i) == s) {
0829             return i;
0830         }
0831     }
0832     return -1;
0833 }
0834 
0835 void FindFilesDialog::updateListItems(KComboBox *combo)
0836 {
0837     QString s = combo->currentText();
0838     if (s.isEmpty()) {
0839         return;
0840     }
0841 
0842     int index = findListItem(combo, s);
0843     if (index > 0) {                                 // combo already contains s
0844         combo->removeItem(index);                   // remove this item
0845     }
0846     else {
0847         if (index == -1) {                          // combo doesn't contain s
0848             if (combo->count() >= KILEGREP_MAX) {
0849                 combo->removeItem(combo->count() - 1);   // remove last item
0850             }
0851         }
0852     }
0853 
0854     if(index != 0) {
0855         combo->insertItem(0, s);                    // insert this item as first item
0856         combo->setCurrentIndex(0);                   // and select it
0857     }
0858 }
0859 
0860 ///////////////////// template /////////////////////
0861 
0862 QString FindFilesDialog::getCommandList(KileDocument::CmdAttribute attrtype)
0863 {
0864     QStringList cmdlist;
0865     QStringList::ConstIterator it;
0866 
0867     // get info about user-defined references
0868     KileDocument::LatexCommands *cmd = m_ki->latexCommands();
0869     cmd->commandList(cmdlist, attrtype, true);
0870 
0871     // build list of references
0872     QString commands;
0873     for (it = cmdlist.constBegin(); it != cmdlist.constEnd(); ++it) {
0874         commands += '|' + (*it).mid(1);
0875     }
0876     return commands;
0877 }
0878 
0879 }