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