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