00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kategrepdialog.h"
00022 #include "katemainwindow.h"
00023
00024 #include <qobject.h>
00025 #include <qlayout.h>
00026 #include <qlineedit.h>
00027 #include <qlabel.h>
00028 #include <qcombobox.h>
00029 #include <qcheckbox.h>
00030 #include <qevent.h>
00031 #include <qlistbox.h>
00032 #include <qregexp.h>
00033 #include <qwhatsthis.h>
00034 #include <qcursor.h>
00035
00036 #include <kapplication.h>
00037 #include <kaccelmanager.h>
00038 #include <kbuttonbox.h>
00039 #include <kfiledialog.h>
00040 #include <kprocess.h>
00041 #include <kapplication.h>
00042 #include <klocale.h>
00043 #include <kiconloader.h>
00044 #include <kmessagebox.h>
00045 #include <kpushbutton.h>
00046 #include <kurlrequester.h>
00047 #include <kurlcompletion.h>
00048 #include <kcombobox.h>
00049 #include <klineedit.h>
00050
00051 const char *template_desc[] = {
00052 "normal",
00053 "assignment",
00054 "->MEMBER(",
00055 "class::MEMBER(",
00056 "OBJECT->member(",
00057 0
00058 };
00059
00060 const char *strTemplate[] = {
00061 "%s",
00062 "\\<%s\\>[\t ]*=[^=]",
00063 "\\->[\\t ]*\\<%s\\>[\\t ]*(",
00064 "[a-z0-9_$]\\+[\\t ]*::[\\t ]*\\<%s\\>[\\t ]*(",
00065 "\\<%s\\>[\\t ]*\\->[\\t ]*[a-z0-9_$]\\+[\\t ]*(",
00066 0
00067 };
00068
00069
00070 GrepTool::GrepTool(KateMainWindow *parent, const char *name)
00071 : QWidget(parent, name), childproc(0)
00072 {
00073 setCaption(i18n("Find in Files"));
00074 config = KGlobal::config();
00075 config->setGroup("GrepTool");
00076 lastSearchItems = config->readListEntry("LastSearchItems");
00077 lastSearchPaths = config->readListEntry("LastSearchPaths");
00078 lastSearchFiles = config->readListEntry("LastSearchFiles");
00079
00080 QGridLayout *layout = new QGridLayout(this, 6, 3, 4, 4);
00081 layout->setColStretch(0, 10);
00082 layout->addColSpacing(1, 10);
00083 layout->setColStretch(1, 0);
00084 layout->setColStretch(2, 1);
00085 layout->setRowStretch(1, 0);
00086 layout->setRowStretch(2, 10);
00087 layout->setRowStretch(4, 0);
00088
00089 QGridLayout *loInput = new QGridLayout(4, 2, 4);
00090 layout->addLayout(loInput, 0, 0);
00091 loInput->setColStretch(0, 0);
00092 loInput->setColStretch(1, 20);
00093
00094 QLabel *lPattern = new QLabel(i18n("Pattern:"), this);
00095 lPattern->setFixedSize(lPattern->sizeHint());
00096 loInput->addWidget(lPattern, 0, 0, AlignRight | AlignVCenter);
00097
00098 QBoxLayout *loPattern = new QHBoxLayout( 4 );
00099 loInput->addLayout( loPattern, 0, 1 );
00100 cmbPattern = new QComboBox(true, this);
00101 cmbPattern->setDuplicatesEnabled(false);
00102 cmbPattern->insertStringList(lastSearchItems);
00103 cmbPattern->setEditText(QString::null);
00104 cmbPattern->setInsertionPolicy(QComboBox::NoInsertion);
00105 lPattern->setBuddy(cmbPattern);
00106 cmbPattern->setFocus();
00107 cmbPattern->setMinimumSize(cmbPattern->sizeHint());
00108 loPattern->addWidget( cmbPattern );
00109
00110 cbCasesensitive = new QCheckBox(i18n("Case sensitive"), this);
00111 cbCasesensitive->setMinimumWidth(cbCasesensitive->sizeHint().width());
00112 cbCasesensitive->setChecked(config->readBoolEntry("CaseSensitive", true));
00113 loPattern->addWidget(cbCasesensitive);
00114
00115 cbRegex = new QCheckBox( i18n("Regular expression"), this );
00116 cbRegex->setMinimumWidth( cbRegex->sizeHint().width() );
00117 cbRegex->setChecked( config->readBoolEntry( "Regex", true ) );
00118 loPattern->addWidget( cbRegex );
00119 loPattern->setStretchFactor( cmbPattern, 100 );
00120
00121 QLabel *lTemplate = new QLabel(i18n("Template:"), this);
00122 lTemplate->setFixedSize(lTemplate->sizeHint());
00123 loInput->addWidget(lTemplate, 1, 0, AlignRight | AlignVCenter);
00124
00125 QBoxLayout *loTemplate = new QHBoxLayout(4);
00126 loInput->addLayout(loTemplate, 1, 1);
00127
00128 leTemplate = new QLineEdit(this);
00129 lTemplate->setBuddy(leTemplate);
00130 leTemplate->setText(strTemplate[0]);
00131 leTemplate->setMinimumSize(leTemplate->sizeHint());
00132 loTemplate->addWidget(leTemplate);
00133
00134 QComboBox *cmbTemplate = new QComboBox(false, this);
00135 cmbTemplate->insertStrList(template_desc);
00136 cmbTemplate->adjustSize();
00137 cmbTemplate->setFixedSize(cmbTemplate->size());
00138 loTemplate->addWidget(cmbTemplate);
00139
00140 QLabel *lFiles = new QLabel(i18n("Files:"), this);
00141 lFiles->setFixedSize(lFiles->sizeHint());
00142 loInput->addWidget(lFiles, 2, 0, AlignRight | AlignVCenter);
00143
00144 cmbFiles = new QComboBox(true, this);
00145 lFiles->setBuddy(cmbFiles->focusProxy());
00146 cmbFiles->setMinimumSize(cmbFiles->sizeHint());
00147 cmbFiles->setDuplicatesEnabled(false);
00148 cmbFiles->insertStringList(lastSearchFiles);
00149 cmbFiles->insertItem("*.h,*.hxx,*.cpp,*.cc,*.C,*.cxx,*.idl,*.c");
00150 cmbFiles->insertItem("*.cpp,*.cc,*.C,*.cxx,*.c");
00151 cmbFiles->insertItem("*.h,*.hxx,*.idl");
00152 cmbFiles->insertItem("*");
00153 loInput->addWidget(cmbFiles, 2, 1);
00154
00155 QLabel *lDir = new QLabel(i18n("Directory:"), this);
00156 lDir->setFixedSize(lDir->sizeHint());
00157 loInput->addWidget(lDir, 3, 0, AlignRight | AlignVCenter);
00158
00159 QBoxLayout *loDir = new QHBoxLayout(3);
00160 loInput->addLayout(loDir, 3, 1);
00161
00162 KComboBox* cmbUrl = new KComboBox(true, this);
00163 cmbUrl->setMinimumWidth(80);
00164 cmbUrl->setDuplicatesEnabled(false);
00165 cmbDir = new KURLRequester( cmbUrl, this, "dir combo" );
00166 cmbDir->completionObject()->setMode(KURLCompletion::DirCompletion);
00167 cmbDir->comboBox()->insertStringList(lastSearchPaths);
00168 cmbDir->setMode( KFile::Directory|KFile::LocalOnly );
00169 loDir->addWidget(cmbDir, 1);
00170 lDir->setBuddy(cmbDir);
00171
00172 cbRecursive = new QCheckBox(i18n("Recursive"), this);
00173 cbRecursive->setMinimumWidth(cbRecursive->sizeHint().width());
00174 cbRecursive->setChecked(config->readBoolEntry("Recursive", true));
00175 loDir->addWidget(cbRecursive);
00176
00177 KButtonBox *actionbox = new KButtonBox(this, Qt::Vertical);
00178 layout->addWidget(actionbox, 0, 2);
00179 actionbox->addStretch();
00180 btnSearch = static_cast<KPushButton*>(actionbox->addButton(KGuiItem(i18n("Find"),"find")));
00181 btnSearch->setDefault(true);
00182 btnClear = static_cast<KPushButton*>(actionbox->addButton( KStdGuiItem::clear() ));
00183 actionbox->addStretch();
00184 actionbox->layout();
00185
00186 lbResult = new QListBox(this);
00187 QFontMetrics rb_fm(lbResult->fontMetrics());
00188 layout->addMultiCellWidget(lbResult, 2, 2, 0, 2);
00189
00190 layout->activate();
00191
00192 KAcceleratorManager::manage( this );
00193
00194 QWhatsThis::add(cmbPattern,
00195 i18n("<p>Enter the expression you want to search for here."
00196 "<p>If 'regular expression' is unchecked, any non-space letters in your "
00197 "expression will be escaped with a backslash character."
00198 "<p>Possible meta characters are:<br>"
00199 "<b>.</b> - Matches any character<br>"
00200 "<b>^</b> - Matches the beginning of a line<br>"
00201 "<b>$</b> - Matches the end of a line<br>"
00202 "<b>\\<</b> - Matches the beginning of a word<br>"
00203 "<b>\\></b> - Matches the end of a word"
00204 "<p>The following repetition operators exist:<br>"
00205 "<b>?</b> - The preceding item is matched at most once<br>"
00206 "<b>*</b> - The preceding item is matched zero or more times<br>"
00207 "<b>+</b> - The preceding item is matched one or more times<br>"
00208 "<b>{<i>n</i>}</b> - The preceding item is matched exactly <i>n</i> times<br>"
00209 "<b>{<i>n</i>,}</b> - The preceding item is matched <i>n</i> or more times<br>"
00210 "<b>{,<i>n</i>}</b> - The preceding item is matched at most <i>n</i> times<br>"
00211 "<b>{<i>n</i>,<i>m</i>}</b> - The preceding item is matched at least <i>n</i>, "
00212 "but at most <i>m</i> times."
00213 "<p>Furthermore, backreferences to bracketed subexpressions are available "
00214 "via the notation <code>\\#</code>."
00215 "<p>See the grep(1) documentation for the full documentation."
00216 ));
00217 QWhatsThis::add(cmbFiles,
00218 i18n("Enter the file name pattern of the files to search here.\n"
00219 "You may give several patterns separated by commas."));
00220 QWhatsThis::add(leTemplate,
00221 i18n("You can choose a template for the pattern from the combo box\n"
00222 "and edit it here. The string %s in the template is replaced\n"
00223 "by the pattern input field, resulting in the regular expression\n"
00224 "to search for."));
00225 QWhatsThis::add(cmbDir,
00226 i18n("Enter the directory which contains the files you want to search in."));
00227 QWhatsThis::add(cbRecursive,
00228 i18n("Check this box to search in all subdirectories."));
00229 QWhatsThis::add(cbCasesensitive,
00230 i18n("If this option is enabled (the default), the search will be case sensitive."));
00231 QWhatsThis::add( cbRegex, i18n(
00232 "<p>If this is enabled, your pattern will be passed unmodified to "
00233 "<em>grep(1)</em>. Otherwise, all characters that are not letters will be "
00234 "escaped using a backslash character to prevent grep from interpreting "
00235 "them as part of the expression.") );
00236 QWhatsThis::add(lbResult,
00237 i18n("The results of the grep run are listed here. Select a\n"
00238 "filename/line number combination and press Enter or doubleclick\n"
00239 "on the item to show the respective line in the editor."));
00240
00241
00242 cmbPattern->installEventFilter( this );
00243 leTemplate->installEventFilter( this );
00244 cmbPattern->installEventFilter( this );
00245 cmbFiles->installEventFilter( this );
00246 cmbDir->comboBox()->installEventFilter( this );
00247
00248 connect( cmbTemplate, SIGNAL(activated(int)),
00249 SLOT(templateActivated(int)) );
00250 connect( lbResult, SIGNAL(selected(const QString&)),
00251 SLOT(itemSelected(const QString&)) );
00252 connect( btnSearch, SIGNAL(clicked()),
00253 SLOT(slotSearch()) );
00254 connect( btnClear, SIGNAL(clicked()),
00255 SLOT(slotClear()) );
00256 connect( cmbPattern->lineEdit(), SIGNAL(textChanged ( const QString & )),
00257 SLOT( patternTextChanged( const QString & )));
00258
00259 patternTextChanged( cmbPattern->lineEdit()->text());
00260 }
00261
00262
00263 GrepTool::~GrepTool()
00264 {
00265 delete childproc;
00266 }
00267
00268 void GrepTool::patternTextChanged( const QString & _text)
00269 {
00270 btnSearch->setEnabled( !_text.isEmpty() );
00271 }
00272
00273 void GrepTool::templateActivated(int index)
00274 {
00275 leTemplate->setText(strTemplate[index]);
00276 }
00277
00278 void GrepTool::itemSelected(const QString& item)
00279 {
00280 int pos;
00281 QString filename, linenumber;
00282
00283 QString str = item;
00284 if ( (pos = str.find(':')) != -1)
00285 {
00286 filename = str.left(pos);
00287 str = str.mid(pos+1);
00288 if ( (pos = str.find(':')) != -1)
00289 {
00290 filename = m_workingDir + QDir::separator() + filename;
00291 linenumber = str.left(pos);
00292 emit itemSelected(filename,linenumber.toInt()-1);
00293 }
00294 }
00295 }
00296
00297 void GrepTool::processOutput()
00298 {
00299 int pos;
00300 while ( (pos = buf.find('\n')) != -1)
00301 {
00302 QString item = buf.mid(2,pos-2);
00303 if (!item.isEmpty())
00304 lbResult->insertItem(item);
00305 buf = buf.mid(pos+1);
00306 }
00307 kapp->processEvents();
00308 }
00309
00310 void GrepTool::slotSearch()
00311 {
00312 if ( cmbPattern->currentText().isEmpty() )
00313 {
00314 cmbPattern->setFocus();
00315 return;
00316 }
00317
00318 if ( cmbDir->url().isEmpty() || ! QDir(cmbDir->url()).exists() )
00319 {
00320 cmbDir->setFocus();
00321 return;
00322 }
00323
00324 if ( ! leTemplate->text().contains("%s") )
00325 {
00326 leTemplate->setFocus();
00327 return;
00328 }
00329
00330 if ( childproc && childproc->isRunning() )
00331 {
00332 childproc->kill();
00333 return;
00334 }
00335
00336 slotClear ();
00337
00338 m_workingDir = cmbDir->url();
00339
00340 QString s = cmbPattern->currentText();
00341 if ( ! cbRegex->isChecked() )
00342 s.replace( QRegExp( "([^\\w'])" ), "\\\\1" );
00343 QString pattern = leTemplate->text();
00344 pattern.replace( "%s", s );
00345
00346 childproc = new KProcess();
00347 childproc->setWorkingDirectory( m_workingDir );
00348 *childproc << "find" << ".";
00349 if (!cbRecursive->isChecked())
00350 *childproc << "-maxdepth" << "1";
00351 if (!cmbFiles->currentText().isEmpty() )
00352 {
00353 QStringList files = QStringList::split ( ",", cmbFiles->currentText(), FALSE );
00354 *childproc << "(" << "-false";
00355 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00356 *childproc << "-o" << "-name" << (*it);
00357 *childproc << ")";
00358 }
00359 *childproc << "-exec" << "grep";
00360 if (!cbCasesensitive->isChecked())
00361 *childproc << "-i";
00362 *childproc << "-n" << "-e" << pattern << "{}";
00363 *childproc << "/dev/null";
00364 *childproc << ";";
00365
00366 connect( childproc, SIGNAL(processExited(KProcess *)),
00367 SLOT(childExited()) );
00368 connect( childproc, SIGNAL(receivedStdout(KProcess *, char *, int)),
00369 SLOT(receivedOutput(KProcess *, char *, int)) );
00370 connect( childproc, SIGNAL(receivedStderr(KProcess *, char *, int)),
00371 SLOT(receivedErrOutput(KProcess *, char *, int)) );
00372
00373
00374 lbResult->setCursor( QCursor(Qt::WaitCursor) );
00375 btnClear->setEnabled( false );
00376 btnSearch->setGuiItem( KGuiItem(i18n("Cancel"), "button_cancel"));
00377 childproc->start(KProcess::NotifyOnExit, KProcess::AllOutput);
00378 }
00379
00380 void GrepTool::slotSearchFor(const QString &pattern)
00381 {
00382 slotClear();
00383 cmbPattern->setEditText(pattern);
00384 slotSearch();
00385 }
00386
00387 void GrepTool::finish()
00388 {
00389 btnSearch->setEnabled( !cmbPattern->lineEdit()->text().isEmpty() );
00390
00391 buf += '\n';
00392 processOutput();
00393 delete childproc;
00394 childproc = 0;
00395
00396 config->setGroup("GrepTool");
00397
00398 if (lastSearchItems.contains(cmbPattern->currentText()) == 0)
00399 {
00400 cmbPattern->insertItem(cmbPattern->currentText(), 0);
00401 lastSearchItems.prepend(cmbPattern->currentText());
00402 if (lastSearchItems.count() > 10) {
00403 lastSearchItems.remove(lastSearchItems.fromLast());
00404 cmbPattern->removeItem(cmbPattern->count() - 1);
00405 }
00406 config->writeEntry("LastSearchItems", lastSearchItems);
00407 }
00408
00409 if (lastSearchPaths.contains(cmbDir->url()) == 0)
00410 {
00411 cmbDir->comboBox()->insertItem(cmbDir->url(), 0);
00412 lastSearchPaths.prepend(cmbDir->url());
00413 if (lastSearchPaths.count() > 10)
00414 {
00415 lastSearchPaths.remove(lastSearchPaths.fromLast());
00416 cmbDir->comboBox()->removeItem(cmbDir->comboBox()->count() - 1);
00417 }
00418 config->writeEntry("LastSearchPaths", lastSearchPaths);
00419 }
00420
00421 if (lastSearchFiles.contains(cmbFiles->currentText()) == 0)
00422 {
00423 cmbFiles->insertItem(cmbFiles->currentText(), 0);
00424 lastSearchFiles.prepend(cmbFiles->currentText());
00425 if (lastSearchItems.count() > 10) {
00426 lastSearchFiles.remove(lastSearchFiles.fromLast());
00427 cmbFiles->removeItem(cmbFiles->count() - 1);
00428 }
00429 config->writeEntry("LastSearchFiles", lastSearchFiles);
00430 }
00431
00432 config->writeEntry("Recursive", cbRecursive->isChecked());
00433 config->writeEntry("CaseSensitive", cbCasesensitive->isChecked());
00434 config->writeEntry( "Regex", cbRegex->isChecked() );
00435 }
00436
00437 void GrepTool::slotCancel()
00438 {
00439 finish();
00440 }
00441
00442 void GrepTool::childExited()
00443 {
00444
00445 lbResult->unsetCursor();
00446 btnClear->setEnabled( true );
00447 btnSearch->setGuiItem( KGuiItem(i18n("Find"), "find") );
00448
00449 if ( ! errbuf.isEmpty() )
00450 {
00451 KMessageBox::information( parentWidget(), i18n("<strong>Error:</strong><p>") + errbuf, i18n("Grep Tool Error") );
00452 errbuf.truncate(0);
00453 }
00454 else
00455 finish();
00456 }
00457
00458 void GrepTool::receivedOutput(KProcess *, char *buffer, int buflen)
00459 {
00460 buf += QCString(buffer, buflen+1);
00461 processOutput();
00462 }
00463
00464 void GrepTool::receivedErrOutput(KProcess *, char *buffer, int buflen)
00465 {
00466 errbuf += QCString( buffer, buflen + 1 );
00467 }
00468
00469 void GrepTool::slotClear()
00470 {
00471 finish();
00472 lbResult->clear();
00473 }
00474
00475 void GrepTool::updateDirName(const QString &dir)
00476 {
00477 if (m_lastUpdatedDir != dir)
00478 {
00479 setDirName (dir);
00480 m_lastUpdatedDir = dir;
00481 }
00482 }
00483
00484 void GrepTool::setDirName(const QString &dir){
00485 cmbDir->setURL(dir);
00486 }
00487
00488 bool GrepTool::eventFilter( QObject *o, QEvent *e )
00489 {
00490 if ( e->type() == QEvent::KeyPress && (
00491 ((QKeyEvent*)e)->key() == Qt::Key_Return ||
00492 ((QKeyEvent*)e)->key() == Qt::Key_Enter ) )
00493 {
00494 slotSearch();
00495 return true;
00496 }
00497
00498 return QWidget::eventFilter( o, e );
00499 }
00500
00501 #include "kategrepdialog.moc"