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 <tqlayout.h> 00044 #include <tqlabel.h> 00045 00046 //BEGIN KateSearch 00047 TQStringList KateSearch::s_searchList = TQStringList(); 00048 TQStringList KateSearch::s_replaceList = TQStringList(); 00049 TQString KateSearch::s_pattern = TQString(); 00050 static const bool arbitraryHLExample = false; 00051 00052 KateSearch::KateSearch( KateView* view ) 00053 : TQObject( 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,TQT_SIGNAL(clicked()),this,TQT_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, TQT_SLOT(find()), ac )->setWhatsThis( 00072 i18n("Look up the first occurrence of a piece of text or regular expression.")); 00073 KStdAction::findNext( this, TQT_SLOT(slotFindNext()), ac )->setWhatsThis( 00074 i18n("Look up the next occurrence of the search phrase.")); 00075 KStdAction::findPrev( this, TQT_SLOT(slotFindPrev()), ac, "edit_find_prev" )->setWhatsThis( 00076 i18n("Look up the previous occurrence of the search phrase.")); 00077 KStdAction::replace( this, TQT_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( TQStringList& list, const TQString& s ) 00082 { 00083 if( list.count() > 0 ) { 00084 TQStringList::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() == TQDialog::Accepted ) { 00107 s_searchList = findDialog->findHistory () ; 00108 // Do *not* remove the TQString() wrapping, it fixes a nasty crash 00109 find( TQString(s_searchList.first()), findDialog->options(), true, true ); 00110 } 00111 00112 delete findDialog; 00113 m_view->repaintText (); 00114 } 00115 00116 void KateSearch::find( const TQString &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() == TQDialog::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 TQString() wrapping, it fixes a nasty crash 00175 replace( TQString(s_searchList.first()), m_replacement, opts ); 00176 } 00177 00178 delete replaceDialog; 00179 m_view->update (); 00180 } 00181 00182 void KateSearch::replace( const TQString& pattern, const TQString &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 TQString replaceWith = m_replacement; 00379 if ( s.flags.regExp && s.flags.useBackRefs ) { 00380 // Replace each "\0"..."\9" with the corresponding capture, 00381 // "\n" and "\t" with newline and tab, 00382 // "\\" with "\", 00383 // and remove the "\" for any other sequence. 00384 TQRegExp br("\\\\(.)"); 00385 int pos = br.search( replaceWith ); 00386 int ncaps = m_re.numCaptures(); 00387 while ( pos >= 0 ) { 00388 TQString substitute; 00389 TQChar argument = TQString(br.cap(1)).at(0); 00390 if ( argument.isDigit() ) { 00391 // the second character is a digit, this is a backreference 00392 int ccap = argument.digitValue(); 00393 if (ccap <= ncaps ) { 00394 substitute = m_re.cap( ccap ); 00395 } else { 00396 kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<TQString(m_re.pattern())<<"'"<<endl; 00397 break; 00398 } 00399 } else if ( argument == 'n' ) { 00400 substitute = '\n'; 00401 } else if ( argument == 't' ) { 00402 substitute = '\t'; 00403 } else { 00404 // handle a validly escaped backslash, or an invalid escape. 00405 substitute = argument; 00406 } 00407 replaceWith.replace( pos, br.matchedLength(), substitute ); 00408 pos = br.search( replaceWith, pos + substitute.length() ); 00409 } 00410 } 00411 00412 doc()->editStart(); 00413 doc()->removeText( s.cursor.line(), s.cursor.col(), 00414 s.cursor.line(), s.cursor.col() + s.matchedLength ); 00415 doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith ); 00416 doc()->editEnd(), 00417 00418 replaces++; 00419 00420 // if we inserted newlines, we better adjust. 00421 uint newlines = replaceWith.contains('\n'); 00422 if ( newlines ) 00423 { 00424 if ( ! s.flags.backward ) 00425 { 00426 s.cursor.setLine( s.cursor.line() + newlines ); 00427 s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') ); 00428 } 00429 // selection? 00430 if ( s.flags.selected ) 00431 s.selEnd.setLine( s.selEnd.line() + newlines ); 00432 } 00433 00434 00435 // adjust selection endcursor if needed 00436 if( s.flags.selected && s.cursor.line() == s.selEnd.line() ) 00437 { 00438 s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength ); 00439 } 00440 00441 // adjust wrap cursor if needed 00442 if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col()) 00443 { 00444 s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength ); 00445 } 00446 00447 if( !s.flags.backward ) { 00448 s.cursor.setCol(s.cursor.col() + replaceWith.length()); 00449 } else if( s.cursor.col() > 0 ) { 00450 s.cursor.setCol(s.cursor.col() - 1); 00451 } else { 00452 s.cursor.setLine(s.cursor.line() - 1); 00453 if( s.cursor.line() >= 0 ) { 00454 s.cursor.setCol(doc()->lineLength( s.cursor.line() )); 00455 } 00456 } 00457 } 00458 00459 void KateSearch::skipOne() 00460 { 00461 if( !s.flags.backward ) { 00462 s.cursor.setCol(s.cursor.col() + s.matchedLength); 00463 } else if( s.cursor.col() > 0 ) { 00464 s.cursor.setCol(s.cursor.col() - 1); 00465 } else { 00466 s.cursor.setLine(s.cursor.line() - 1); 00467 if( s.cursor.line() >= 0 ) { 00468 s.cursor.setCol(doc()->lineLength(s.cursor.line())); 00469 } 00470 } 00471 } 00472 00473 void KateSearch::replaceSlot() { 00474 switch( (Dialog_results)replacePrompt->result() ) { 00475 case srCancel: replacePrompt->hide(); break; 00476 case srAll: replacePrompt->hide(); replaceAll(); break; 00477 case srYes: replaceOne(); promptReplace(); break; 00478 case srLast: replacePrompt->hide(), replaceOne(); break; 00479 case srNo: skipOne(); promptReplace(); break; 00480 } 00481 } 00482 00483 bool KateSearch::askContinue() 00484 { 00485 TQString made = 00486 i18n( "%n replacement made.", 00487 "%n replacements made.", 00488 replaces ); 00489 00490 TQString reached = !s.flags.backward ? 00491 i18n( "End of document reached." ) : 00492 i18n( "Beginning of document reached." ); 00493 00494 if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText) 00495 { 00496 reached = !s.flags.backward ? 00497 i18n( "End of selection reached." ) : 00498 i18n( "Beginning of selection reached." ); 00499 } 00500 00501 TQString question = !s.flags.backward ? 00502 i18n( "Continue from the beginning?" ) : 00503 i18n( "Continue from the end?" ); 00504 00505 TQString text = s.flags.replace ? 00506 made + "\n" + reached + "\n" + question : 00507 reached + "\n" + question; 00508 00509 return KMessageBox::Yes == KMessageBox::questionYesNo( 00510 view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"), 00511 KStdGuiItem::cont(), i18n("&Stop") ); 00512 } 00513 00514 TQString KateSearch::getSearchText() 00515 { 00516 // SelectionOnly: use selection 00517 // WordOnly: use word under cursor 00518 // SelectionWord: use selection if available, else use word under cursor 00519 // WordSelection: use word if available, else use selection 00520 TQString str; 00521 00522 int getFrom = view()->config()->textToSearchMode(); 00523 switch (getFrom) 00524 { 00525 case KateViewConfig::SelectionOnly: // (Windows) 00526 //kdDebug() << "getSearchText(): SelectionOnly" << endl; 00527 if( m_view->hasSelection() ) 00528 str = m_view->selection(); 00529 break; 00530 00531 case KateViewConfig::SelectionWord: // (classic Kate behavior) 00532 //kdDebug() << "getSearchText(): SelectionWord" << endl; 00533 if( m_view->hasSelection() ) 00534 str = m_view->selection(); 00535 else 00536 str = view()->currentWord(); 00537 break; 00538 00539 case KateViewConfig::WordOnly: // (weird?) 00540 //kdDebug() << "getSearchText(): WordOnly" << endl; 00541 str = view()->currentWord(); 00542 break; 00543 00544 case KateViewConfig::WordSelection: // (persistent selection lover) 00545 //kdDebug() << "getSearchText(): WordSelection" << endl; 00546 str = view()->currentWord(); 00547 if (str.isEmpty() && m_view->hasSelection() ) 00548 str = m_view->selection(); 00549 break; 00550 00551 default: // (nowhere) 00552 //kdDebug() << "getSearchText(): Nowhere" << endl; 00553 break; 00554 } 00555 00556 str.replace( TQRegExp("^\\n"), "" ); 00557 str.replace( TQRegExp("\\n.*"), "" ); 00558 00559 return str; 00560 } 00561 00562 KateTextCursor KateSearch::getCursor( SearchFlags flags ) 00563 { 00564 if (flags.backward && !flags.selected && view()->hasSelection()) 00565 { 00566 // We're heading backwards (and not within a selection), 00567 // the selection might start before the cursor. 00568 return kMin( KateTextCursor(view()->selStartLine(), view()->selStartCol()), 00569 KateTextCursor(view()->cursorLine(), view()->cursorColumnReal())); 00570 } 00571 return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal()); 00572 } 00573 00574 bool KateSearch::doSearch( const TQString& text ) 00575 { 00576 /* 00577 rodda: Still Working on this... :) 00578 00579 bool result = false; 00580 00581 if (m_searchResults.count()) { 00582 m_resultIndex++; 00583 if (m_resultIndex < (int)m_searchResults.count()) { 00584 s = m_searchResults[m_resultIndex]; 00585 result = true; 00586 } 00587 00588 } else { 00589 int temp = 0; 00590 do {*/ 00591 00592 #if 0 00593 static int oldLine = -1; 00594 static int oldCol = -1; 00595 #endif 00596 00597 uint line = s.cursor.line(); 00598 uint col = s.cursor.col();// + (result ? s.matchedLength : 0); 00599 bool backward = s.flags.backward; 00600 bool caseSensitive = s.flags.caseSensitive; 00601 bool regExp = s.flags.regExp; 00602 bool wholeWords = s.flags.wholeWords; 00603 uint foundLine, foundCol, matchLen; 00604 bool found = false; 00605 //kdDebug() << "Searching at " << line << ", " << col << endl; 00606 // kdDebug()<<"KateSearch::doSearch: "<<line<<", "<<col<<", "<<backward<<endl; 00607 00608 if (backward) 00609 { 00610 KateDocCursor docCursor(line, col, doc()); 00611 00612 // If we're at the top of the document, we're not gonna find anything, so bail. 00613 if (docCursor.line() == 0 && docCursor.col() == 0) 00614 return false; 00615 00616 // Move one step backward before searching, if this is a "find again", we don't 00617 // want to find the same match. 00618 docCursor.moveBackward(1); 00619 line = docCursor.line(); 00620 col = docCursor.col(); 00621 } 00622 00623 do { 00624 if( regExp ) { 00625 m_re = TQRegExp( text, caseSensitive ); 00626 found = doc()->searchText( line, col, m_re, 00627 &foundLine, &foundCol, 00628 &matchLen, backward ); 00629 } 00630 else if ( wholeWords ) 00631 { 00632 bool maybefound = false; 00633 do 00634 { 00635 maybefound = doc()->searchText( line, col, text, 00636 &foundLine, &foundCol, 00637 &matchLen, caseSensitive, backward ); 00638 if ( maybefound ) 00639 { 00640 found = ( 00641 ( foundCol == 0 || 00642 ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol - 1 ) ) ) && 00643 ( foundCol + matchLen == doc()->lineLength( foundLine ) || 00644 ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol + matchLen ) ) ) 00645 ); 00646 if ( found ) 00647 { 00648 break; 00649 } 00650 else if ( backward && foundCol == 0 ) // we are done on this line and want to avoid endless loops like in #137312 00651 { 00652 if ( line == 0 ) // we are completely done... 00653 break; 00654 else 00655 line--; 00656 } 00657 else 00658 { 00659 line = foundLine; 00660 col = foundCol + 1; 00661 } 00662 } 00663 } while ( maybefound ); 00664 } 00665 else { 00666 found = doc()->searchText( line, col, text, 00667 &foundLine, &foundCol, 00668 &matchLen, caseSensitive, backward ); 00669 } 00670 00671 if ( found && s.flags.selected ) 00672 { 00673 KateTextCursor start (s.selBegin); 00674 KateTextCursor end (s.selEnd); 00675 00676 // recalc for block sel, to have start with lowest col, end with highest 00677 if (m_view->blockSelectionMode()) 00678 { 00679 start.setCol (kMin(s.selBegin.col(), s.selEnd.col())); 00680 end.setCol (kMax(s.selBegin.col(), s.selEnd.col())); 00681 } 00682 00683 if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= end 00684 || s.flags.backward && KateTextCursor( foundLine, foundCol ) < start ) 00685 { 00686 found = false; 00687 } 00688 else if (m_view->blockSelectionMode()) 00689 { 00690 if ((int)foundCol >= start.col() && (int)foundCol < end.col()) 00691 break; 00692 } 00693 } 00694 00695 line = foundLine; 00696 col = foundCol+1; 00697 } 00698 while (s.flags.selected && m_view->blockSelectionMode() && found); 00699 // in the case we want to search in selection + blockselection we need to loop 00700 00701 if( !found ) return false; 00702 00703 // save the search result 00704 s.cursor.setPos(foundLine, foundCol); 00705 s.matchedLength = matchLen; 00706 00707 // we allready wrapped around one time 00708 if (s.wrapped) 00709 { 00710 if (s.flags.backward) 00711 { 00712 if ( (s.cursor.line() < s.wrappedEnd.line()) 00713 || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) ) 00714 return false; 00715 } 00716 else 00717 { 00718 if ( (s.cursor.line() > s.wrappedEnd.line()) 00719 || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) ) 00720 return false; 00721 } 00722 } 00723 00724 // kdDebug() << "Found at " << s.cursor.line() << ", " << s.cursor.col() << endl; 00725 00726 00727 //m_searchResults.append(s); 00728 00729 if (arbitraryHLExample) { 00730 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); 00731 hl->setBold(); 00732 hl->setTextColor(Qt::white); 00733 hl->setBGColor(Qt::black); 00734 // destroy the highlight upon change 00735 connect(hl, TQT_SIGNAL(contentsChanged()), hl, TQT_SIGNAL(eliminated())); 00736 m_arbitraryHLList->append(hl); 00737 } 00738 00739 return true; 00740 00741 /* rodda: more of my search highlighting work 00742 00743 } while (++temp < 100); 00744 00745 if (result) { 00746 s = m_searchResults.first(); 00747 m_resultIndex = 0; 00748 } 00749 } 00750 00751 return result;*/ 00752 } 00753 00754 void KateSearch::exposeFound( KateTextCursor &cursor, int slen ) 00755 { 00756 view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 ); 00757 view()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen ); 00758 view()->syncSelectionCache(); 00759 } 00760 //END KateSearch 00761 00762 //BEGIN KateReplacePrompt 00763 // this dialog is not modal 00764 KateReplacePrompt::KateReplacePrompt ( TQWidget *parent ) 00765 : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ), 00766 User3 | User2 | User1 | Close | Ok , Ok, true, 00767 i18n("Replace &All"), i18n("Re&place && Close"), i18n("&Replace") ) 00768 { 00769 setButtonOK( i18n("&Find Next") ); 00770 TQWidget *page = new TQWidget(this); 00771 setMainWidget(page); 00772 00773 TQBoxLayout *topLayout = new TQVBoxLayout( page, 0, spacingHint() ); 00774 TQLabel *label = new TQLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page); 00775 topLayout->addWidget(label ); 00776 } 00777 00778 void KateReplacePrompt::slotOk () 00779 { // Search Next 00780 done(KateSearch::srNo); 00781 actionButton(Ok)->setFocus(); 00782 } 00783 00784 void KateReplacePrompt::slotClose () 00785 { // Close 00786 done(KateSearch::srCancel); 00787 actionButton(Close)->setFocus(); 00788 } 00789 00790 void KateReplacePrompt::slotUser1 () 00791 { // Replace All 00792 done(KateSearch::srAll); 00793 actionButton(User1)->setFocus(); 00794 } 00795 00796 void KateReplacePrompt::slotUser2 () 00797 { // Replace & Close 00798 done(KateSearch::srLast); 00799 actionButton(User2)->setFocus(); 00800 } 00801 00802 void KateReplacePrompt::slotUser3 () 00803 { // Replace 00804 done(KateSearch::srYes); 00805 actionButton(User3)->setFocus(); 00806 } 00807 00808 void KateReplacePrompt::done (int result) 00809 { 00810 setResult(result); 00811 00812 emit clicked(); 00813 } 00814 //END KateReplacePrompt 00815 00816 //BEGIN SearchCommand 00817 bool SearchCommand::exec(class Kate::View *view, const TQString &cmd, TQString &msg) 00818 { 00819 TQString flags, pattern, replacement; 00820 if ( cmd.startsWith( "find" ) ) 00821 { 00822 00823 static TQRegExp re_find("find(?::([bcersw]*))?\\s+(.+)"); 00824 if ( re_find.search( cmd ) < 0 ) 00825 { 00826 msg = i18n("Usage: find[:[bcersw]] PATTERN"); 00827 return false; 00828 } 00829 flags = re_find.cap( 1 ); 00830 pattern = re_find.cap( 2 ); 00831 } 00832 00833 else if ( cmd.startsWith( "ifind" ) ) 00834 { 00835 static TQRegExp re_ifind("ifind(?::([bcrs]*))?\\s+(.*)"); 00836 if ( re_ifind.search( cmd ) < 0 ) 00837 { 00838 msg = i18n("Usage: ifind[:[bcrs]] PATTERN"); 00839 return false; 00840 } 00841 ifindClear(); 00842 return true; 00843 } 00844 00845 else if ( cmd.startsWith( "replace" ) ) 00846 { 00847 // Try if the pattern and replacement is quoted, using a quote character ["'] 00848 static TQRegExp re_rep("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s+\\2((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$"); 00849 // Or one quoted argument 00850 TQRegExp re_rep1("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$"); 00851 // Else, it's just one or two (space separated) words 00852 TQRegExp re_rep2("replace(?::([bceprsw]*))?\\s+(\\S+)(.*)"); 00853 #define unbackslash(s) p=0;\ 00854 while ( (p = pattern.find( '\\' + delim, p )) > -1 )\ 00855 {\ 00856 if ( !p || pattern[p-1] != '\\' )\ 00857 pattern.remove( p, 1 );\ 00858 p++;\ 00859 } 00860 00861 if ( re_rep.search( cmd ) >= 0 ) 00862 { 00863 flags = re_rep.cap(1); 00864 pattern = re_rep.cap( 3 ); 00865 replacement = re_rep.cap( 4 ); 00866 00867 int p(0); 00868 // unbackslash backslashed delimiter strings 00869 // in pattern .. 00870 TQString delim = re_rep.cap( 2 ); 00871 unbackslash(pattern); 00872 // .. and in replacement 00873 unbackslash(replacement); 00874 } 00875 else if ( re_rep1.search( cmd ) >= 0 ) 00876 { 00877 flags = re_rep1.cap(1); 00878 pattern = re_rep1.cap( 3 ); 00879 00880 int p(0); 00881 TQString delim = re_rep1.cap( 2 ); 00882 unbackslash(pattern); 00883 } 00884 else if ( re_rep2.search( cmd ) >= 0 ) 00885 { 00886 flags = re_rep2.cap( 1 ); 00887 pattern = re_rep2.cap( 2 ); 00888 replacement = TQString(re_rep2.cap( 3 )).stripWhiteSpace(); 00889 } 00890 else 00891 { 00892 msg = i18n("Usage: replace[:[bceprsw]] PATTERN [REPLACEMENT]"); 00893 return false; 00894 } 00895 kdDebug()<<"replace '"<<pattern<<"' with '"<<replacement<<"'"<<endl; 00896 #undef unbackslash 00897 } 00898 00899 long f = 0; 00900 if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards; 00901 if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor; 00902 if ( flags.contains( 'e' ) ) f |= KFindDialog::SelectedText; 00903 if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression; 00904 if ( flags.contains( 'p' ) ) f |= KReplaceDialog::PromptOnReplace; 00905 if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive; 00906 if ( flags.contains( 'w' ) ) f |= KFindDialog::WholeWordsOnly; 00907 00908 if ( cmd.startsWith( "find" ) ) 00909 { 00910 ((KateView*)view)->find( pattern, f ); 00911 return true; 00912 } 00913 else if ( cmd.startsWith( "replace" ) ) 00914 { 00915 f |= KReplaceDialog::BackReference; // mandatory here? 00916 ((KateView*)view)->replace( pattern, replacement, f ); 00917 return true; 00918 } 00919 00920 return false; 00921 } 00922 00923 bool SearchCommand::help(class Kate::View *, const TQString &cmd, TQString &msg) 00924 { 00925 if ( cmd == "find" ) 00926 msg = i18n("<p>Usage: <code>find[:bcersw] PATTERN</code></p>"); 00927 00928 else if ( cmd == "ifind" ) 00929 msg = i18n("<p>Usage: <code>ifind:[:bcrs] PATTERN</code>" 00930 "<br>ifind does incremental or 'as-you-type' search</p>"); 00931 00932 else 00933 msg = i18n("<p>Usage: <code>replace[:bceprsw] PATTERN REPLACEMENT</code></p>"); 00934 00935 msg += i18n( 00936 "<h4><caption>Options</h4><p>" 00937 "<b>b</b> - Search backward" 00938 "<br><b>c</b> - Search from cursor" 00939 "<br><b>r</b> - Pattern is a regular expression" 00940 "<br><b>s</b> - Case sensitive search" 00941 ); 00942 00943 if ( cmd == "find" ) 00944 msg += i18n( 00945 "<br><b>e</b> - Search in selected text only" 00946 "<br><b>w</b> - Search whole words only" 00947 ); 00948 00949 if ( cmd == "replace" ) 00950 msg += i18n( 00951 "<br><b>p</b> - Prompt for replace</p>" 00952 "<p>If REPLACEMENT is not present, an empty string is used.</p>" 00953 "<p>If you want to have whitespace in your PATTERN, you need to " 00954 "quote both PATTERN and REPLACEMENT with either single or double " 00955 "quotes. To have the quote characters in the strings, prepend them " 00956 "with a backslash."); 00957 00958 msg += "</p>"; 00959 return true; 00960 } 00961 00962 TQStringList SearchCommand::cmds() 00963 { 00964 TQStringList l; 00965 l << "find" << "replace" << "ifind"; 00966 return l; 00967 } 00968 00969 bool SearchCommand::wantsToProcessText( const TQString &cmdname ) 00970 { 00971 return cmdname == "ifind"; 00972 } 00973 00974 void SearchCommand::processText( Kate::View *view, const TQString &cmd ) 00975 { 00976 static TQRegExp re_ifind("ifind(?::([bcrs]*))?\\s(.*)"); 00977 if ( re_ifind.search( cmd ) > -1 ) 00978 { 00979 TQString flags = re_ifind.cap( 1 ); 00980 TQString pattern = re_ifind.cap( 2 ); 00981 00982 00983 // if there is no setup, or the text length is 0, set up the properties 00984 if ( ! m_ifindFlags || pattern.isEmpty() ) 00985 ifindInit( flags ); 00986 // if there is no fromCursor, add it if this is not the first character 00987 else if ( ! ( m_ifindFlags & KFindDialog::FromCursor ) && ! pattern.isEmpty() ) 00988 m_ifindFlags |= KFindDialog::FromCursor; 00989 00990 // search.. 00991 if ( ! pattern.isEmpty() ) 00992 { 00993 KateView *v = (KateView*)view; 00994 00995 // If it *looks like* we are continuing, place the cursor 00996 // at the beginning of the selection, so that the search continues. 00997 // ### check more carefully, like is the cursor currently at the end 00998 // of the selection. 00999 if ( pattern.startsWith( v->selection() ) && 01000 v->selection().length() + 1 == pattern.length() ) 01001 v->setCursorPositionInternal( v->selStartLine(), v->selStartCol() ); 01002 01003 v->find( pattern, m_ifindFlags, false ); 01004 } 01005 } 01006 } 01007 01008 void SearchCommand::ifindInit( const TQString &flags ) 01009 { 01010 long f = 0; 01011 if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards; 01012 if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor; 01013 if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression; 01014 if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive; 01015 m_ifindFlags = f; 01016 } 01017 01018 void SearchCommand::ifindClear() 01019 { 01020 m_ifindFlags = 0; 01021 } 01022 //END SearchCommand 01023 01024 // kate: space-indent on; indent-width 2; replace-tabs on;