kategrepdialog.cpp
00001 /* This file is part of the KDE project 00002 Copyright (C) 2001 Christoph Cullmann <cullmann@kde.org> 00003 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00004 Copyright (C) 2001, 2004 Anders Lund <anders.lund@lund.tdcadsl.dk> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License version 2 as published by the Free Software Foundation. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kategrepdialog.h" 00022 #include "katemainwindow.h" 00023 00024 #include <tqobject.h> 00025 #include <tqlayout.h> 00026 #include <tqlabel.h> 00027 #include <tqcheckbox.h> 00028 #include <tqevent.h> 00029 #include <tqlistbox.h> 00030 #include <tqregexp.h> 00031 #include <tqwhatsthis.h> 00032 #include <tqcursor.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(TQWidget *parent, const char *name) 00069 : TQWidget(parent, name/*, false*/), 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 // if there are no entries, most probably the first Kate start. 00081 // Initialize with default values. 00082 lastSearchFiles << "*.h,*.hxx,*.cpp,*.cc,*.C,*.cxx,*.idl,*.c" 00083 << "*.cpp,*.cc,*.C,*.cxx,*.c" 00084 << "*.h,*.hxx,*.idl" 00085 << "*"; 00086 } 00087 00088 TQGridLayout *layout = new TQGridLayout(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 TQGridLayout *loInput = new TQGridLayout(4, 2, 4); 00098 layout->addLayout(loInput, 0, 0); 00099 loInput->setColStretch(0, 0); 00100 loInput->setColStretch(1, 20); 00101 00102 TQLabel *lPattern = new TQLabel(i18n("Pattern:"), this); 00103 lPattern->setFixedSize(lPattern->sizeHint()); 00104 loInput->addWidget(lPattern, 0, 0, Qt::AlignRight | Qt::AlignVCenter); 00105 00106 TQBoxLayout *loPattern = new TQHBoxLayout( 4 ); 00107 loInput->addLayout( loPattern, 0, 1 ); 00108 cmbPattern = new KComboBox(true, this); 00109 cmbPattern->setDuplicatesEnabled(false); 00110 cmbPattern->insertStringList(lastSearchItems); 00111 cmbPattern->setEditText(TQString::null); 00112 cmbPattern->setInsertionPolicy(TQComboBox::NoInsertion); 00113 lPattern->setBuddy(cmbPattern); 00114 cmbPattern->setFocus(); 00115 cmbPattern->setMinimumSize(cmbPattern->sizeHint()); 00116 loPattern->addWidget( cmbPattern ); 00117 00118 cbCasesensitive = new TQCheckBox(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 TQCheckBox( 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 TQLabel *lTemplate = new TQLabel(i18n("Template:"), this); 00130 lTemplate->setFixedSize(lTemplate->sizeHint()); 00131 loInput->addWidget(lTemplate, 1, 0, Qt::AlignRight | Qt::AlignVCenter); 00132 00133 TQBoxLayout *loTemplate = new TQHBoxLayout(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 TQLabel *lFiles = new TQLabel(i18n("Files:"), this); 00149 lFiles->setFixedSize(lFiles->sizeHint()); 00150 loInput->addWidget(lFiles, 2, 0, Qt::AlignRight | Qt::AlignVCenter); 00151 00152 cmbFiles = new KComboBox(true, this); 00153 lFiles->setBuddy(TQT_TQWIDGET(cmbFiles->focusProxy())); 00154 cmbFiles->setMinimumSize(cmbFiles->sizeHint()); 00155 cmbFiles->setInsertionPolicy(TQComboBox::NoInsertion); 00156 cmbFiles->setDuplicatesEnabled(false); 00157 cmbFiles->insertStringList(lastSearchFiles); 00158 loInput->addWidget(cmbFiles, 2, 1); 00159 00160 TQLabel *lDir = new TQLabel(i18n("Folder:"), this); 00161 lDir->setFixedSize(lDir->sizeHint()); 00162 loInput->addWidget(lDir, 3, 0, Qt::AlignRight | Qt::AlignVCenter); 00163 00164 TQBoxLayout *loDir = new TQHBoxLayout(3); 00165 loInput->addLayout(loDir, 3, 1); 00166 00167 KComboBox* cmbUrl = new KComboBox(true, this); 00168 cmbUrl->setMinimumWidth(80); // make sure that 800x600 res works 00169 cmbUrl->setDuplicatesEnabled(false); 00170 cmbUrl->setInsertionPolicy(TQComboBox::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 TQCheckBox(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 TQListBox(this); 00193 TQFontMetrics rb_fm(lbResult->fontMetrics()); 00194 layout->addMultiCellWidget(lbResult, 2, 2, 0, 2); 00195 00196 layout->activate(); 00197 00198 KAcceleratorManager::manage( this ); 00199 00200 TQWhatsThis::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 TQWhatsThis::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 TQWhatsThis::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 TQWhatsThis::add(lDir, 00232 i18n("Enter the folder which contains the files in which you want to search.")); 00233 TQWhatsThis::add(cbRecursive, 00234 i18n("Check this box to search in all subfolders.")); 00235 TQWhatsThis::add(cbCasesensitive, 00236 i18n("If this option is enabled (the default), the search will be case sensitive.")); 00237 TQWhatsThis::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 TQWhatsThis::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 // event filter, do something relevant for RETURN 00248 cmbPattern->installEventFilter( this ); 00249 leTemplate->installEventFilter( this ); 00250 cmbFiles->installEventFilter( this ); 00251 cmbDir->comboBox()->installEventFilter( this ); 00252 00253 connect( cmbTemplate, TQT_SIGNAL(activated(int)), 00254 TQT_SLOT(templateActivated(int)) ); 00255 connect( lbResult, TQT_SIGNAL(selected(const TQString&)), 00256 TQT_SLOT(itemSelected(const TQString&)) ); 00257 connect( btnSearch, TQT_SIGNAL(clicked()), 00258 TQT_SLOT(slotSearch()) ); 00259 connect( btnClear, TQT_SIGNAL(clicked()), 00260 TQT_SLOT(slotClear()) ); 00261 connect( cmbPattern->lineEdit(), TQT_SIGNAL(textChanged ( const TQString & )), 00262 TQT_SLOT( patternTextChanged( const TQString & ))); 00263 00264 patternTextChanged( cmbPattern->lineEdit()->text()); 00265 } 00266 00267 00268 GrepTool::~GrepTool() 00269 { 00270 delete childproc; 00271 } 00272 00273 void GrepTool::patternTextChanged( const TQString & _text) 00274 { 00275 btnSearch->setEnabled( !_text.isEmpty() ); 00276 } 00277 00278 void GrepTool::templateActivated(int index) 00279 { 00280 leTemplate->setText(strTemplate[index]); 00281 } 00282 00283 void GrepTool::itemSelected(const TQString& item) 00284 { 00285 int pos; 00286 TQString filename, linenumber; 00287 00288 TQString str = item; 00289 if ( (pos = str.find(':')) != -1) 00290 { 00291 filename = str.left(pos); 00292 str = str.mid(pos+1); 00293 if ( (pos = str.find(':')) != -1) 00294 { 00295 filename = m_workingDir + TQDir::separator() + filename; 00296 linenumber = str.left(pos); 00297 emit itemSelected(filename,linenumber.toInt()-1); 00298 } 00299 } 00300 } 00301 00302 void GrepTool::processOutput() 00303 { 00304 int pos; 00305 while ( (pos = buf.find('\n')) != -1) 00306 { 00307 TQString item = buf.mid(2,pos-2); 00308 if (!item.isEmpty()) 00309 lbResult->insertItem(item); 00310 buf = buf.mid(pos+1); 00311 } 00312 kapp->processEvents(); 00313 } 00314 00315 void GrepTool::slotSearch() 00316 { 00317 if ( cmbPattern->currentText().isEmpty() ) 00318 { 00319 cmbPattern->setFocus(); 00320 return; 00321 } 00322 00323 if ( cmbDir->url().isEmpty() || ! TQDir(cmbDir->url()).exists() ) 00324 { 00325 cmbDir->setFocus(); 00326 KMessageBox::information( this, i18n( 00327 "You must enter an existing local folder in the 'Folder' entry."), 00328 i18n("Invalid Folder"), "Kate grep tool: invalid folder" ); 00329 return; 00330 } 00331 00332 if ( ! leTemplate->text().contains("%s") ) 00333 { 00334 leTemplate->setFocus(); 00335 return; 00336 } 00337 00338 if ( childproc && childproc->isRunning() ) 00339 { 00340 childproc->kill(); 00341 return; 00342 } 00343 00344 slotClear (); 00345 00346 m_workingDir = cmbDir->url(); 00347 00348 TQString s = cmbPattern->currentText(); 00349 if ( ! cbRegex->isChecked() ) 00350 s.replace( TQRegExp( "([^\\w'()<>])" ), "\\\\1" ); 00351 TQString pattern = leTemplate->text(); 00352 pattern.replace( "%s", s ); 00353 00354 childproc = new KProcess(); 00355 childproc->setWorkingDirectory( m_workingDir ); 00356 *childproc << "find" << "."; 00357 if (!cbRecursive->isChecked()) 00358 *childproc << "-maxdepth" << "1"; 00359 if (!cmbFiles->currentText().isEmpty() ) 00360 { 00361 TQStringList files = TQStringList::split ( ",", cmbFiles->currentText(), FALSE ); 00362 *childproc << "("; 00363 bool first = true; 00364 for ( TQStringList::Iterator it = files.begin(); it != files.end(); ++it ) 00365 { 00366 if (!first) 00367 *childproc << "-o"; 00368 *childproc << "-name" << (*it); 00369 first = false; 00370 } 00371 *childproc << ")"; 00372 } 00373 *childproc << "-exec" << "grep"; 00374 if (!cbCasesensitive->isChecked()) 00375 *childproc << "-i"; 00376 *childproc << "-n" << "-e" << pattern << "{}"; 00377 *childproc << "/dev/null"; //trick to have grep always display the filename 00378 *childproc << ";"; 00379 00380 connect( childproc, TQT_SIGNAL(processExited(KProcess *)), 00381 TQT_SLOT(childExited()) ); 00382 connect( childproc, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)), 00383 TQT_SLOT(receivedOutput(KProcess *, char *, int)) ); 00384 connect( childproc, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)), 00385 TQT_SLOT(receivedErrOutput(KProcess *, char *, int)) ); 00386 00387 // actually it should be checked whether the process was started successfully 00388 lbResult->setCursor( TQCursor(Qt::WaitCursor) ); 00389 btnClear->setEnabled( false ); 00390 btnSearch->setGuiItem( KGuiItem(i18n("Cancel"), "button_cancel")); 00391 childproc->start(KProcess::NotifyOnExit, KProcess::AllOutput); 00392 } 00393 00394 void GrepTool::slotSearchFor(const TQString &pattern) 00395 { 00396 slotClear(); 00397 cmbPattern->setEditText(pattern); 00398 slotSearch(); 00399 } 00400 00401 void GrepTool::finish() 00402 { 00403 btnSearch->setEnabled( !cmbPattern->lineEdit()->text().isEmpty() ); 00404 00405 buf += '\n'; 00406 processOutput(); 00407 delete childproc; 00408 childproc = 0; 00409 00410 config->setGroup("GrepTool"); 00411 00412 TQString cmbText = cmbPattern->currentText(); 00413 bool itemsRemoved = lastSearchItems.remove(cmbText) > 0; 00414 lastSearchItems.prepend(cmbText); 00415 if (itemsRemoved) 00416 { 00417 cmbPattern->removeItem(cmbPattern->currentItem()); 00418 } 00419 cmbPattern->insertItem(cmbText, 0); 00420 cmbPattern->setCurrentItem(0); 00421 if (lastSearchItems.count() > 10) { 00422 lastSearchItems.pop_back(); 00423 cmbPattern->removeItem(cmbPattern->count() - 1); 00424 } 00425 config->writeEntry("LastSearchItems", lastSearchItems); 00426 00427 00428 cmbText = cmbDir->url(); 00429 itemsRemoved = lastSearchPaths.remove(cmbText) > 0; 00430 lastSearchPaths.prepend(cmbText); 00431 if (itemsRemoved) 00432 { 00433 cmbDir->comboBox()->removeItem(cmbDir->comboBox()->currentItem()); 00434 } 00435 cmbDir->comboBox()->insertItem(cmbText, 0); 00436 cmbDir->comboBox()->setCurrentItem(0); 00437 if (lastSearchPaths.count() > 10) 00438 { 00439 lastSearchPaths.pop_back(); 00440 cmbDir->comboBox()->removeItem(cmbDir->comboBox()->count() - 1); 00441 } 00442 config->writeEntry("LastSearchPaths", lastSearchPaths); 00443 00444 00445 cmbText = cmbFiles->currentText(); 00446 itemsRemoved = lastSearchFiles.remove(cmbText) > 0; 00447 lastSearchFiles.prepend(cmbText); 00448 if (itemsRemoved) 00449 { 00450 cmbFiles->removeItem(cmbFiles->currentItem()); 00451 } 00452 cmbFiles->insertItem(cmbText, 0); 00453 cmbFiles->setCurrentItem(0); 00454 if (lastSearchFiles.count() > 10) { 00455 lastSearchFiles.pop_back(); 00456 cmbFiles->removeItem(cmbFiles->count() - 1); 00457 } 00458 config->writeEntry("LastSearchFiles", lastSearchFiles); 00459 00460 config->writeEntry("Recursive", cbRecursive->isChecked()); 00461 config->writeEntry("CaseSensitive", cbCasesensitive->isChecked()); 00462 config->writeEntry("Regex", cbRegex->isChecked()); 00463 } 00464 00465 void GrepTool::slotCancel() 00466 { 00467 finish(); 00468 } 00469 00470 void GrepTool::childExited() 00471 { 00472 // int status = childproc->exitStatus(); 00473 lbResult->unsetCursor(); 00474 btnClear->setEnabled( true ); 00475 btnSearch->setGuiItem( KGuiItem(i18n("Find"), "find") ); 00476 00477 if ( ! errbuf.isEmpty() ) 00478 { 00479 KMessageBox::information( parentWidget(), i18n("<strong>Error:</strong><p>") + errbuf, i18n("Grep Tool Error") ); 00480 errbuf.truncate(0); 00481 } 00482 else 00483 finish(); 00484 } 00485 00486 void GrepTool::receivedOutput(KProcess */*proc*/, char *buffer, int buflen) 00487 { 00488 buf += TQCString(buffer, buflen+1); 00489 processOutput(); 00490 } 00491 00492 void GrepTool::receivedErrOutput(KProcess */*proc*/, char *buffer, int buflen) 00493 { 00494 errbuf += TQCString( buffer, buflen + 1 ); 00495 } 00496 00497 void GrepTool::slotClear() 00498 { 00499 finish(); 00500 lbResult->clear(); 00501 } 00502 00503 void GrepTool::updateDirName(const TQString &dir) 00504 { 00505 if (m_lastUpdatedDir != dir) 00506 { 00507 setDirName (dir); 00508 m_lastUpdatedDir = dir; 00509 } 00510 } 00511 00512 void GrepTool::setDirName(const TQString &dir){ 00513 cmbDir->setURL(dir); 00514 } 00515 00516 bool GrepTool::eventFilter( TQObject *o, TQEvent *e ) 00517 { 00518 if ( e->type() == TQEvent::KeyPress && ( 00519 ((TQKeyEvent*)e)->key() == Qt::Key_Return || 00520 ((TQKeyEvent*)e)->key() == Qt::Key_Enter ) ) 00521 { 00522 slotSearch(); 00523 return true; 00524 } 00525 00526 return TQWidget::eventFilter( o, e ); 00527 } 00528 00529 void GrepTool::focusInEvent ( TQFocusEvent * ev ) 00530 { 00531 TQWidget::focusInEvent(ev); 00532 if (m_fixFocus) { 00533 m_fixFocus = false; 00534 cmbPattern->setFocus(); 00535 } 00536 } 00537 00538 void GrepTool::showEvent( TQShowEvent * ev ) 00539 { 00540 TQWidget::showEvent(ev); 00541 m_fixFocus = true; 00542 } 00543 00544 #include "kategrepdialog.moc"