katesearch.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2004-2005 Anders Lund <anders@alweb.dk> 00003 Copyright (C) 2003 Clarence Dang <dang@kde.org> 00004 Copyright (C) 2002 John Firebaugh <jfirebaugh@kde.org> 00005 Copyright (C) 2001-2004 Christoph Cullmann <cullmann@kde.org> 00006 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00007 Copyright (C) 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License version 2 as published by the Free Software Foundation. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "katesearch.h" 00025 #include "katesearch.moc" 00026 00027 #include "kateview.h" 00028 #include "katedocument.h" 00029 #include "katesupercursor.h" 00030 #include "katearbitraryhighlight.h" 00031 #include "kateconfig.h" 00032 #include "katehighlight.h" 00033 00034 #include <klocale.h> 00035 #include <kstdaction.h> 00036 #include <kmessagebox.h> 00037 #include <kstringhandler.h> 00038 #include <kdebug.h> 00039 #include <kfinddialog.h> 00040 #include <kreplacedialog.h> 00041 #include <kpushbutton.h> 00042 00043 #include <qlayout.h> 00044 #include <qlabel.h> 00045 00046 //BEGIN KateSearch 00047 QStringList KateSearch::s_searchList = QStringList(); 00048 QStringList KateSearch::s_replaceList = QStringList(); 00049 QString KateSearch::s_pattern = QString(); 00050 static const bool arbitraryHLExample = false; 00051 00052 KateSearch::KateSearch( KateView* view ) 00053 : QObject( view, "kate search" ) 00054 , m_view( view ) 00055 , m_doc( view->doc() ) 00056 , replacePrompt( new KateReplacePrompt( view ) ) 00057 { 00058 m_arbitraryHLList = new KateSuperRangeList(); 00059 if (arbitraryHLExample) m_doc->arbitraryHL()->addHighlightToView(m_arbitraryHLList, m_view); 00060 00061 connect(replacePrompt,SIGNAL(clicked()),this,SLOT(replaceSlot())); 00062 } 00063 00064 KateSearch::~KateSearch() 00065 { 00066 delete m_arbitraryHLList; 00067 } 00068 00069 void KateSearch::createActions( KActionCollection* ac ) 00070 { 00071 KStdAction::find( this, SLOT(find()), ac )->setWhatsThis( 00072 i18n("Look up the first occurrence of a piece of text or regular expression.")); 00073 KStdAction::findNext( this, SLOT(slotFindNext()), ac )->setWhatsThis( 00074 i18n("Look up the next occurrence of the search phrase.")); 00075 KStdAction::findPrev( this, SLOT(slotFindPrev()), ac, "edit_find_prev" )->setWhatsThis( 00076 i18n("Look up the previous occurrence of the search phrase.")); 00077 KStdAction::replace( this, SLOT(replace()), ac )->setWhatsThis( 00078 i18n("Look up a piece of text or regular expression and replace the result with some given text.")); 00079 } 00080 00081 void KateSearch::addToList( QStringList& list, const QString& s ) 00082 { 00083 if( list.count() > 0 ) { 00084 QStringList::Iterator it = list.find( s ); 00085 if( *it != 0L ) 00086 list.remove( it ); 00087 if( list.count() >= 16 ) 00088 list.remove( list.fromLast() ); 00089 } 00090 list.prepend( s ); 00091 } 00092 00093 void KateSearch::find() 00094 { 00095 // if multiline selection around, search in it 00096 long searchf = KateViewConfig::global()->searchFlags(); 00097 if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine()) 00098 searchf |= KFindDialog::SelectedText; 00099 00100 KFindDialog *findDialog = new KFindDialog ( m_view, "", searchf, 00101 s_searchList, m_view->hasSelection() ); 00102 00103 findDialog->setPattern (getSearchText()); 00104 00105 00106 if( findDialog->exec() == QDialog::Accepted ) { 00107 s_searchList = findDialog->findHistory () ; 00108 // Do *not* remove the QString() wrapping, it fixes a nasty crash 00109 find( QString(s_searchList.first()), findDialog->options(), true, true ); 00110 } 00111 00112 delete findDialog; 00113 m_view->repaintText (); 00114 } 00115 00116 void KateSearch::find( const QString &pattern, long flags, bool add, bool shownotfound ) 00117 { 00118 KateViewConfig::global()->setSearchFlags( flags ); 00119 if( add ) 00120 addToList( s_searchList, pattern ); 00121 00122 s_pattern = pattern; 00123 00124 SearchFlags searchFlags; 00125 00126 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive; 00127 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly; 00128 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor) 00129 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText); 00130 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards; 00131 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText; 00132 searchFlags.prompt = false; 00133 searchFlags.replace = false; 00134 searchFlags.finished = false; 00135 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression; 00136 searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference; 00137 00138 if ( searchFlags.selected ) 00139 { 00140 s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() ); 00141 s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() ); 00142 s.cursor = s.flags.backward ? s.selEnd : s.selBegin; 00143 } else { 00144 s.cursor = getCursor( searchFlags ); 00145 } 00146 00147 s.wrappedEnd = s.cursor; 00148 s.wrapped = false; 00149 s.showNotFound = shownotfound; 00150 00151 search( searchFlags ); 00152 } 00153 00154 void KateSearch::replace() 00155 { 00156 if (!doc()->isReadWrite()) return; 00157 00158 // if multiline selection around, search in it 00159 long searchf = KateViewConfig::global()->searchFlags(); 00160 if (m_view->hasSelection() && m_view->selStartLine() != m_view->selEndLine()) 00161 searchf |= KFindDialog::SelectedText; 00162 00163 KReplaceDialog *replaceDialog = new KReplaceDialog ( m_view, "", searchf, 00164 s_searchList, s_replaceList, m_view->hasSelection() ); 00165 00166 replaceDialog->setPattern (getSearchText()); 00167 00168 if( replaceDialog->exec() == QDialog::Accepted ) { 00169 long opts = replaceDialog->options(); 00170 m_replacement = replaceDialog->replacement(); 00171 s_searchList = replaceDialog->findHistory () ; 00172 s_replaceList = replaceDialog->replacementHistory () ; 00173 00174 // Do *not* remove the QString() wrapping, it fixes a nasty crash 00175 replace( QString(s_searchList.first()), m_replacement, opts ); 00176 } 00177 00178 delete replaceDialog; 00179 m_view->update (); 00180 } 00181 00182 void KateSearch::replace( const QString& pattern, const QString &replacement, long flags ) 00183 { 00184 if (!doc()->isReadWrite()) return; 00185 00186 addToList( s_searchList, pattern ); 00187 s_pattern = pattern; 00188 addToList( s_replaceList, replacement ); 00189 m_replacement = replacement; 00190 KateViewConfig::global()->setSearchFlags( flags ); 00191 00192 SearchFlags searchFlags; 00193 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive; 00194 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly; 00195 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor) 00196 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText); 00197 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards; 00198 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText; 00199 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace; 00200 searchFlags.replace = true; 00201 searchFlags.finished = false; 00202 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression; 00203 searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference; 00204 if ( searchFlags.selected ) 00205 { 00206 s.selBegin = KateTextCursor( m_view->selStartLine(), m_view->selStartCol() ); 00207 s.selEnd = KateTextCursor( m_view->selEndLine(), m_view->selEndCol() ); 00208 s.cursor = s.flags.backward ? s.selEnd : s.selBegin; 00209 } else { 00210 s.cursor = getCursor( searchFlags ); 00211 } 00212 00213 s.wrappedEnd = s.cursor; 00214 s.wrapped = false; 00215 00216 search( searchFlags ); 00217 } 00218 00219 void KateSearch::findAgain( bool reverseDirection ) 00220 { 00221 SearchFlags searchFlags; 00222 searchFlags.caseSensitive = KateViewConfig::global()->searchFlags() & KFindDialog::CaseSensitive; 00223 searchFlags.wholeWords = KateViewConfig::global()->searchFlags() & KFindDialog::WholeWordsOnly; 00224 searchFlags.fromBeginning = !(KateViewConfig::global()->searchFlags() & KFindDialog::FromCursor) 00225 && !(KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText); 00226 searchFlags.backward = KateViewConfig::global()->searchFlags() & KFindDialog::FindBackwards; 00227 searchFlags.selected = KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText; 00228 searchFlags.prompt = KateViewConfig::global()->searchFlags() & KReplaceDialog::PromptOnReplace; 00229 searchFlags.replace = false; 00230 searchFlags.finished = false; 00231 searchFlags.regExp = KateViewConfig::global()->searchFlags() & KFindDialog::RegularExpression; 00232 searchFlags.useBackRefs = KateViewConfig::global()->searchFlags() & KReplaceDialog::BackReference; 00233 00234 if (reverseDirection) 00235 searchFlags.backward = !searchFlags.backward; 00236 00237 searchFlags.fromBeginning = false; 00238 searchFlags.prompt = true; // ### why is the above assignment there? 00239 00240 s.cursor = getCursor( searchFlags ); 00241 search( searchFlags ); 00242 } 00243 00244 void KateSearch::search( SearchFlags flags ) 00245 { 00246 s.flags = flags; 00247 00248 if( s.flags.fromBeginning ) { 00249 if( !s.flags.backward ) { 00250 s.cursor.setPos(0, 0); 00251 } else { 00252 s.cursor.setLine(doc()->numLines() - 1); 00253 s.cursor.setCol(doc()->lineLength( s.cursor.line() )); 00254 } 00255 } 00256 00257 if((!s.flags.backward && 00258 s.cursor.col() == 0 && 00259 s.cursor.line() == 0 ) || 00260 ( s.flags.backward && 00261 s.cursor.col() == doc()->lineLength( s.cursor.line() ) && 00262 s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) { 00263 s.flags.finished = true; 00264 } 00265 00266 if( s.flags.replace ) { 00267 replaces = 0; 00268 if( s.flags.prompt ) 00269 promptReplace(); 00270 else 00271 replaceAll(); 00272 } else { 00273 findAgain(); 00274 } 00275 } 00276 00277 void KateSearch::wrapSearch() 00278 { 00279 if( s.flags.selected ) 00280 { 00281 KateTextCursor start (s.selBegin); 00282 KateTextCursor end (s.selEnd); 00283 00284 // recalc for block sel, to have start with lowest col, end with highest 00285 if (m_view->blockSelectionMode()) 00286 { 00287 start.setCol (kMin(s.selBegin.col(), s.selEnd.col())); 00288 end.setCol (kMax(s.selBegin.col(), s.selEnd.col())); 00289 } 00290 00291 s.cursor = s.flags.backward ? end : start; 00292 } 00293 else 00294 { 00295 if( !s.flags.backward ) { 00296 s.cursor.setPos(0, 0); 00297 } else { 00298 s.cursor.setLine(doc()->numLines() - 1); 00299 s.cursor.setCol(doc()->lineLength( s.cursor.line() ) ); 00300 } 00301 } 00302 00303 // oh, we wrapped around one time allready now ! 00304 // only check that on replace 00305 s.wrapped = s.flags.replace; 00306 00307 replaces = 0; 00308 s.flags.finished = true; 00309 } 00310 00311 void KateSearch::findAgain() 00312 { 00313 if( s_pattern.isEmpty() ) { 00314 find(); 00315 return; 00316 } 00317 00318 if ( doSearch( s_pattern ) ) { 00319 exposeFound( s.cursor, s.matchedLength ); 00320 } else if( !s.flags.finished ) { 00321 if( askContinue() ) { 00322 wrapSearch(); 00323 findAgain(); 00324 } else { 00325 if (arbitraryHLExample) m_arbitraryHLList->clear(); 00326 } 00327 } else { 00328 if (arbitraryHLExample) m_arbitraryHLList->clear(); 00329 if ( s.showNotFound ) 00330 KMessageBox::sorry( view(), 00331 i18n("Search string '%1' not found!") 00332 .arg( KStringHandler::csqueeze( s_pattern ) ), 00333 i18n("Find")); 00334 } 00335 } 00336 00337 void KateSearch::replaceAll() 00338 { 00339 doc()->editStart (); 00340 00341 while( doSearch( s_pattern ) ) 00342 replaceOne(); 00343 00344 doc()->editEnd (); 00345 00346 if( !s.flags.finished ) { 00347 if( askContinue() ) { 00348 wrapSearch(); 00349 replaceAll(); 00350 } 00351 } else { 00352 KMessageBox::information( view(), 00353 i18n("%n replacement made.","%n replacements made.",replaces), 00354 i18n("Replace") ); 00355 } 00356 } 00357 00358 void KateSearch::promptReplace() 00359 { 00360 if ( doSearch( s_pattern ) ) { 00361 exposeFound( s.cursor, s.matchedLength ); 00362 replacePrompt->show(); 00363 replacePrompt->setFocus (); 00364 } else if( !s.flags.finished && askContinue() ) { 00365 wrapSearch(); 00366 promptReplace(); 00367 } else { 00368 if (arbitraryHLExample) m_arbitraryHLList->clear(); 00369 replacePrompt->hide(); 00370 KMessageBox::information( view(), 00371 i18n("%n replacement made.","%n replacements made.",replaces), 00372 i18n("Replace") ); 00373 } 00374 } 00375 00376 void KateSearch::replaceOne() 00377 { 00378 QString replaceWith = m_replacement; 00379 if ( s.flags.regExp && s.flags.useBackRefs ) { 00380 // replace each "(?!\)\d+" with the corresponding capture 00381 QRegExp br("\\\\(\\d+)"); 00382 int pos = br.search( replaceWith ); 00383 int ncaps = m_re.numCaptures(); 00384 while ( pos >= 0 ) { 00385 QString sc; 00386 if ( !pos || replaceWith.at( pos-1) != '\\' ) { 00387 int ccap = br.cap(1).toInt(); 00388 if (ccap <= ncaps ) { 00389 sc = m_re.cap( ccap ); 00390 replaceWith.replace( pos, br.matchedLength(), sc ); 00391 } 00392 else { 00393 kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<m_re.pattern()<<"'"<<endl; 00394 } 00395 } 00396 pos = br.search( replaceWith, pos + (int)sc.length() ); 00397 } 00398 } 00399 00400 doc()->editStart(); 00401 doc()->removeText( s.cursor.line(), s.cursor.col(), 00402 s.cursor.line(), s.cursor.col() + s.matchedLength ); 00403 doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith ); 00404 doc()->editEnd(), 00405 00406 replaces++; 00407 00408 // if we inserted newlines, we better adjust. 00409 uint newlines = replaceWith.contains('\n'); 00410 if ( newlines ) 00411 { 00412 if ( ! s.flags.backward ) 00413 { 00414 s.cursor.setLine( s.cursor.line() + newlines ); 00415 s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') ); 00416 } 00417 // selection? 00418 if ( s.flags.selected ) 00419 s.selEnd.setLine( s.selEnd.line() + newlines ); 00420 } 00421 00422 00423 // adjust selection endcursor if needed 00424 if( s.flags.selected && s.cursor.line() == s.selEnd.line() ) 00425 { 00426 s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength ); 00427 } 00428 00429 // adjust wrap cursor if needed 00430 if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col()) 00431 { 00432 s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength ); 00433 } 00434 00435 if( !s.flags.backward ) { 00436 s.cursor.setCol(s.cursor.col() + replaceWith.length()); 00437 } else if( s.cursor.col() > 0 ) { 00438 s.cursor.setCol(s.cursor.col() - 1); 00439 } else { 00440 s.cursor.setLine(s.cursor.line() - 1); 00441 if( s.cursor.line() >= 0 ) { 00442 s.cursor.setCol(doc()->lineLength( s.cursor.line() )); 00443 } 00444 } 00445 } 00446 00447 void KateSearch::skipOne() 00448 { 00449 if( !s.flags.backward ) { 00450 s.cursor.setCol(s.cursor.col() + s.matchedLength); 00451 } else if( s.cursor.col() > 0 ) { 00452 s.cursor.setCol(s.cursor.col() - 1); 00453 } else { 00454 s.cursor.setLine(s.cursor.line() - 1); 00455 if( s.cursor.line() >= 0 ) { 00456 s.cursor.setCol(doc()->lineLength(s.cursor.line())); 00457 } 00458 } 00459 } 00460 00461 void KateSearch::replaceSlot() { 00462 switch( (Dialog_results)replacePrompt->result() ) { 00463 case srCancel: replacePrompt->hide(); break; 00464 case srAll: replacePrompt->hide(); replaceAll(); break; 00465 case srYes: replaceOne(); promptReplace(); break; 00466 case srLast: replacePrompt->hide(), replaceOne(); break; 00467 case srNo: skipOne(); promptReplace(); break; 00468 } 00469 } 00470 00471 bool KateSearch::askContinue() 00472 { 00473 QString made = 00474 i18n( "%n replacement made.", 00475 "%n replacements made.", 00476 replaces ); 00477 00478 QString reached = !s.flags.backward ? 00479 i18n( "End of document reached." ) : 00480 i18n( "Beginning of document reached." ); 00481 00482 if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText) 00483 { 00484 reached = !s.flags.backward ? 00485 i18n( "End of selection reached." ) : 00486 i18n( "Beginning of selection reached." ); 00487 } 00488 00489 QString question = !s.flags.backward ? 00490 i18n( "Continue from the beginning?" ) : 00491 i18n( "Continue from the end?" ); 00492 00493 QString text = s.flags.replace ? 00494 made + "\n" + reached + "\n" + question : 00495 reached + "\n" + question; 00496 00497 return KMessageBox::Yes == KMessageBox::questionYesNo( 00498 view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"), 00499 KStdGuiItem::cont(), i18n("&Stop") ); 00500 } 00501 00502 QString KateSearch::getSearchText() 00503 { 00504 // SelectionOnly: use selection 00505 // WordOnly: use word under cursor 00506 // SelectionWord: use selection if available, else use word under cursor 00507 // WordSelection: use word if available, else use selection 00508 QString str; 00509 00510 int getFrom = view()->config()->textToSearchMode(); 00511 switch (getFrom) 00512 { 00513 case KateViewConfig::SelectionOnly: // (Windows) 00514 //kdDebug() << "getSearchText(): SelectionOnly" << endl; 00515 if( m_view->hasSelection() ) 00516 str = m_view->selection(); 00517 break; 00518 00519 case KateViewConfig::SelectionWord: // (classic Kate behavior) 00520 //kdDebug() << "getSearchText(): SelectionWord" << endl; 00521 if( m_view->hasSelection() ) 00522 str = m_view->selection(); 00523 else 00524 str = view()->currentWord(); 00525 break; 00526 00527 case KateViewConfig::WordOnly: // (weird?) 00528 //kdDebug() << "getSearchText(): WordOnly" << endl; 00529 str = view()->currentWord(); 00530 break; 00531 00532 case KateViewConfig::WordSelection: // (persistent selection lover) 00533 //kdDebug() << "getSearchText(): WordSelection" << endl; 00534 str = view()->currentWord(); 00535 if (str.isEmpty() && m_view->hasSelection() ) 00536 str = m_view->selection(); 00537 break; 00538 00539 default: // (nowhere) 00540 //kdDebug() << "getSearchText(): Nowhere" << endl; 00541 break; 00542 } 00543 00544 str.replace( QRegExp("^\\n"), "" ); 00545 str.replace( QRegExp("\\n.*"), "" ); 00546 00547 return str; 00548 } 00549 00550 KateTextCursor KateSearch::getCursor( SearchFlags flags ) 00551 { 00552 if (flags.backward && !flags.selected && view()->hasSelection()) 00553 { 00554 // We're heading backwards (and not within a selection), 00555 // the selection might start before the cursor. 00556 return kMin( KateTextCursor(view()->selStartLine(), view()->selStartCol()), 00557 KateTextCursor(view()->cursorLine(), view()->cursorColumnReal())); 00558 } 00559 return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal()); 00560 } 00561 00562 bool KateSearch::doSearch( const QString& text ) 00563 { 00564 /* 00565 rodda: Still Working on this... :) 00566 00567 bool result = false; 00568 00569 if (m_searchResults.count()) { 00570 m_resultIndex++; 00571 if (m_resultIndex < (int)m_searchResults.count()) { 00572 s = m_searchResults[m_resultIndex]; 00573 result = true; 00574 } 00575 00576 } else { 00577 int temp = 0; 00578 do {*/ 00579 00580 #if 0 00581 static int oldLine = -1; 00582 static int oldCol = -1; 00583 #endif 00584 00585 uint line = s.cursor.line(); 00586 uint col = s.cursor.col();// + (result ? s.matchedLength : 0); 00587 bool backward = s.flags.backward; 00588 bool caseSensitive = s.flags.caseSensitive; 00589 bool regExp = s.flags.regExp; 00590 bool wholeWords = s.flags.wholeWords; 00591 uint foundLine, foundCol, matchLen; 00592 bool found = false; 00593 //kdDebug() << "Searching at " << line << ", " << col << endl; 00594 // kdDebug()<<"KateSearch::doSearch: "<<line<<", "<<col<<", "<<backward<<endl; 00595 00596 if (backward) 00597 { 00598 KateDocCursor docCursor(line, col, doc()); 00599 00600 // If we're at the top of the document, we're not gonna find anything, so bail. 00601 if (docCursor.line() == 0 && docCursor.col() == 0) 00602 return false; 00603 00604 // Move one step backward before searching, if this is a "find again", we don't 00605 // want to find the same match. 00606 docCursor.moveBackward(1); 00607 line = docCursor.line(); 00608 col = docCursor.col(); 00609 } 00610 00611 do { 00612 if( regExp ) { 00613 m_re = QRegExp( text, caseSensitive ); 00614 found = doc()->searchText( line, col, m_re, 00615 &foundLine, &foundCol, 00616 &matchLen, backward ); 00617 } 00618 else if ( wholeWords ) 00619 { 00620 bool maybefound = false; 00621 do 00622 { 00623 maybefound = doc()->searchText( line, col, text, 00624 &foundLine, &foundCol, 00625 &matchLen, caseSensitive, backward ); 00626 if ( maybefound ) 00627 { 00628 found = ( 00629 ( foundCol == 0 || 00630 ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol - 1 ) ) ) && 00631 ( foundCol + matchLen == doc()->lineLength( foundLine ) || 00632 ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol + matchLen ) ) ) 00633 ); 00634 if ( found ) 00635 { 00636 break; 00637 } 00638 else if ( backward && foundCol == 0 ) // we are done on this line and want to avoid endless loops like in #137312 00639 { 00640 if ( line == 0 ) // we are completely done... 00641 break; 00642 else 00643 line--; 00644 } 00645 else 00646 { 00647 line = foundLine; 00648 col = foundCol + 1; 00649 } 00650 } 00651 } while ( maybefound ); 00652 } 00653 else { 00654 found = doc()->searchText( line, col, text, 00655 &foundLine, &foundCol, 00656 &matchLen, caseSensitive, backward ); 00657 } 00658 00659 if ( found && s.flags.selected ) 00660 { 00661 KateTextCursor start (s.selBegin); 00662 KateTextCursor end (s.selEnd); 00663 00664 // recalc for block sel, to have start with lowest col, end with highest 00665 if (m_view->blockSelectionMode()) 00666 { 00667 start.setCol (kMin(s.selBegin.col(), s.selEnd.col())); 00668 end.setCol (kMax(s.selBegin.col(), s.selEnd.col())); 00669 } 00670 00671 if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= end 00672 || s.flags.backward && KateTextCursor( foundLine, foundCol ) < start ) 00673 { 00674 found = false; 00675 } 00676 else if (m_view->blockSelectionMode()) 00677 { 00678 if ((int)foundCol >= start.col() && (int)foundCol < end.col()) 00679 break; 00680 } 00681 } 00682 00683 line = foundLine; 00684 col = foundCol+1; 00685 } 00686 while (s.flags.selected && m_view->blockSelectionMode() && found); 00687 // in the case we want to search in selection + blockselection we need to loop 00688 00689 if( !found ) return false; 00690 00691 // save the search result 00692 s.cursor.setPos(foundLine, foundCol); 00693 s.matchedLength = matchLen; 00694 00695 // we allready wrapped around one time 00696 if (s.wrapped) 00697 { 00698 if (s.flags.backward) 00699 { 00700 if ( (s.cursor.line() < s.wrappedEnd.line()) 00701 || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) ) 00702 return false; 00703 } 00704 else 00705 { 00706 if ( (s.cursor.line() > s.wrappedEnd.line()) 00707 || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) ) 00708 return false; 00709 } 00710 } 00711 00712 // kdDebug() << "Found at " << s.cursor.line() << ", " << s.cursor.col() << endl; 00713 00714 00715 //m_searchResults.append(s); 00716 00717 if (arbitraryHLExample) { 00718 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); 00719 hl->setBold(); 00720 hl->setTextColor(Qt::white); 00721 hl->setBGColor(Qt::black); 00722 // destroy the highlight upon change 00723 connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated())); 00724 m_arbitraryHLList->append(hl); 00725 } 00726 00727 return true; 00728 00729 /* rodda: more of my search highlighting work 00730 00731 } while (++temp < 100); 00732 00733 if (result) { 00734 s = m_searchResults.first(); 00735 m_resultIndex = 0; 00736 } 00737 } 00738 00739 return result;*/ 00740 } 00741 00742 void KateSearch::exposeFound( KateTextCursor &cursor, int slen ) 00743 { 00744 view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 ); 00745 view()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen ); 00746 view()->syncSelectionCache(); 00747 } 00748 //END KateSearch 00749 00750 //BEGIN KateReplacePrompt 00751 // this dialog is not modal 00752 KateReplacePrompt::KateReplacePrompt ( QWidget *parent ) 00753 : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ), 00754 User3 | User2 | User1 | Close | Ok , Ok, true, 00755 i18n("Replace &All"), i18n("Re&place && Close"), i18n("&Replace") ) 00756 { 00757 setButtonOK( i18n("&Find Next") ); 00758 QWidget *page = new QWidget(this); 00759 setMainWidget(page); 00760 00761 QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() ); 00762 QLabel *label = new QLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page); 00763 topLayout->addWidget(label ); 00764 } 00765 00766 void KateReplacePrompt::slotOk () 00767 { // Search Next 00768 done(KateSearch::srNo); 00769 actionButton(Ok)->setFocus(); 00770 } 00771 00772 void KateReplacePrompt::slotClose () 00773 { // Close 00774 done(KateSearch::srCancel); 00775 actionButton(Close)->setFocus(); 00776 } 00777 00778 void KateReplacePrompt::slotUser1 () 00779 { // Replace All 00780 done(KateSearch::srAll); 00781 actionButton(User1)->setFocus(); 00782 } 00783 00784 void KateReplacePrompt::slotUser2 () 00785 { // Replace & Close 00786 done(KateSearch::srLast); 00787 actionButton(User2)->setFocus(); 00788 } 00789 00790 void KateReplacePrompt::slotUser3 () 00791 { // Replace 00792 done(KateSearch::srYes); 00793 actionButton(User3)->setFocus(); 00794 } 00795 00796 void KateReplacePrompt::done (int result) 00797 { 00798 setResult(result); 00799 00800 emit clicked(); 00801 } 00802 //END KateReplacePrompt 00803 00804 //BEGIN SearchCommand 00805 bool SearchCommand::exec(class Kate::View *view, const QString &cmd, QString &msg) 00806 { 00807 QString flags, pattern, replacement; 00808 if ( cmd.startsWith( "find" ) ) 00809 { 00810 00811 static QRegExp re_find("find(?::([bcersw]*))?\\s+(.+)"); 00812 if ( re_find.search( cmd ) < 0 ) 00813 { 00814 msg = i18n("Usage: find[:[bcersw]] PATTERN"); 00815 return false; 00816 } 00817 flags = re_find.cap( 1 ); 00818 pattern = re_find.cap( 2 ); 00819 } 00820 00821 else if ( cmd.startsWith( "ifind" ) ) 00822 { 00823 static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s+(.*)"); 00824 if ( re_ifind.search( cmd ) < 0 ) 00825 { 00826 msg = i18n("Usage: ifind[:[bcrs]] PATTERN"); 00827 return false; 00828 } 00829 ifindClear(); 00830 return true; 00831 } 00832 00833 else if ( cmd.startsWith( "replace" ) ) 00834 { 00835 // Try if the pattern and replacement is quoted, using a quote character ["'] 00836 static QRegExp re_rep("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s+\\2((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$"); 00837 // Or one quoted argument 00838 QRegExp re_rep1("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$"); 00839 // Else, it's just one or two (space separated) words 00840 QRegExp re_rep2("replace(?::([bceprsw]*))?\\s+(\\S+)(.*)"); 00841 #define unbackslash(s) p=0;\ 00842 while ( (p = pattern.find( '\\' + delim, p )) > -1 )\ 00843 {\ 00844 if ( !p || pattern[p-1] != '\\' )\ 00845 pattern.remove( p, 1 );\ 00846 p++;\ 00847 } 00848 00849 if ( re_rep.search( cmd ) >= 0 ) 00850 { 00851 flags = re_rep.cap(1); 00852 pattern = re_rep.cap( 3 ); 00853 replacement = re_rep.cap( 4 ); 00854 00855 int p(0); 00856 // unbackslash backslashed delimiter strings 00857 // in pattern .. 00858 QString delim = re_rep.cap( 2 ); 00859 unbackslash(pattern); 00860 // .. and in replacement 00861 unbackslash(replacement); 00862 } 00863 else if ( re_rep1.search( cmd ) >= 0 ) 00864 { 00865 flags = re_rep1.cap(1); 00866 pattern = re_rep1.cap( 3 ); 00867 00868 int p(0); 00869 QString delim = re_rep1.cap( 2 ); 00870 unbackslash(pattern); 00871 } 00872 else if ( re_rep2.search( cmd ) >= 0 ) 00873 { 00874 flags = re_rep2.cap( 1 ); 00875 pattern = re_rep2.cap( 2 ); 00876 replacement = re_rep2.cap( 3 ).stripWhiteSpace(); 00877 } 00878 else 00879 { 00880 msg = i18n("Usage: replace[:[bceprsw]] PATTERN [REPLACEMENT]"); 00881 return false; 00882 } 00883 kdDebug()<<"replace '"<<pattern<<"' with '"<<replacement<<"'"<<endl; 00884 #undef unbackslash 00885 } 00886 00887 long f = 0; 00888 if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards; 00889 if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor; 00890 if ( flags.contains( 'e' ) ) f |= KFindDialog::SelectedText; 00891 if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression; 00892 if ( flags.contains( 'p' ) ) f |= KReplaceDialog::PromptOnReplace; 00893 if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive; 00894 if ( flags.contains( 'w' ) ) f |= KFindDialog::WholeWordsOnly; 00895 00896 if ( cmd.startsWith( "find" ) ) 00897 { 00898 ((KateView*)view)->find( pattern, f ); 00899 return true; 00900 } 00901 else if ( cmd.startsWith( "replace" ) ) 00902 { 00903 f |= KReplaceDialog::BackReference; // mandatory here? 00904 ((KateView*)view)->replace( pattern, replacement, f ); 00905 return true; 00906 } 00907 00908 return false; 00909 } 00910 00911 bool SearchCommand::help(class Kate::View *, const QString &cmd, QString &msg) 00912 { 00913 if ( cmd == "find" ) 00914 msg = i18n("<p>Usage: <code>find[:bcersw] PATTERN</code></p>"); 00915 00916 else if ( cmd == "ifind" ) 00917 msg = i18n("<p>Usage: <code>ifind:[:bcrs] PATTERN</code>" 00918 "<br>ifind does incremental or 'as-you-type' search</p>"); 00919 00920 else 00921 msg = i18n("<p>Usage: <code>replace[:bceprsw] PATTERN REPLACEMENT</code></p>"); 00922 00923 msg += i18n( 00924 "<h4><caption>Options</h4><p>" 00925 "<b>b</b> - Search backward" 00926 "<br><b>c</b> - Search from cursor" 00927 "<br><b>r</b> - Pattern is a regular expression" 00928 "<br><b>s</b> - Case sensitive search" 00929 ); 00930 00931 if ( cmd == "find" ) 00932 msg += i18n( 00933 "<br><b>e</b> - Search in selected text only" 00934 "<br><b>w</b> - Search whole words only" 00935 ); 00936 00937 if ( cmd == "replace" ) 00938 msg += i18n( 00939 "<br><b>p</b> - Prompt for replace</p>" 00940 "<p>If REPLACEMENT is not present, an empty string is used.</p>" 00941 "<p>If you want to have whitespace in your PATTERN, you need to " 00942 "quote both PATTERN and REPLACEMENT with either single or double " 00943 "quotes. To have the quote characters in the strings, prepend them " 00944 "with a backslash."); 00945 00946 msg += "</p>"; 00947 return true; 00948 } 00949 00950 QStringList SearchCommand::cmds() 00951 { 00952 QStringList l; 00953 l << "find" << "replace" << "ifind"; 00954 return l; 00955 } 00956 00957 bool SearchCommand::wantsToProcessText( const QString &cmdname ) 00958 { 00959 return cmdname == "ifind"; 00960 } 00961 00962 void SearchCommand::processText( Kate::View *view, const QString &cmd ) 00963 { 00964 static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s(.*)"); 00965 if ( re_ifind.search( cmd ) > -1 ) 00966 { 00967 QString flags = re_ifind.cap( 1 ); 00968 QString pattern = re_ifind.cap( 2 ); 00969 00970 00971 // if there is no setup, or the text length is 0, set up the properties 00972 if ( ! m_ifindFlags || pattern.isEmpty() ) 00973 ifindInit( flags ); 00974 // if there is no fromCursor, add it if this is not the first character 00975 else if ( ! ( m_ifindFlags & KFindDialog::FromCursor ) && ! pattern.isEmpty() ) 00976 m_ifindFlags |= KFindDialog::FromCursor; 00977 00978 // search.. 00979 if ( ! pattern.isEmpty() ) 00980 { 00981 KateView *v = (KateView*)view; 00982 00983 // If it *looks like* we are continuing, place the cursor 00984 // at the beginning of the selection, so that the search continues. 00985 // ### check more carefully, like is the cursor currently at the end 00986 // of the selection. 00987 if ( pattern.startsWith( v->selection() ) && 00988 v->selection().length() + 1 == pattern.length() ) 00989 v->setCursorPositionInternal( v->selStartLine(), v->selStartCol() ); 00990 00991 v->find( pattern, m_ifindFlags, false ); 00992 } 00993 } 00994 } 00995 00996 void SearchCommand::ifindInit( const QString &flags ) 00997 { 00998 long f = 0; 00999 if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards; 01000 if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor; 01001 if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression; 01002 if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive; 01003 m_ifindFlags = f; 01004 } 01005 01006 void SearchCommand::ifindClear() 01007 { 01008 m_ifindFlags = 0; 01009 } 01010 //END SearchCommand 01011 01012 // kate: space-indent on; indent-width 2; replace-tabs on;