00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "katesearch.h"
00024 #include "katesearch.moc"
00025
00026 #include "kateview.h"
00027 #include "katedocument.h"
00028 #include "katesupercursor.h"
00029 #include "katearbitraryhighlight.h"
00030 #include "kateconfig.h"
00031
00032 #include <klocale.h>
00033 #include <kstdaction.h>
00034 #include <kmessagebox.h>
00035 #include <kstringhandler.h>
00036 #include <kdebug.h>
00037 #include <kfinddialog.h>
00038 #include <kreplacedialog.h>
00039
00040 #include <qlayout.h>
00041 #include <qlabel.h>
00042
00043 QStringList KateSearch::s_searchList = QStringList();
00044 QStringList KateSearch::s_replaceList = QStringList();
00045 static const bool arbitraryHLExample = false;
00046
00047 KateSearch::KateSearch( KateView* view )
00048 : QObject( view, "kate search" )
00049 , m_view( view )
00050 , m_doc( view->doc() )
00051 , replacePrompt( new ReplacePrompt( view ) )
00052 {
00053 m_arbitraryHLList = new KateSuperRangeList();
00054 if (arbitraryHLExample) m_doc->arbitraryHL()->addHighlightToView(m_arbitraryHLList, m_view);
00055
00056 connect(replacePrompt,SIGNAL(clicked()),this,SLOT(replaceSlot()));
00057 }
00058
00059 KateSearch::~KateSearch()
00060 {
00061 delete m_arbitraryHLList;
00062 }
00063
00064 void KateSearch::createActions( KActionCollection* ac )
00065 {
00066 KStdAction::find( this, SLOT(find()), ac )->setWhatsThis(
00067 i18n("Look up the first occurrence of a piece of text or regular expression."));
00068 KStdAction::findNext( this, SLOT(slotFindNext()), ac )->setWhatsThis(
00069 i18n("Look up the next occurrence of the search phrase."));
00070 KStdAction::findPrev( this, SLOT(slotFindPrev()), ac, "edit_find_prev" )->setWhatsThis(
00071 i18n("Look up the previous occurrence of the search phrase."));
00072 KStdAction::replace( this, SLOT(replace()), ac )->setWhatsThis(
00073 i18n("Look up a piece of text or regular expression and replace the result with some given text."));
00074 }
00075
00076 void KateSearch::addToList( QStringList& list, const QString& s )
00077 {
00078 if( list.count() > 0 ) {
00079 QStringList::Iterator it = list.find( s );
00080 if( *it != 0L )
00081 list.remove( it );
00082 if( list.count() >= 16 )
00083 list.remove( list.fromLast() );
00084 }
00085 list.prepend( s );
00086 }
00087
00088 void KateSearch::find()
00089 {
00090 KFindDialog *findDialog = new KFindDialog ( m_view, "", KateViewConfig::global()->searchFlags(),
00091 s_searchList, m_doc->hasSelection() );
00092
00093 findDialog->setPattern (getSearchText());
00094
00095 if( findDialog->exec() == QDialog::Accepted ) {
00096 s_searchList = findDialog->findHistory () ;
00097 KateViewConfig::global()->setSearchFlags(findDialog->options ());
00098
00099 SearchFlags searchFlags;
00100
00101 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00102 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00103 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00104 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00105 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00106 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00107 searchFlags.prompt = false;
00108 searchFlags.replace = false;
00109 searchFlags.finished = false;
00110 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00111
00112 if ( searchFlags.selected )
00113 {
00114 s.selBegin = KateTextCursor( doc()->selStartLine(), doc()->selStartCol() );
00115 s.selEnd = KateTextCursor( doc()->selEndLine(), doc()->selEndCol() );
00116 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00117 } else {
00118 s.cursor = getCursor();
00119 }
00120
00121 s.wrappedEnd = s.cursor;
00122 s.wrapped = false;
00123
00124 search( searchFlags );
00125 }
00126
00127 delete findDialog;
00128 m_view->repaintText ();
00129 }
00130
00131 void KateSearch::replace()
00132 {
00133 if (!doc()->isReadWrite()) return;
00134
00135 KReplaceDialog *replaceDialog = new KReplaceDialog ( m_view, "", KateViewConfig::global()->searchFlags(),
00136 s_searchList, s_replaceList, m_doc->hasSelection() );
00137
00138 replaceDialog->setPattern (getSearchText());
00139
00140 if( replaceDialog->exec() == QDialog::Accepted ) {
00141 m_replacement = replaceDialog->replacement();
00142 s_searchList = replaceDialog->findHistory () ;
00143 s_replaceList = replaceDialog->replacementHistory () ;
00144 KateViewConfig::global()->setSearchFlags(replaceDialog->options ());
00145
00146 SearchFlags searchFlags;
00147 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00148 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00149 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00150 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00151 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00152 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00153 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
00154 searchFlags.replace = true;
00155 searchFlags.finished = false;
00156 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00157 if ( searchFlags.selected )
00158 {
00159 s.selBegin = KateTextCursor( doc()->selStartLine(), doc()->selStartCol() );
00160 s.selEnd = KateTextCursor( doc()->selEndLine(), doc()->selEndCol() );
00161 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00162 } else {
00163 s.cursor = getCursor();
00164 }
00165
00166 s.wrappedEnd = s.cursor;
00167 s.wrapped = false;
00168
00169 search( searchFlags );
00170 }
00171
00172 delete replaceDialog;
00173 m_view->update ();
00174 }
00175
00176 void KateSearch::findAgain( bool back )
00177 {
00178 SearchFlags searchFlags;
00179 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00180 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00181 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00182 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00183 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00184 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00185 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
00186 searchFlags.replace = false;
00187 searchFlags.finished = false;
00188 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00189
00190 searchFlags.backward = searchFlags.backward != back;
00191 searchFlags.fromBeginning = false;
00192 searchFlags.prompt = true;
00193 s.cursor = getCursor();
00194
00195 search( searchFlags );
00196 }
00197
00198 void KateSearch::search( SearchFlags flags )
00199 {
00200 s.flags = flags;
00201
00202 if( s.flags.fromBeginning ) {
00203 if( !s.flags.backward ) {
00204 s.cursor.setPos(0, 0);
00205 } else {
00206 s.cursor.setLine(doc()->numLines() - 1);
00207 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00208 }
00209 }
00210
00211 if((!s.flags.backward &&
00212 s.cursor.col() == 0 &&
00213 s.cursor.line() == 0 ) ||
00214 ( s.flags.backward &&
00215 s.cursor.col() == doc()->lineLength( s.cursor.line() ) &&
00216 s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) {
00217 s.flags.finished = true;
00218 }
00219
00220 if( s.flags.replace ) {
00221 replaces = 0;
00222 if( s.flags.prompt )
00223 promptReplace();
00224 else
00225 replaceAll();
00226 } else {
00227 findAgain();
00228 }
00229 }
00230
00231 void KateSearch::wrapSearch()
00232 {
00233 if( s.flags.selected )
00234 {
00235 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00236 }
00237 else
00238 {
00239 if( !s.flags.backward ) {
00240 s.cursor.setPos(0, 0);
00241 } else {
00242 s.cursor.setLine(doc()->numLines() - 1);
00243 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00244 }
00245 }
00246
00247
00248
00249 s.wrapped = s.flags.replace;
00250
00251 replaces = 0;
00252 s.flags.finished = true;
00253 }
00254
00255 void KateSearch::findAgain()
00256 {
00257 QString searchFor = s_searchList.first();
00258
00259 if( searchFor.isEmpty() ) {
00260 find();
00261 return;
00262 }
00263
00264 if ( doSearch( searchFor ) ) {
00265 exposeFound( s.cursor, s.matchedLength );
00266 } else if( !s.flags.finished ) {
00267 if( askContinue() ) {
00268 wrapSearch();
00269 findAgain();
00270 } else {
00271 if (arbitraryHLExample) m_arbitraryHLList->clear();
00272 }
00273 } else {
00274 if (arbitraryHLExample) m_arbitraryHLList->clear();
00275 KMessageBox::sorry( view(),
00276 i18n("Search string '%1' not found!")
00277 .arg( KStringHandler::csqueeze( searchFor ) ),
00278 i18n("Find"));
00279 }
00280 }
00281
00282 void KateSearch::replaceAll()
00283 {
00284 QString searchFor = s_searchList.first();
00285
00286 doc()->editStart ();
00287
00288 while( doSearch( searchFor ) )
00289 replaceOne();
00290
00291 doc()->editEnd ();
00292
00293 if( !s.flags.finished ) {
00294 if( askContinue() ) {
00295 wrapSearch();
00296 replaceAll();
00297 }
00298 } else {
00299 KMessageBox::information( view(),
00300 i18n("%n replacement made.","%n replacements made.",replaces),
00301 i18n("Replace") );
00302 }
00303 }
00304
00305 void KateSearch::promptReplace()
00306 {
00307 QString searchFor = s_searchList.first();
00308 if ( doSearch( searchFor ) ) {
00309 exposeFound( s.cursor, s.matchedLength );
00310 replacePrompt->show();
00311 replacePrompt->setFocus();
00312 } else if( !s.flags.finished && askContinue() ) {
00313 wrapSearch();
00314 promptReplace();
00315 } else {
00316 if (arbitraryHLExample) m_arbitraryHLList->clear();
00317 replacePrompt->hide();
00318 KMessageBox::information( view(),
00319 i18n("%n replacement made.","%n replacements made.",replaces),
00320 i18n("Replace") );
00321 }
00322 }
00323
00324 void KateSearch::replaceOne()
00325 {
00326 QString replaceWith = m_replacement;
00327 if ( s.flags.regExp ) {
00328
00329 QRegExp br("\\\\(\\d+)");
00330 int pos = br.search( replaceWith );
00331 int ncaps = m_re.numCaptures();
00332 while ( pos >= 0 ) {
00333 QString sc;
00334 if ( !pos || replaceWith.at( pos-1) != '\\' ) {
00335 int ccap = br.cap(1).toInt();
00336 if (ccap <= ncaps ) {
00337 sc = m_re.cap( ccap );
00338 replaceWith.replace( pos, br.matchedLength(), sc );
00339 }
00340 else {
00341
00342 kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<m_re.pattern()<<"'"<<endl;
00343 }
00344 }
00345 pos = br.search( replaceWith, pos+QMAX(br.matchedLength(), (int)sc.length()) );
00346 }
00347 }
00348
00349 doc()->editStart();
00350 doc()->removeText( s.cursor.line(), s.cursor.col(),
00351 s.cursor.line(), s.cursor.col() + s.matchedLength );
00352 doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith );
00353 doc()->editEnd(),
00354
00355 replaces++;
00356
00357 if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
00358 {
00359 s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
00360 }
00361
00362 if( !s.flags.backward ) {
00363 s.cursor.setCol(s.cursor.col() + replaceWith.length());
00364 } else if( s.cursor.col() > 0 ) {
00365 s.cursor.setCol(s.cursor.col() - 1);
00366 } else {
00367 s.cursor.setLine(s.cursor.line() - 1);
00368 if( s.cursor.line() >= 0 ) {
00369 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00370 }
00371 }
00372 }
00373
00374 void KateSearch::skipOne()
00375 {
00376 if( !s.flags.backward ) {
00377 s.cursor.setCol(s.cursor.col() + s.matchedLength);
00378 } else if( s.cursor.col() > 0 ) {
00379 s.cursor.setCol(s.cursor.col() - 1);
00380 } else {
00381 s.cursor.setLine(s.cursor.line() - 1);
00382 if( s.cursor.line() >= 0 ) {
00383 s.cursor.setCol(doc()->lineLength(s.cursor.line()));
00384 }
00385 }
00386 }
00387
00388 void KateSearch::replaceSlot() {
00389 switch( (Dialog_results)replacePrompt->result() ) {
00390 case srCancel: replacePrompt->hide(); break;
00391 case srAll: replacePrompt->hide(); replaceAll(); break;
00392 case srYes: replaceOne(); promptReplace(); break;
00393 case srLast: replacePrompt->hide(), replaceOne(); break;
00394 case srNo: skipOne(); promptReplace(); break;
00395 }
00396 }
00397
00398 bool KateSearch::askContinue()
00399 {
00400 QString made =
00401 i18n( "%n replacement made.",
00402 "%n replacements made.",
00403 replaces );
00404
00405 QString reached = !s.flags.backward ?
00406 i18n( "End of document reached." ) :
00407 i18n( "Beginning of document reached." );
00408
00409 if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
00410 {
00411 reached = !s.flags.backward ?
00412 i18n( "End of selection reached." ) :
00413 i18n( "Beginning of selection reached." );
00414 }
00415
00416 QString question = !s.flags.backward ?
00417 i18n( "Continue from the beginning?" ) :
00418 i18n( "Continue from the end?" );
00419
00420 QString text = s.flags.replace ?
00421 made + "\n" + reached + "\n" + question :
00422 reached + "\n" + question;
00423
00424 return KMessageBox::Yes == KMessageBox::questionYesNo(
00425 view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
00426 KStdGuiItem::cont(), i18n("&Stop") );
00427 }
00428
00429 QString KateSearch::getSearchText()
00430 {
00431
00432
00433
00434
00435 QString str;
00436
00437 int getFrom = view()->config()->textToSearchMode();
00438 switch (getFrom)
00439 {
00440 case KateViewConfig::SelectionOnly:
00441
00442 if( doc()->hasSelection() )
00443 str = doc()->selection();
00444 break;
00445
00446 case KateViewConfig::SelectionWord:
00447
00448 if( doc()->hasSelection() )
00449 str = doc()->selection();
00450 else
00451 str = view()->currentWord();
00452 break;
00453
00454 case KateViewConfig::WordOnly:
00455
00456 str = view()->currentWord();
00457 break;
00458
00459 case KateViewConfig::WordSelection:
00460
00461 str = view()->currentWord();
00462 if (str.isEmpty() && doc()->hasSelection() )
00463 str = doc()->selection();
00464 break;
00465
00466 default:
00467
00468 break;
00469 }
00470
00471 str.replace( QRegExp("^\\n"), "" );
00472 str.replace( QRegExp("\\n.*"), "" );
00473
00474 return str;
00475 }
00476
00477 KateTextCursor KateSearch::getCursor()
00478 {
00479 return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
00480 }
00481
00482 bool KateSearch::doSearch( const QString& text )
00483 {
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 #if 0
00501 static int oldLine = -1;
00502 static int oldCol = -1;
00503 #endif
00504
00505 uint line = s.cursor.line();
00506 uint col = s.cursor.col();
00507 bool backward = s.flags.backward;
00508 bool caseSensitive = s.flags.caseSensitive;
00509 bool regExp = s.flags.regExp;
00510 bool wholeWords = s.flags.wholeWords;
00511 uint foundLine, foundCol, matchLen;
00512 bool found = false;
00513
00514 if( regExp ) {
00515 m_re = QRegExp( text, caseSensitive );
00516 found = doc()->searchText( line, col, m_re,
00517 &foundLine, &foundCol,
00518 &matchLen, backward );
00519 } else if ( wholeWords ) {
00520 QRegExp re( "\\b" + text + "\\b", caseSensitive );
00521 found = doc()->searchText( line, col, re,
00522 &foundLine, &foundCol,
00523 &matchLen, backward );
00524 } else {
00525 found = doc()->searchText( line, col, text,
00526 &foundLine, &foundCol,
00527 &matchLen, caseSensitive, backward );
00528 }
00529 if ( found && s.flags.selected )
00530 {
00531 if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= s.selEnd
00532 || s.flags.backward && KateTextCursor( foundLine, foundCol ) < s.selBegin )
00533 found = false;
00534 }
00535 if( !found ) return false;
00536
00537
00538
00539
00540 s.cursor.setPos(foundLine, foundCol);
00541 s.matchedLength = matchLen;
00542
00543
00544 if (s.wrapped)
00545 {
00546 if (s.flags.backward)
00547 {
00548 if (s.cursor < s.wrappedEnd)
00549 return false;
00550 }
00551 else
00552 {
00553 if ( (s.cursor.line() > s.wrappedEnd.line())
00554 || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) > uint(s.wrappedEnd.col())) ) )
00555 return false;
00556 }
00557 }
00558
00559
00560
00561
00562
00563
00564 if (arbitraryHLExample) {
00565 ArbitraryHighlightRange* hl = new ArbitraryHighlightRange(new KateSuperCursor(m_doc, true, s.cursor), new KateSuperCursor(m_doc, true, s.cursor.line(), s.cursor.col() + s.matchedLength), this);
00566 hl->setBold();
00567 hl->setTextColor(Qt::white);
00568 hl->setBGColor(Qt::black);
00569
00570 connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated()));
00571 m_arbitraryHLList->append(hl);
00572 }
00573
00574 return true;
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587 }
00588
00589 void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
00590 {
00591 view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
00592 doc()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
00593 }
00594
00595
00596
00597 ReplacePrompt::ReplacePrompt( QWidget *parent )
00598 : KDialogBase(parent, 0L, false, i18n( "Replace Text" ),
00599 User3 | User2 | User1 | Close | Ok , Ok, true,
00600 i18n("&All"), i18n("&Last"), i18n("&No") ) {
00601
00602 setButtonOK( KStdGuiItem::yes() );
00603 QWidget *page = new QWidget(this);
00604 setMainWidget(page);
00605
00606 QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
00607 QLabel *label = new QLabel(i18n("Replace this occurrence?"),page);
00608 topLayout->addWidget(label );
00609 }
00610
00611 void ReplacePrompt::slotOk( void ) {
00612 done(KateSearch::srYes);
00613 }
00614
00615 void ReplacePrompt::slotClose( void ) {
00616 done(KateSearch::srCancel);
00617 }
00618
00619 void ReplacePrompt::slotUser1( void ) {
00620 done(KateSearch::srAll);
00621 }
00622
00623 void ReplacePrompt::slotUser2( void ) {
00624 done(KateSearch::srLast);
00625 }
00626
00627 void ReplacePrompt::slotUser3( void ) {
00628 done(KateSearch::srNo);
00629 }
00630
00631 void ReplacePrompt::done(int r) {
00632 setResult(r);
00633 emit clicked();
00634 }
00635
00636
00637