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 uint newlines = replaceWith.contains('\n');
00369 if ( newlines )
00370 {
00371 if ( ! s.flags.backward )
00372 {
00373 s.cursor.setLine( s.cursor.line() + newlines );
00374 s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') );
00375 }
00376
00377 if ( s.flags.selected )
00378 s.selEnd.setLine( s.selEnd.line() + newlines );
00379 }
00380
00381
00382 if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
00383 {
00384 s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
00385 }
00386
00387
00388 if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col())
00389 {
00390 s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength );
00391 }
00392
00393 if( !s.flags.backward ) {
00394 s.cursor.setCol(s.cursor.col() + replaceWith.length());
00395 } else if( s.cursor.col() > 0 ) {
00396 s.cursor.setCol(s.cursor.col() - 1);
00397 } else {
00398 s.cursor.setLine(s.cursor.line() - 1);
00399 if( s.cursor.line() >= 0 ) {
00400 s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00401 }
00402 }
00403 }
00404
00405 void KateSearch::skipOne()
00406 {
00407 if( !s.flags.backward ) {
00408 s.cursor.setCol(s.cursor.col() + s.matchedLength);
00409 } else if( s.cursor.col() > 0 ) {
00410 s.cursor.setCol(s.cursor.col() - 1);
00411 } else {
00412 s.cursor.setLine(s.cursor.line() - 1);
00413 if( s.cursor.line() >= 0 ) {
00414 s.cursor.setCol(doc()->lineLength(s.cursor.line()));
00415 }
00416 }
00417 }
00418
00419 void KateSearch::replaceSlot() {
00420 switch( (Dialog_results)replacePrompt->result() ) {
00421 case srCancel: replacePrompt->hide(); break;
00422 case srAll: replacePrompt->hide(); replaceAll(); break;
00423 case srYes: replaceOne(); promptReplace(); break;
00424 case srLast: replacePrompt->hide(), replaceOne(); break;
00425 case srNo: skipOne(); promptReplace(); break;
00426 }
00427 }
00428
00429 bool KateSearch::askContinue()
00430 {
00431 QString made =
00432 i18n( "%n replacement made.",
00433 "%n replacements made.",
00434 replaces );
00435
00436 QString reached = !s.flags.backward ?
00437 i18n( "End of document reached." ) :
00438 i18n( "Beginning of document reached." );
00439
00440 if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
00441 {
00442 reached = !s.flags.backward ?
00443 i18n( "End of selection reached." ) :
00444 i18n( "Beginning of selection reached." );
00445 }
00446
00447 QString question = !s.flags.backward ?
00448 i18n( "Continue from the beginning?" ) :
00449 i18n( "Continue from the end?" );
00450
00451 QString text = s.flags.replace ?
00452 made + "\n" + reached + "\n" + question :
00453 reached + "\n" + question;
00454
00455 return KMessageBox::Yes == KMessageBox::questionYesNo(
00456 view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
00457 KStdGuiItem::cont(), i18n("&Stop") );
00458 }
00459
00460 QString KateSearch::getSearchText()
00461 {
00462
00463
00464
00465
00466 QString str;
00467
00468 int getFrom = view()->config()->textToSearchMode();
00469 switch (getFrom)
00470 {
00471 case KateViewConfig::SelectionOnly:
00472
00473 if( doc()->hasSelection() )
00474 str = doc()->selection();
00475 break;
00476
00477 case KateViewConfig::SelectionWord:
00478
00479 if( doc()->hasSelection() )
00480 str = doc()->selection();
00481 else
00482 str = view()->currentWord();
00483 break;
00484
00485 case KateViewConfig::WordOnly:
00486
00487 str = view()->currentWord();
00488 break;
00489
00490 case KateViewConfig::WordSelection:
00491
00492 str = view()->currentWord();
00493 if (str.isEmpty() && doc()->hasSelection() )
00494 str = doc()->selection();
00495 break;
00496
00497 default:
00498
00499 break;
00500 }
00501
00502 str.replace( QRegExp("^\\n"), "" );
00503 str.replace( QRegExp("\\n.*"), "" );
00504
00505 return str;
00506 }
00507
00508 KateTextCursor KateSearch::getCursor()
00509 {
00510 return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
00511 }
00512
00513 bool KateSearch::doSearch( const QString& text )
00514 {
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531 #if 0
00532 static int oldLine = -1;
00533 static int oldCol = -1;
00534 #endif
00535
00536 uint line = s.cursor.line();
00537 uint col = s.cursor.col();
00538 bool backward = s.flags.backward;
00539 bool caseSensitive = s.flags.caseSensitive;
00540 bool regExp = s.flags.regExp;
00541 bool wholeWords = s.flags.wholeWords;
00542 uint foundLine, foundCol, matchLen;
00543 bool found = false;
00544
00545
00546 do {
00547 if( regExp ) {
00548 m_re = QRegExp( text, caseSensitive );
00549 found = doc()->searchText( line, col, m_re,
00550 &foundLine, &foundCol,
00551 &matchLen, backward );
00552 } else if ( wholeWords ) {
00553 QRegExp re( "\\b" + text + "\\b", caseSensitive );
00554 found = doc()->searchText( line, col, re,
00555 &foundLine, &foundCol,
00556 &matchLen, backward );
00557 } else {
00558 found = doc()->searchText( line, col, text,
00559 &foundLine, &foundCol,
00560 &matchLen, caseSensitive, backward );
00561 }
00562
00563 if ( found && s.flags.selected )
00564 {
00565 if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= s.selEnd
00566 || s.flags.backward && KateTextCursor( foundLine, foundCol ) < s.selBegin )
00567 found = false;
00568 else if (m_doc->blockSelectionMode())
00569 {
00570 if ((int)foundCol < s.selEnd.col() && (int)foundCol >= s.selBegin.col())
00571 break;
00572 }
00573 }
00574
00575 line = foundLine;
00576 col = foundCol+1;
00577 }
00578 while (m_doc->blockSelectionMode() && found);
00579
00580 if( !found ) return false;
00581
00582
00583 s.cursor.setPos(foundLine, foundCol);
00584 s.matchedLength = matchLen;
00585
00586
00587 if (s.wrapped)
00588 {
00589 if (s.flags.backward)
00590 {
00591 if ( (s.cursor.line() < s.wrappedEnd.line())
00592 || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) )
00593 return false;
00594 }
00595 else
00596 {
00597 if ( (s.cursor.line() > s.wrappedEnd.line())
00598 || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) )
00599 return false;
00600 }
00601 }
00602
00603
00604
00605
00606
00607
00608 if (arbitraryHLExample) {
00609 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);
00610 hl->setBold();
00611 hl->setTextColor(Qt::white);
00612 hl->setBGColor(Qt::black);
00613
00614 connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated()));
00615 m_arbitraryHLList->append(hl);
00616 }
00617
00618 return true;
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631 }
00632
00633 void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
00634 {
00635 view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
00636 doc()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
00637 }
00638
00639
00640
00641 KateReplacePrompt::KateReplacePrompt ( QWidget *parent )
00642 : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ),
00643 User3 | User2 | User1 | Close | Ok , Ok, true,
00644 i18n("Replace &All"), i18n("Replace && Close"), i18n("&Replace") )
00645 {
00646 setButtonOK( i18n("Find Next") );
00647 QWidget *page = new QWidget(this);
00648 setMainWidget(page);
00649
00650 QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
00651 QLabel *label = new QLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page);
00652 topLayout->addWidget(label );
00653 }
00654
00655 void KateReplacePrompt::slotOk ()
00656 {
00657 done(KateSearch::srNo);
00658 }
00659
00660 void KateReplacePrompt::slotClose ()
00661 {
00662 done(KateSearch::srCancel);
00663 }
00664
00665 void KateReplacePrompt::slotUser1 ()
00666 {
00667 done(KateSearch::srAll);
00668 }
00669
00670 void KateReplacePrompt::slotUser2 ()
00671 {
00672 done(KateSearch::srLast);
00673 }
00674
00675 void KateReplacePrompt::slotUser3 ()
00676 {
00677 done(KateSearch::srYes);
00678 }
00679
00680 void KateReplacePrompt::done (int result)
00681 {
00682 setResult(result);
00683
00684 emit clicked();
00685 }
00686
00687
00688