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