kate Library API Documentation

katesearch.cpp

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