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();
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();
00211   }
00212 
00213   s.wrappedEnd = s.cursor;
00214   s.wrapped = false;
00215 
00216   search( searchFlags );
00217 }
00218 
00219 void KateSearch::findAgain( bool back )
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   searchFlags.backward = searchFlags.backward != back;
00235   searchFlags.fromBeginning = false;
00236   searchFlags.prompt = true; // ### why is the above assignment there?
00237   s.cursor = getCursor();
00238 
00239   search( searchFlags );
00240 }
00241 
00242 void KateSearch::search( SearchFlags flags )
00243 {
00244   s.flags = flags;
00245 
00246   if( s.flags.fromBeginning ) {
00247     if( !s.flags.backward ) {
00248       s.cursor.setPos(0, 0);
00249     } else {
00250       s.cursor.setLine(doc()->numLines() - 1);
00251       s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00252     }
00253   }
00254 
00255   if((!s.flags.backward &&
00256        s.cursor.col() == 0 &&
00257        s.cursor.line() == 0 ) ||
00258      ( s.flags.backward &&
00259        s.cursor.col() == doc()->lineLength( s.cursor.line() ) &&
00260        s.cursor.line() == (((int)doc()->numLines()) - 1) ) ) {
00261     s.flags.finished = true;
00262   }
00263 
00264   if( s.flags.replace ) {
00265     replaces = 0;
00266     if( s.flags.prompt )
00267       promptReplace();
00268     else
00269       replaceAll();
00270   } else {
00271     findAgain();
00272   }
00273 }
00274 
00275 void KateSearch::wrapSearch()
00276 {
00277   if( s.flags.selected )
00278   {
00279     KateTextCursor start (s.selBegin);
00280     KateTextCursor end (s.selEnd);
00281 
00282     // recalc for block sel, to have start with lowest col, end with highest
00283     if (m_view->blockSelectionMode())
00284     {
00285       start.setCol (QMIN(s.selBegin.col(), s.selEnd.col()));
00286       end.setCol (QMAX(s.selBegin.col(), s.selEnd.col()));
00287     }
00288 
00289     s.cursor = s.flags.backward ? end : start;
00290   }
00291   else
00292   {
00293     if( !s.flags.backward ) {
00294       s.cursor.setPos(0, 0);
00295     } else {
00296       s.cursor.setLine(doc()->numLines() - 1);
00297       s.cursor.setCol(doc()->lineLength( s.cursor.line() ) );
00298     }
00299   }
00300 
00301   // oh, we wrapped around one time allready now !
00302   // only check that on replace
00303   s.wrapped = s.flags.replace;
00304 
00305   replaces = 0;
00306   s.flags.finished = true;
00307 }
00308 
00309 void KateSearch::findAgain()
00310 {
00311   if(  s_pattern.isEmpty() ) {
00312     find();
00313     return;
00314   }
00315 
00316   if ( doSearch(  s_pattern ) ) {
00317     exposeFound( s.cursor, s.matchedLength );
00318   } else if( !s.flags.finished ) {
00319     if( askContinue() ) {
00320       wrapSearch();
00321       findAgain();
00322     } else {
00323       if (arbitraryHLExample) m_arbitraryHLList->clear();
00324     }
00325   } else {
00326     if (arbitraryHLExample) m_arbitraryHLList->clear();
00327     if ( s.showNotFound )
00328       KMessageBox::sorry( view(),
00329         i18n("Search string '%1' not found!")
00330              .arg( KStringHandler::csqueeze(  s_pattern ) ),
00331         i18n("Find"));
00332   }
00333 }
00334 
00335 void KateSearch::replaceAll()
00336 {
00337   doc()->editStart ();
00338 
00339   while( doSearch(  s_pattern ) )
00340     replaceOne();
00341 
00342   doc()->editEnd ();
00343 
00344   if( !s.flags.finished ) {
00345     if( askContinue() ) {
00346       wrapSearch();
00347       replaceAll();
00348     }
00349   } else {
00350     KMessageBox::information( view(),
00351         i18n("%n replacement made.","%n replacements made.",replaces),
00352         i18n("Replace") );
00353   }
00354 }
00355 
00356 void KateSearch::promptReplace()
00357 {
00358   if ( doSearch(  s_pattern ) ) {
00359     exposeFound( s.cursor, s.matchedLength );
00360     replacePrompt->show();
00361     replacePrompt->setFocus ();
00362   } else if( !s.flags.finished && askContinue() ) {
00363     wrapSearch();
00364     promptReplace();
00365   } else {
00366     if (arbitraryHLExample) m_arbitraryHLList->clear();
00367     replacePrompt->hide();
00368     KMessageBox::information( view(),
00369         i18n("%n replacement made.","%n replacements made.",replaces),
00370         i18n("Replace") );
00371   }
00372 }
00373 
00374 void KateSearch::replaceOne()
00375 {
00376   QString replaceWith = m_replacement;
00377   if ( s.flags.regExp && s.flags.useBackRefs ) {
00378     // replace each "(?!\)\d+" with the corresponding capture
00379     QRegExp br("\\\\(\\d+)");
00380     int pos = br.search( replaceWith );
00381     int ncaps = m_re.numCaptures();
00382     while ( pos >= 0 ) {
00383       QString sc;
00384       if ( !pos ||  replaceWith.at( pos-1) != '\\' ) {
00385         int ccap = br.cap(1).toInt();
00386         if (ccap <= ncaps ) {
00387           sc = m_re.cap( ccap );
00388           replaceWith.replace( pos, br.matchedLength(), sc );
00389         }
00390         else {
00391           kdDebug()<<"KateSearch::replaceOne(): you don't have "<<ccap<<" backreferences in regexp '"<<m_re.pattern()<<"'"<<endl;
00392         }
00393       }
00394       pos = br.search( replaceWith, pos+QMAX(br.matchedLength(), (int)sc.length()) );
00395     }
00396   }
00397 
00398   doc()->editStart();
00399   doc()->removeText( s.cursor.line(), s.cursor.col(),
00400       s.cursor.line(), s.cursor.col() + s.matchedLength );
00401   doc()->insertText( s.cursor.line(), s.cursor.col(), replaceWith );
00402   doc()->editEnd(),
00403 
00404   replaces++;
00405 
00406   // if we inserted newlines, we better adjust.
00407   uint newlines = replaceWith.contains('\n');
00408   if ( newlines )
00409   {
00410     if ( ! s.flags.backward )
00411     {
00412       s.cursor.setLine( s.cursor.line() + newlines );
00413       s.cursor.setCol( replaceWith.length() - replaceWith.findRev('\n') );
00414     }
00415     // selection?
00416     if ( s.flags.selected )
00417       s.selEnd.setLine( s.selEnd.line() + newlines );
00418   }
00419 
00420 
00421   // adjust selection endcursor if needed
00422   if( s.flags.selected && s.cursor.line() == s.selEnd.line() )
00423   {
00424     s.selEnd.setCol(s.selEnd.col() + replaceWith.length() - s.matchedLength );
00425   }
00426 
00427   // adjust wrap cursor if needed
00428   if( s.cursor.line() == s.wrappedEnd.line() && s.cursor.col() <= s.wrappedEnd.col())
00429   {
00430     s.wrappedEnd.setCol(s.wrappedEnd.col() + replaceWith.length() - s.matchedLength );
00431   }
00432 
00433   if( !s.flags.backward ) {
00434     s.cursor.setCol(s.cursor.col() + replaceWith.length());
00435   } else if( s.cursor.col() > 0 ) {
00436     s.cursor.setCol(s.cursor.col() - 1);
00437   } else {
00438     s.cursor.setLine(s.cursor.line() - 1);
00439     if( s.cursor.line() >= 0 ) {
00440       s.cursor.setCol(doc()->lineLength( s.cursor.line() ));
00441     }
00442   }
00443 }
00444 
00445 void KateSearch::skipOne()
00446 {
00447   if( !s.flags.backward ) {
00448     s.cursor.setCol(s.cursor.col() + s.matchedLength);
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::replaceSlot() {
00460   switch( (Dialog_results)replacePrompt->result() ) {
00461   case srCancel: replacePrompt->hide();                break;
00462   case srAll:    replacePrompt->hide(); replaceAll();  break;
00463   case srYes:    replaceOne(); promptReplace();        break;
00464   case srLast:   replacePrompt->hide(), replaceOne();  break;
00465   case srNo:     skipOne();    promptReplace();        break;
00466   }
00467 }
00468 
00469 bool KateSearch::askContinue()
00470 {
00471   QString made =
00472      i18n( "%n replacement made.",
00473            "%n replacements made.",
00474            replaces );
00475 
00476   QString reached = !s.flags.backward ?
00477      i18n( "End of document reached." ) :
00478      i18n( "Beginning of document reached." );
00479 
00480   if (KateViewConfig::global()->searchFlags() & KFindDialog::SelectedText)
00481   {
00482     reached = !s.flags.backward ?
00483      i18n( "End of selection reached." ) :
00484      i18n( "Beginning of selection reached." );
00485   }
00486 
00487   QString question = !s.flags.backward ?
00488      i18n( "Continue from the beginning?" ) :
00489      i18n( "Continue from the end?" );
00490 
00491   QString text = s.flags.replace ?
00492      made + "\n" + reached + "\n" + question :
00493      reached + "\n" + question;
00494 
00495   return KMessageBox::Yes == KMessageBox::questionYesNo(
00496      view(), text, s.flags.replace ? i18n("Replace") : i18n("Find"),
00497      KStdGuiItem::cont(), i18n("&Stop") );
00498 }
00499 
00500 QString KateSearch::getSearchText()
00501 {
00502   // SelectionOnly: use selection
00503   // WordOnly: use word under cursor
00504   // SelectionWord: use selection if available, else use word under cursor
00505   // WordSelection: use word if available, else use selection
00506   QString str;
00507 
00508   int getFrom = view()->config()->textToSearchMode();
00509   switch (getFrom)
00510   {
00511   case KateViewConfig::SelectionOnly: // (Windows)
00512     //kdDebug() << "getSearchText(): SelectionOnly" << endl;
00513     if( m_view->hasSelection() )
00514       str = m_view->selection();
00515     break;
00516 
00517   case KateViewConfig::SelectionWord: // (classic Kate behavior)
00518     //kdDebug() << "getSearchText(): SelectionWord" << endl;
00519     if( m_view->hasSelection() )
00520       str = m_view->selection();
00521     else
00522       str = view()->currentWord();
00523     break;
00524 
00525   case KateViewConfig::WordOnly: // (weird?)
00526     //kdDebug() << "getSearchText(): WordOnly" << endl;
00527     str = view()->currentWord();
00528     break;
00529 
00530   case KateViewConfig::WordSelection: // (persistent selection lover)
00531     //kdDebug() << "getSearchText(): WordSelection" << endl;
00532     str = view()->currentWord();
00533     if (str.isEmpty() && m_view->hasSelection() )
00534       str = m_view->selection();
00535     break;
00536 
00537   default: // (nowhere)
00538     //kdDebug() << "getSearchText(): Nowhere" << endl;
00539     break;
00540   }
00541 
00542   str.replace( QRegExp("^\\n"), "" );
00543   str.replace( QRegExp("\\n.*"), "" );
00544 
00545   return str;
00546 }
00547 
00548 KateTextCursor KateSearch::getCursor()
00549 {
00550   return KateTextCursor(view()->cursorLine(), view()->cursorColumnReal());
00551 }
00552 
00553 bool KateSearch::doSearch( const QString& text )
00554 {
00555 /*
00556   rodda: Still Working on this... :)
00557 
00558   bool result = false;
00559 
00560   if (m_searchResults.count()) {
00561     m_resultIndex++;
00562     if (m_resultIndex < (int)m_searchResults.count()) {
00563       s = m_searchResults[m_resultIndex];
00564       result = true;
00565     }
00566 
00567   } else {
00568     int temp = 0;
00569     do {*/
00570 
00571 #if 0
00572   static int oldLine = -1;
00573   static int oldCol = -1;
00574 #endif
00575 
00576   uint line = s.cursor.line();
00577   uint col = s.cursor.col();// + (result ? s.matchedLength : 0);
00578   bool backward = s.flags.backward;
00579   bool caseSensitive = s.flags.caseSensitive;
00580   bool regExp = s.flags.regExp;
00581   bool wholeWords = s.flags.wholeWords;
00582   uint foundLine, foundCol, matchLen;
00583   bool found = false;
00584   //kdDebug() << "Searching at " << line << ", " << col << endl;
00585 //   kdDebug()<<"KateSearch::doSearch: "<<line<<", "<<col<<", "<<backward<<endl;
00586 
00587   do {
00588       if( regExp ) {
00589         m_re = QRegExp( text, caseSensitive );
00590         found = doc()->searchText( line, col, m_re,
00591                                   &foundLine, &foundCol,
00592                                   &matchLen, backward );
00593       }
00594       else if ( wholeWords )
00595       {
00596         bool maybefound = false;
00597         do
00598         {
00599           maybefound = doc()->searchText( line, col, text,
00600                                   &foundLine, &foundCol,
00601                                   &matchLen, caseSensitive, backward );
00602           if ( maybefound )
00603           {
00604             found = (
00605                       ( foundCol == 0 ||
00606                         ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol - 1 ) ) ) &&
00607                       ( foundCol + matchLen == doc()->lineLength( foundLine ) ||
00608                         ! doc()->highlight()->isInWord( doc()->textLine( foundLine ).at( foundCol + matchLen ) ) )
00609                     );
00610             if ( found )
00611             {
00612               break;
00613             }
00614             else
00615             {
00616               line = foundLine;
00617               col = foundCol + 1;
00618             }
00619           }
00620         } while ( maybefound );
00621       }
00622       else {
00623         found = doc()->searchText( line, col, text,
00624                                   &foundLine, &foundCol,
00625                                   &matchLen, caseSensitive, backward );
00626       }
00627 
00628     if ( found && s.flags.selected )
00629     {
00630       KateTextCursor start (s.selBegin);
00631       KateTextCursor end (s.selEnd);
00632 
00633       // recalc for block sel, to have start with lowest col, end with highest
00634       if (m_view->blockSelectionMode())
00635       {
00636         start.setCol (QMIN(s.selBegin.col(), s.selEnd.col()));
00637         end.setCol (QMAX(s.selBegin.col(), s.selEnd.col()));
00638       }
00639 
00640       if ( !s.flags.backward && KateTextCursor( foundLine, foundCol ) >= end
00641         ||  s.flags.backward && KateTextCursor( foundLine, foundCol ) < start )
00642       {
00643         found = false;
00644       }
00645       else if (m_view->blockSelectionMode())
00646       {
00647         if ((int)foundCol >= start.col() && (int)foundCol < end.col())
00648           break;
00649       }
00650     }
00651 
00652     line = foundLine;
00653     col = foundCol+1;
00654   }
00655   while (s.flags.selected && m_view->blockSelectionMode() && found);
00656   // in the case we want to search in selection + blockselection we need to loop
00657 
00658   if( !found ) return false;
00659 
00660   // save the search result
00661   s.cursor.setPos(foundLine, foundCol);
00662   s.matchedLength = matchLen;
00663 
00664   // we allready wrapped around one time
00665   if (s.wrapped)
00666   {
00667     if (s.flags.backward)
00668     {
00669       if ( (s.cursor.line() < s.wrappedEnd.line())
00670            || ( (s.cursor.line() == s.wrappedEnd.line()) && ((s.cursor.col()+matchLen) <= uint(s.wrappedEnd.col())) ) )
00671         return false;
00672     }
00673     else
00674     {
00675       if ( (s.cursor.line() > s.wrappedEnd.line())
00676            || ( (s.cursor.line() == s.wrappedEnd.line()) && (s.cursor.col() > s.wrappedEnd.col()) ) )
00677         return false;
00678     }
00679   }
00680 
00681 //   kdDebug() << "Found at " << s.cursor.line() << ", " << s.cursor.col() << endl;
00682 
00683 
00684   //m_searchResults.append(s);
00685 
00686   if (arbitraryHLExample)  {
00687     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);
00688     hl->setBold();
00689     hl->setTextColor(Qt::white);
00690     hl->setBGColor(Qt::black);
00691     // destroy the highlight upon change
00692     connect(hl, SIGNAL(contentsChanged()), hl, SIGNAL(eliminated()));
00693     m_arbitraryHLList->append(hl);
00694   }
00695 
00696   return true;
00697 
00698     /* rodda: more of my search highlighting work
00699 
00700     } while (++temp < 100);
00701 
00702     if (result) {
00703       s = m_searchResults.first();
00704       m_resultIndex = 0;
00705     }
00706   }
00707 
00708   return result;*/
00709 }
00710 
00711 void KateSearch::exposeFound( KateTextCursor &cursor, int slen )
00712 {
00713   view()->setCursorPositionInternal ( cursor.line(), cursor.col() + slen, 1 );
00714   view()->setSelection( cursor.line(), cursor.col(), cursor.line(), cursor.col() + slen );
00715 }
00716 //END KateSearch
00717 
00718 //BEGIN KateReplacePrompt
00719 // this dialog is not modal
00720 KateReplacePrompt::KateReplacePrompt ( QWidget *parent )
00721   : KDialogBase ( parent, 0L, false, i18n( "Replace Confirmation" ),
00722                   User3 | User2 | User1 | Close | Ok , Ok, true,
00723                   i18n("Replace &All"), i18n("Re&place && Close"), i18n("&Replace") )
00724 {
00725   setButtonOK( i18n("&Find Next") );
00726   QWidget *page = new QWidget(this);
00727   setMainWidget(page);
00728 
00729   QBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
00730   QLabel *label = new QLabel(i18n("Found an occurrence of your search term. What do you want to do?"),page);
00731   topLayout->addWidget(label );
00732 }
00733 
00734 void KateReplacePrompt::slotOk ()
00735 { // Search Next
00736   done(KateSearch::srNo);
00737   actionButton(Ok)->setFocus();
00738 }
00739 
00740 void KateReplacePrompt::slotClose ()
00741 { // Close
00742   done(KateSearch::srCancel);
00743   actionButton(Close)->setFocus();
00744 }
00745 
00746 void KateReplacePrompt::slotUser1 ()
00747 { // Replace All
00748   done(KateSearch::srAll);
00749   actionButton(User1)->setFocus();
00750 }
00751 
00752 void KateReplacePrompt::slotUser2 ()
00753 { // Replace & Close
00754   done(KateSearch::srLast);
00755   actionButton(User2)->setFocus();
00756 }
00757 
00758 void KateReplacePrompt::slotUser3 ()
00759 { // Replace
00760   done(KateSearch::srYes);
00761   actionButton(User3)->setFocus();
00762 }
00763 
00764 void KateReplacePrompt::done (int result)
00765 {
00766   setResult(result);
00767 
00768   emit clicked();
00769 }
00770 //END KateReplacePrompt
00771 
00772 //BEGIN SearchCommand
00773 bool SearchCommand::exec(class Kate::View *view, const QString &cmd, QString &msg)
00774 {
00775   QString flags, pattern, replacement;
00776   if ( cmd.startsWith( "find" ) )
00777   {
00778 
00779     static QRegExp re_find("find(?::([bcersw]*))?\\s+(.+)");
00780     if ( re_find.search( cmd ) < 0 )
00781     {
00782       msg = i18n("Usage: find[:[bcersw]] PATTERN");
00783       return false;
00784     }
00785     flags = re_find.cap( 1 );
00786     pattern = re_find.cap( 2 );
00787   }
00788 
00789   else if ( cmd.startsWith( "ifind" ) )
00790   {
00791     static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s+(.*)");
00792     if ( re_ifind.search( cmd ) < 0 )
00793     {
00794       msg = i18n("Usage: ifind[:[bcrs]] PATTERN");
00795       return false;
00796     }
00797     ifindClear();
00798     return true;
00799   }
00800 
00801   else if ( cmd.startsWith( "replace" ) )
00802   {
00803     // Try if the pattern and replacement is quoted, using a quote character ["']
00804     static QRegExp re_rep("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s+\\2((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
00805     // Or one quoted argument
00806     QRegExp re_rep1("replace(?::([bceprsw]*))?\\s+([\"'])((?:[^\\\\\\\\2]|\\\\.)*)\\2\\s*$");
00807     // Else, it's just one or two (space separated) words
00808     QRegExp re_rep2("replace(?::([bceprsw]*))?\\s+(\\S+)(.*)");
00809 #define unbackslash(s) p=0;\
00810 while ( (p = pattern.find( '\\' + delim, p )) > -1 )\
00811 {\
00812   if ( !p || pattern[p-1] != '\\' )\
00813     pattern.remove( p, 1 );\
00814   p++;\
00815 }
00816 
00817     if ( re_rep.search( cmd ) >= 0 )
00818     {
00819       flags = re_rep.cap(1);
00820       pattern = re_rep.cap( 3 );
00821       replacement = re_rep.cap( 4 );
00822 
00823       int p(0);
00824       // unbackslash backslashed delimiter strings
00825       // in pattern ..
00826       QString delim = re_rep.cap( 2 );
00827       unbackslash(pattern);
00828       // .. and in replacement
00829       unbackslash(replacement);
00830     }
00831     else if ( re_rep1.search( cmd ) >= 0 )
00832     {
00833       flags = re_rep1.cap(1);
00834       pattern = re_rep1.cap( 3 );
00835 
00836       int p(0);
00837       QString delim = re_rep1.cap( 2 );
00838       unbackslash(pattern);
00839     }
00840     else if ( re_rep2.search( cmd ) >= 0 )
00841     {
00842       flags = re_rep2.cap( 1 );
00843       pattern = re_rep2.cap( 2 );
00844       replacement = re_rep2.cap( 3 ).stripWhiteSpace();
00845     }
00846     else
00847     {
00848       msg = i18n("Usage: replace[:[bceprsw]] PATTERN [REPLACEMENT]");
00849       return false;
00850     }
00851     kdDebug()<<"replace '"<<pattern<<"' with '"<<replacement<<"'"<<endl;
00852 #undef unbackslash
00853   }
00854 
00855   long f = 0;
00856   if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
00857   if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
00858   if ( flags.contains( 'e' ) ) f |= KFindDialog::SelectedText;
00859   if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
00860   if ( flags.contains( 'p' ) ) f |= KReplaceDialog::PromptOnReplace;
00861   if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
00862   if ( flags.contains( 'w' ) ) f |= KFindDialog::WholeWordsOnly;
00863 
00864   if ( cmd.startsWith( "find" ) )
00865   {
00866     ((KateView*)view)->find( pattern, f );
00867     return true;
00868   }
00869   else if ( cmd.startsWith( "replace" ) )
00870   {
00871     f |= KReplaceDialog::BackReference; // mandatory here?
00872     ((KateView*)view)->replace( pattern, replacement, f );
00873     return true;
00874   }
00875 
00876   return false;
00877 }
00878 
00879 bool SearchCommand::help(class Kate::View *, const QString &cmd, QString &msg)
00880 {
00881   if ( cmd == "find" )
00882     msg = i18n("<p>Usage: <code>find[:bcersw] PATTERN</code></p>");
00883 
00884   else if ( cmd == "ifind" )
00885     msg = i18n("<p>Usage: <code>ifind:[:bcrs] PATTERN</code>"
00886         "<br>ifind does incremental or 'as-you-type' search</p>");
00887 
00888   else
00889     msg = i18n("<p>Usage: <code>replace[:bceprsw] PATTERN REPLACEMENT</code></p>");
00890 
00891   msg += i18n(
00892       "<h4><caption>Options</h4><p>"
00893       "<b>b</b> - Search backward"
00894       "<br><b>c</b> - Search from cursor"
00895       "<br><b>r</b> - Pattern is a regular expression"
00896       "<br><b>s</b> - Case sensitive search"
00897              );
00898 
00899   if ( cmd == "find" )
00900     msg += i18n(
00901         "<br><b>e</b> - Search in selected text only"
00902         "<br><b>w</b> - Search whole words only"
00903                );
00904 
00905   if ( cmd == "replace" )
00906     msg += i18n(
00907         "<br><b>p</b> - Prompt for replace</p>"
00908         "<p>If REPLACEMENT is not present, an empty string is used.</p>"
00909         "<p>If you want to have whitespace in your PATTERN, you need to "
00910         "quote both PATTERN and REPLACEMENT with either single or double "
00911         "quotes. To have the quote characters in the strings, prepend them "
00912         "with a backslash.");
00913 
00914   msg += "</p>";
00915   return true;
00916 }
00917 
00918 QStringList SearchCommand::cmds()
00919 {
00920   QStringList l;
00921   l << "find" << "replace" << "ifind";
00922   return l;
00923 }
00924 
00925 bool SearchCommand::wantsToProcessText( const QString &cmdname )
00926 {
00927   return  cmdname == "ifind";
00928 }
00929 
00930 void SearchCommand::processText( Kate::View *view, const QString &cmd )
00931 {
00932   static QRegExp re_ifind("ifind(?::([bcrs]*))?\\s(.*)");
00933   if ( re_ifind.search( cmd ) > -1 )
00934   {
00935     QString flags = re_ifind.cap( 1 );
00936     QString pattern = re_ifind.cap( 2 );
00937 
00938 
00939     // if there is no setup, or the text length is 0, set up the properties
00940     if ( ! m_ifindFlags || pattern.isEmpty() )
00941       ifindInit( flags );
00942     // if there is no fromCursor, add it if this is not the first character
00943     else if ( ! ( m_ifindFlags & KFindDialog::FromCursor ) && ! pattern.isEmpty() )
00944       m_ifindFlags |= KFindDialog::FromCursor;
00945 
00946     // search..
00947     if ( ! pattern.isEmpty() )
00948     {
00949       KateView *v = (KateView*)view;
00950 
00951       // If it *looks like* we are continuing, place the cursor
00952       // at the beginning of the selection, so that the search continues.
00953       // ### check more carefully, like is  the cursor currently at the end
00954       // of the selection.
00955       if ( pattern.startsWith( v->selection() ) &&
00956            v->selection().length() + 1 == pattern.length() )
00957         v->setCursorPositionInternal( v->selStartLine(), v->selStartCol() );
00958 
00959       v->find( pattern, m_ifindFlags, false );
00960     }
00961   }
00962 }
00963 
00964 void SearchCommand::ifindInit( const QString &flags )
00965 {
00966   long f = 0;
00967   if ( flags.contains( 'b' ) ) f |= KFindDialog::FindBackwards;
00968   if ( flags.contains( 'c' ) ) f |= KFindDialog::FromCursor;
00969   if ( flags.contains( 'r' ) ) f |= KFindDialog::RegularExpression;
00970   if ( flags.contains( 's' ) ) f |= KFindDialog::CaseSensitive;
00971   m_ifindFlags = f;
00972 }
00973 
00974 void SearchCommand::ifindClear()
00975 {
00976   m_ifindFlags = 0;
00977 }
00978 //END SearchCommand
00979 
00980 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys