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 KateReplacePrompt( 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
00091 long searchf = KateViewConfig::global()->searchFlags();
00092 if (m_doc->hasSelection() && m_doc->selStartLine() != m_doc->selEndLine())
00093 searchf |= KFindDialog::SelectedText;
00094
00095 KFindDialog *findDialog = new KFindDialog ( m_view, "", searchf,
00096 s_searchList, m_doc->hasSelection() );
00097
00098 findDialog->setPattern (getSearchText());
00099
00100
00101 if( findDialog->exec() == QDialog::Accepted ) {
00102 s_searchList = findDialog->findHistory () ;
00103 KateViewConfig::global()->setSearchFlags(findDialog->options ());
00104
00105 SearchFlags searchFlags;
00106
00107 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00108 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00109 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00110 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00111 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00112 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00113 searchFlags.prompt = false;
00114 searchFlags.replace = false;
00115 searchFlags.finished = false;
00116 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00117
00118 if ( searchFlags.selected )
00119 {
00120 s.selBegin = KateTextCursor( doc()->selStartLine(), doc()->selStartCol() );
00121 s.selEnd = KateTextCursor( doc()->selEndLine(), doc()->selEndCol() );
00122 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00123 } else {
00124 s.cursor = getCursor();
00125 }
00126
00127 s.wrappedEnd = s.cursor;
00128 s.wrapped = false;
00129
00130 search( searchFlags );
00131 }
00132
00133 delete findDialog;
00134 m_view->repaintText ();
00135 }
00136
00137 void KateSearch::replace()
00138 {
00139 if (!doc()->isReadWrite()) return;
00140
00141
00142 long searchf = KateViewConfig::global()->searchFlags();
00143 if (m_doc->hasSelection() && m_doc->selStartLine() != m_doc->selEndLine())
00144 searchf |= KFindDialog::SelectedText;
00145
00146 KReplaceDialog *replaceDialog = new KReplaceDialog ( m_view, "", searchf,
00147 s_searchList, s_replaceList, m_doc->hasSelection() );
00148
00149 replaceDialog->setPattern (getSearchText());
00150
00151 if( replaceDialog->exec() == QDialog::Accepted ) {
00152 m_replacement = replaceDialog->replacement();
00153 s_searchList = replaceDialog->findHistory () ;
00154 s_replaceList = replaceDialog->replacementHistory () ;
00155 KateViewConfig::global()->setSearchFlags(replaceDialog->options ());
00156
00157 SearchFlags searchFlags;
00158 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00159 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00160 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00161 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00162 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00163 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00164 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
00165 searchFlags.replace = true;
00166 searchFlags.finished = false;
00167 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00168 if ( searchFlags.selected )
00169 {
00170 s.selBegin = KateTextCursor( doc()->selStartLine(), doc()->selStartCol() );
00171 s.selEnd = KateTextCursor( doc()->selEndLine(), doc()->selEndCol() );
00172 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00173 } else {
00174 s.cursor = getCursor();
00175 }
00176
00177 s.wrappedEnd = s.cursor;
00178 s.wrapped = false;
00179
00180 search( searchFlags );
00181 }
00182
00183 delete replaceDialog;
00184 m_view->update ();
00185 }
00186
00187 void KateSearch::findAgain( bool back )
00188 {
00189 SearchFlags searchFlags;
00190 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive;
00191 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly;
00192 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor)
00193 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText);
00194 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards;
00195 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText;
00196 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace;
00197 searchFlags.replace = false;
00198 searchFlags.finished = false;
00199 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression;
00200
00201 searchFlags.backward = searchFlags.backward != back;
00202 searchFlags.fromBeginning = false;
00203 searchFlags.prompt = true;
00204 s.cursor = getCursor();
00205
00206 search( searchFlags );
00207 }
00208
00209 void KateSearch::search( SearchFlags flags )
00210 {
00211 s.flags = flags;
00212
00213 if( s.flags.fromBeginning ) {
00214 if( !s.flags.backward ) {
00215 s.cursor.setPos(0, 0);
00216 } else {
00217 s.cursor.setLine(doc()->numLines() - 1);
00218 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00219 }
00220 }
00221
00222 if((!s.flags.backward &&
00223 s.cursor.col() == 0 &&
00224 s.cursor.line() == 0 ) ||
00225 ( s.flags.backward &&
00226 s.cursor.col() == doc()->lineLength( s.cursor.line() ) &&
00227 s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) {
00228 s.flags.finished = true;
00229 }
00230
00231 if( s.flags.replace ) {
00232 replaces = 0;
00233 if( s.flags.prompt )
00234 promptReplace();
00235 else
00236 replaceAll();
00237 } else {
00238 findAgain();
00239 }
00240 }
00241
00242 void KateSearch::wrapSearch()
00243 {
00244 if( s.flags.selected )
00245 {
00246 s.cursor = s.flags.backward ? s.selEnd : s.selBegin;
00247 }
00248 else
00249 {
00250 if( !s.flags.backward ) {
00251 s.cursor.setPos(0, 0);
00252 } else {
00253 s.cursor.setLine(doc()->numLines() - 1);
00254 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00255 }
00256 }
00257
00258
00259
00260 s.wrapped = s.flags.replace;
00261
00262 replaces = 0;
00263 s.flags.finished = true;
00264 }
00265
00266 void KateSearch::findAgain()
00267 {
00268 QString searchFor = s_searchList.first();
00269
00270 if( searchFor.isEmpty() ) {
00271 find();
00272 return;
00273 }
00274
00275 if ( doSearch( searchFor ) ) {
00276 exposeFound( s.cursor, s.matchedLength );
00277 } else if( !s.flags.finished ) {
00278 if( askContinue() ) {
00279 wrapSearch();
00280 findAgain();
00281 } else {
00282 if (arbitraryHLExample) m_arbitraryHLList->clear();
00283 }
00284 } else {
00285 if (arbitraryHLExample) m_arbitraryHLList->clear();
00286 KMessageBox::sorry( view(),
00287 i18n("Search string '%1' not found!")
00288 .arg( KStringHandler::csqueeze( searchFor ) ),
00289 i18n("Find"));
00290 }
00291 }
00292
00293 void KateSearch::replaceAll()
00294 {
00295 QString searchFor = s_searchList.first();
00296
00297 doc()->editStart ();
00298
00299 while( doSearch( searchFor ) )
00300 replaceOne();
00301
00302 doc()->editEnd ();
00303
00304 if( !s.flags.finished ) {
00305 if( askContinue() ) {
00306 wrapSearch();
00307 replaceAll();
00308 }
00309 } else {
00310 KMessageBox::information( view(),
00311 i18n("%n replacement made.","%n replacements made.",replaces),
00312 i18n("Replace") );
00313 }
00314 }
00315
00316 void KateSearch::promptReplace()
00317 {
00318 QString searchFor = s_searchList.first();
00319 if ( doSearch( searchFor ) ) {
00320 exposeFound( s.cursor, s.matchedLength );
00321 replacePrompt->show();
00322 replacePrompt->setFocus ();
00323 } else if( !s.flags.finished && askContinue() ) {
00324 wrapSearch();
00325 promptReplace();
00326 } else {
00327 if (arbitraryHLExample) m_arbitraryHLList->clear();
00328 replacePrompt->hide();
00329 KMessageBox::information( view(),
00330 i18n("%n replacement made.","%n replacements made.",replaces),
00331 i18n("Replace") );
00332 }
00333 }
00334
00335 void KateSearch::replaceOne()
00336 {
00337 QString replaceWith = m_replacement;
00338 if ( s.flags.regExp ) {
00339
00340 QRegExp br("\\\\(\\d+)");
00341 int pos = br.search( replaceWith );
00342 int ncaps = m_re.numCaptures();
00343 while ( pos >= 0 ) {
00344 QString sc;
00345 if ( !pos || replaceWith.at( pos-1) != '\\' ) {
00346 int ccap = br.cap(1).toInt();
00347 if (ccap <= ncaps ) {
00348 sc = m_re.cap( ccap );
00349 replaceWith.replace( pos, br.matchedLength(), sc );
00350 }
00351 else {
00352
00353 kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<m_re.pattern()<<"'"<<endl;
00354 }
00355 }
00356 pos = br.search( replaceWith, pos+QMAX(br.matchedLength(), (int)sc.length()) );
00357 }
00358 }
00359
00360 doc()->editStart();
00361 doc()->removeText( s.cursor.line(), s.cursor.col(),
00362 s.cursor.line(), s.cursor.col() + s.matchedLength );
00363 doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith );
00364 doc()->editEnd(),
00365
00366 replaces++;
00367
00368
00369 if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
00370 {
00371 s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
00372 }
00373
00374
00375 if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col())
00376 {
00377 s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength );
00378 }
00379
00380 if( !s.flags.backward ) {
00381 s.cursor.setCol(s.cursor.col() + replaceWith.length());
00382 } else if( s.cursor.col() > 0 ) {
00383 s.cursor.setCol(s.cursor.col() - 1);
00384 } else {
00385 s.cursor.setLine(s.cursor.line() - 1);
00386 if( s.cursor.line() >= 0 ) {
00387 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00388 }
00389 }
00390 }
00391
00392 void KateSearch::skipOne()
00393 {
00394 if( !s.flags.backward ) {
00395 s.cursor.setCol(s.cursor.col() + s.matchedLength);
00396 } else if( s.cursor.col() > 0 ) {
00397 s.cursor.setCol(s.cursor.col() - 1);
00398 } else {
00399 s.cursor.setLine(s.cursor.line() - 1);
00400 if( s.cursor.line() >= 0 ) {
00401 s.cursor.setCol(doc()->lineLength(s.cursor.line()));
00402 }
00403 }
00404 }
00405
00406 void KateSearch::replaceSlot() {
00407 switch( (Dialog_results)replacePrompt->result() ) {
00408 case srCancel: replacePrompt->hide(); break;
00409 case srAll: replacePrompt->hide(); replaceAll(); break;
00410 case srYes: replaceOne(); promptReplace(); break;
00411 case srLast: replacePrompt->hide(), replaceOne(); break;
00412 case srNo: skipOne(); promptReplace(); break;
00413 }
00414 }
00415
00416 bool KateSearch::askContinue()
00417 {
00418 QString made =
00419 i18n( "%n replacement made.",
00420 "%n replacements made.",
00421 replaces );
00422
00423 QString reached = !s.flags.backward ?
00424 i18n( "End of document reached." ) :
00425 i18n( "Beginning of document reached." );
00426
00427 if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
00428 {
00429 reached = !s.flags.backward ?
00430 i18n( "End of selection reached." ) :
00431 i18n( "Beginning of selection reached." );
00432 }
00433
00434 QString question = !s.flags.backward ?
00435 i18n( "Continue from the beginning?" ) :
00436 i18n( "Continue from the end?" );
00437
00438 QString text = s.flags.replace ?
00439 made + "\n" + reached + "\n" + question :
00440 reached + "\n" + question;
00441
00442 return KMessageBox::Yes == KMessageBox::questionYesNo(
00443 view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
00444 KStdGuiItem::cont(), i18n("&Stop") );
00445 }
00446
00447 QString KateSearch::getSearchText()
00448 {
00449
00450
00451
00452
00453 QString str;
00454
00455 int getFrom = view()->config()->textToSearchMode();
00456 switch (getFrom)
00457 {
00458 case KateViewConfig::SelectionOnly:
00459
00460 if( doc()->hasSelection() )
00461 str = doc()->selection();
00462 break;
00463
00464 case KateViewConfig::SelectionWord:
00465
00466 if( doc()->hasSelection() )
00467 str = doc()->selection();
00468 else
00469 str = view()->currentWord();
00470 break;
00471
00472 case KateViewConfig::WordOnly:
00473
00474 str = view()->currentWord();
00475 break;
00476
00477 case KateViewConfig::WordSelection:
00478
00479 str = view()->currentWord();
00480 if (str.isEmpty() && doc()->hasSelection() )
00481 str = doc()->selection();
00482 break;
00483
00484 default:
00485
00486 break;
00487 }
00488
00489 str.replace( QRegExp("^\\n"), "" );
00490 str.replace( QRegExp("\\n.*"), "" );
00491
00492 return str;
00493 }
00494
00495 KateTextCursor KateSearch::getCursor()
00496 {
00497 return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
00498 }
00499
00500 bool KateSearch::doSearch( const QString& text )
00501 {
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518 #if 0
00519 static int oldLine = -1;
00520 static int oldCol = -1;
00521 #endif
00522
00523 uint line = s.cursor.line();
00524 uint col = s.cursor.col();
00525 bool backward = s.flags.backward;
00526 bool caseSensitive = s.flags.caseSensitive;
00527 bool regExp = s.flags.regExp;
00528 bool wholeWords = s.flags.wholeWords;
00529 uint foundLine, foundCol, matchLen;
00530 bool found = false;
00531
00532
00533 do {
00534 if( regExp ) {
00535 m_re = QRegExp( text, caseSensitive );
00536 found = doc()->searchText( line, col, m_re,
00537 &foundLine, &foundCol,
00538 &matchLen, backward );
00539 } else if ( wholeWords ) {
00540 QRegExp re( "\\b" + text + "\\b", caseSensitive );
00541 found = doc()->searchText( line, col, re,
00542 &foundLine, &foundCol,
00543 &matchLen, backward );
00544 } else {
00545 found = doc()->searchText( line, col, text,
00546 &foundLine, &foundCol,
00547 &matchLen, caseSensitive, backward );
00548 }
00549
00550 if ( found && s.flags.selected )
00551 {
00552 if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= s.selEnd
00553 || s.flags.backward && KateTextCursor( foundLine, foundCol ) < s.selBegin )
00554 found = false;
00555 else if (m_doc->blockSelectionMode())
00556 {
00557 if ((int)foundCol < s.selEnd.col() && (int)foundCol >= s.selBegin.col())
00558 break;
00559 }
00560 }
00561
00562 line = foundLine;
00563 col = foundCol+1;
00564 }
00565 while (m_doc->blockSelectionMode() && found);
00566
00567 if( !found ) return false;
00568
00569
00570 s.cursor.setPos(foundLine, foundCol);
00571 s.matchedLength = matchLen;
00572
00573
00574 if (s.wrapped)
00575 {
00576 if (s.flags.backward)
00577 {
00578 if ( (s.cursor.line() < s.wrappedEnd.line())
00579 || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) )
00580 return false;
00581 }
00582 else
00583 {
00584 if ( (s.cursor.line() > s.wrappedEnd.line())
00585 || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) )
00586 return false;
00587 }
00588 }
00589
00590
00591
00592
00593
00594
00595 if (arbitraryHLExample) {
00596 KateArbitraryHighlightRange* hl = new KateArbitraryHighlightRange(new KateSuperCursor(m_doc, true, s.cursor), new KateSuperCursor(m_doc, true, s.cursor.line(), s.cursor.col() + s.matchedLength), this);
00597 hl->setBold();
00598 hl->setTextColor(Qt::white);
00599 hl->setBGColor(Qt::black);
00600
00601 connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated()));
00602 m_arbitraryHLList->append(hl);
00603 }
00604
00605 return true;
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618 }
00619
00620 void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
00621 {
00622 view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
00623 doc()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
00624 }
00625
00626
00627
00628 KateReplacePrompt::KateReplacePrompt ( QWidget *parent )
00629 : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ),
00630 User3 | User2 | User1 | Close | Ok , Ok, true,
00631 i18n("Replace &All"), i18n("Replace && Close"), i18n("&Replace") )
00632 {
00633 setButtonOK( i18n("Find Next") );
00634 QWidget *page = new QWidget(this);
00635 setMainWidget(page);
00636
00637 QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
00638 QLabel *label = new QLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page);
00639 topLayout->addWidget(label );
00640 }
00641
00642 void KateReplacePrompt::slotOk ()
00643 {
00644 done(KateSearch::srNo);
00645 }
00646
00647 void KateReplacePrompt::slotClose ()
00648 {
00649 done(KateSearch::srCancel);
00650 }
00651
00652 void KateReplacePrompt::slotUser1 ()
00653 {
00654 done(KateSearch::srAll);
00655 }
00656
00657 void KateReplacePrompt::slotUser2 ()
00658 {
00659 done(KateSearch::srLast);
00660 }
00661
00662 void KateReplacePrompt::slotUser3 ()
00663 {
00664 done(KateSearch::srYes);
00665 }
00666
00667 void KateReplacePrompt::done (int result)
00668 {
00669 setResult(result);
00670
00671 emit clicked();
00672 }
00673
00674
00675