| # kutils Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kfind.cpp

    00001 /*
    00002     Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
    00003     Copyright (C) 2002, David Faure <david@mandrakesoft.com>
    00004     This file is part of the KDE project
    00005
    00006     This library is free software; you can redistribute it and/or
    00007     modify it under the terms of the GNU Library General Public
    00008     License version 2, as published by the Free Software Foundation.
    00009
    00010     This library is distributed in the hope that it will be useful,
    00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
    00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00013     Library General Public License for more details.
    00014
    00015     You should have received a copy of the GNU Library General Public License
    00016     along with this library; see the file COPYING.LIB.  If not, write to
    00017     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00018     Boston, MA 02111-1307, USA.
    00019 */
    00020
    00021 #include "kfind.h"
    00022 #include "kfinddialog.h"
    00023 #include <kapplication.h>
    00024 #include <klocale.h>
    00025 #include <kmessagebox.h>
    00026 #include <qlabel.h>
    00027 #include <qregexp.h>
    00028 #include <kdebug.h>
    00029
    00030 //#define DEBUG_FIND
    00031
    00032 #define INDEX_NOMATCH -1
    00033
    00034 class KFindNextDialog : public KDialogBase
    00035 {
    00036 public:
    00037     KFindNextDialog(const QString &pattern, QWidget *parent);
    00038 };
    00039
    00040 // Create the dialog.
    00041 KFindNextDialog::KFindNextDialog(const QString &pattern, QWidget *parent) :
    00042     KDialogBase(parent, 0, false,  // non-modal!
    00043         i18n("Find"),
    00044         User1 | Close,
    00045         User1,
    00046         false,
    00047         i18n("&Yes"))
    00048 {
    00049     setMainWidget( new QLabel( i18n("<qt>Find next occurrence of '<b>%1</b>'?</qt>").arg(pattern), this ) );
    00050 }
    00051
    00053
    00054 KFind::KFind( const QString &pattern, long options, QWidget *parent )
    00055     : QObject( parent )
    00056 {
    00057     m_options = options;
    00058     init( pattern );
    00059 }
    00060
    00061 void KFind::init( const QString& pattern )
    00062 {
    00063     m_matches = 0;
    00064     m_pattern = pattern;
    00065     m_dialog = 0;
    00066     m_dialogClosed = false;
    00067     m_index = INDEX_NOMATCH;
    00068     m_lastResult = NoMatch;
    00069     if (m_options & KFindDialog::RegularExpression)
    00070         m_regExp = new QRegExp(pattern, m_options & KFindDialog::CaseSensitive);
    00071     else {
    00072         m_regExp = 0;
    00073     }
    00074 }
    00075
    00076 KFind::~KFind()
    00077 {
    00078     delete m_dialog;
    00079 }
    00080
    00081 bool KFind::needData() const
    00082 {
    00083     // always true when m_text is empty.
    00084     if (m_options & KFindDialog::FindBackwards)
    00085         return m_index < 0;
    00086     else
    00087         return m_index >= (int)m_text.length() || m_index == INDEX_NOMATCH;
    00088 }
    00089
    00090 void KFind::setData( const QString& data, int startPos )
    00091 {
    00092     m_text = data;
    00093     if ( startPos != -1 )
    00094         m_index = startPos;
    00095     else if (m_options & KFindDialog::FindBackwards)
    00096         m_index = QMAX( (int)m_text.length() - 1, 0 );
    00097     else
    00098         m_index = 0;
    00099 #ifdef DEBUG_FIND
    00100     kdDebug() << "setData: '" << m_text << "' m_index=" << m_index << endl;
    00101 #endif
    00102     Q_ASSERT( m_index != INDEX_NOMATCH );
    00103     m_lastResult = NoMatch;
    00104 }
    00105
    00106 KDialogBase* KFind::findNextDialog( bool create )
    00107 {
    00108     if ( !m_dialog && create )
    00109     {
    00110         m_dialog = new KFindNextDialog( m_pattern, parentWidget() );
    00111         connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotFindNext() ) );
    00112         connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) );
    00113     }
    00114     return m_dialog;
    00115 }
    00116
    00117 KFind::Result KFind::find()
    00118 {
    00119     Q_ASSERT( m_index != INDEX_NOMATCH );
    00120     if ( m_lastResult == Match )
    00121     {
    00122         // Move on before looking for the next match, _if_ we just found a match
    00123         if (m_options & KFindDialog::FindBackwards) {
    00124             m_index--;
    00125             if ( m_index == -1 ) // don't call KFind::find with -1, it has a special meaning
    00126             {
    00127                 m_lastResult = NoMatch;
    00128                 return NoMatch;
    00129             }
    00130         } else
    00131             m_index++;
    00132     }
    00133
    00134 #ifdef DEBUG_FIND
    00135     kdDebug() << k_funcinfo << "m_index=" << m_index << endl;
    00136 #endif
    00137     do // this loop is only because validateMatch can fail
    00138     {
    00139         // Find the next candidate match.
    00140         if ( m_options & KFindDialog::RegularExpression )
    00141             m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength);
    00142         else
    00143             m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength);
    00144         if ( m_index != -1 )
    00145         {
    00146             // Flexibility: the app can add more rules to validate a possible match
    00147             if ( validateMatch( m_text, m_index, m_matchedLength ) )
    00148             {
    00149                 m_matches++;
    00150                 // Tell the world about the match we found, in case someone wants to
    00151                 // highlight it.
    00152                 emit highlight(m_text, m_index, m_matchedLength);
    00153
    00154                 if ( !m_dialogClosed )
    00155                     findNextDialog(true)->show();
    00156
    00157 #ifdef DEBUG_FIND
    00158                 kdDebug() << k_funcinfo << "Match. Next m_index=" << m_index << endl;
    00159 #endif
    00160                 m_lastResult = Match;
    00161                 return Match;
    00162             }
    00163             else // Skip match
    00164                 if (m_options & KFindDialog::FindBackwards)
    00165                     m_index--;
    00166                 else
    00167                     m_index++;
    00168         } else
    00169             m_index = INDEX_NOMATCH;
    00170     }
    00171     while (m_index != INDEX_NOMATCH);
    00172
    00173 #ifdef DEBUG_FIND
    00174     kdDebug() << k_funcinfo << "NoMatch. m_index=" << m_index << endl;
    00175 #endif
    00176     m_lastResult = NoMatch;
    00177     return NoMatch;
    00178 }
    00179
    00180 // static
    00181 int KFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
    00182 {
    00183     // Handle regular expressions in the appropriate way.
    00184     if (options & KFindDialog::RegularExpression)
    00185     {
    00186         QRegExp regExp(pattern, options & KFindDialog::CaseSensitive);
    00187
    00188         return find(text, regExp, index, options, matchedLength);
    00189     }
    00190
    00191     bool caseSensitive = (options & KFindDialog::CaseSensitive);
    00192
    00193     if (options & KFindDialog::WholeWordsOnly)
    00194     {
    00195         if (options & KFindDialog::FindBackwards)
    00196         {
    00197             // Backward search, until the beginning of the line...
    00198             while (index >= 0)
    00199             {
    00200                 // ...find the next match.
    00201                 index = text.findRev(pattern, index, caseSensitive);
    00202                 if (index == -1)
    00203                     break;
    00204
    00205                 // Is the match delimited correctly?
    00206                 *matchedLength = pattern.length();
    00207                 if (isWholeWords(text, index, *matchedLength))
    00208                     break;
    00209                 index--;
    00210             }
    00211         }
    00212         else
    00213         {
    00214             // Forward search, until the end of the line...
    00215             while (index < (int)text.length())
    00216             {
    00217                 // ...find the next match.
    00218                 index = text.find(pattern, index, caseSensitive);
    00219                 if (index == -1)
    00220                     break;
    00221
    00222                 // Is the match delimited correctly?
    00223                 *matchedLength = pattern.length();
    00224                 if (isWholeWords(text, index, *matchedLength))
    00225                     break;
    00226                 index++;
    00227             }
    00228             if (index >= (int)text.length()) // end of line
    00229                 index = -1; // not found
    00230         }
    00231     }
    00232     else
    00233     {
    00234         // Non-whole-word search.
    00235         if (options & KFindDialog::FindBackwards)
    00236         {
    00237             index = text.findRev(pattern, index, caseSensitive);
    00238         }
    00239         else
    00240         {
    00241             index = text.find(pattern, index, caseSensitive);
    00242         }
    00243         if (index != -1)
    00244         {
    00245             *matchedLength = pattern.length();
    00246         }
    00247     }
    00248     return index;
    00249 }
    00250
    00251 // static
    00252 int KFind::find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
    00253 {
    00254     if (options & KFindDialog::WholeWordsOnly)
    00255     {
    00256         if (options & KFindDialog::FindBackwards)
    00257         {
    00258             // Backward search, until the beginning of the line...
    00259             while (index >= 0)
    00260             {
    00261                 // ...find the next match.
    00262                 index = text.findRev(pattern, index);
    00263                 if (index == -1)
    00264                     break;
    00265
    00266                 // Is the match delimited correctly?
    00267                 //pattern.match(text, index, matchedLength, false);
    00268                 /*int pos =*/ pattern.search( text.mid(index) );
    00269                 *matchedLength = pattern.matchedLength();
    00270                 if (isWholeWords(text, index, *matchedLength))
    00271                     break;
    00272                 index--;
    00273             }
    00274         }
    00275         else
    00276         {
    00277             // Forward search, until the end of the line...
    00278             while (index < (int)text.length())
    00279             {
    00280                 // ...find the next match.
    00281                 index = text.find(pattern, index);
    00282                 if (index == -1)
    00283                     break;
    00284
    00285                 // Is the match delimited correctly?
    00286                 //pattern.match(text, index, matchedLength, false);
    00287                 /*int pos =*/ pattern.search( text.mid(index) );
    00288                 *matchedLength = pattern.matchedLength();
    00289                 if (isWholeWords(text, index, *matchedLength))
    00290                     break;
    00291                 index++;
    00292             }
    00293             if (index >= (int)text.length()) // end of line
    00294                 index = -1; // not found
    00295         }
    00296     }
    00297     else
    00298     {
    00299         // Non-whole-word search.
    00300         if (options & KFindDialog::FindBackwards)
    00301         {
    00302             index = text.findRev(pattern, index);
    00303         }
    00304         else
    00305         {
    00306             index = text.find(pattern, index);
    00307         }
    00308         if (index != -1)
    00309         {
    00310             //pattern.match(text, index, matchedLength, false);
    00311             /*int pos =*/ pattern.search( text.mid(index) );
    00312             *matchedLength = pattern.matchedLength();
    00313         }
    00314     }
    00315     return index;
    00316 }
    00317
    00318 bool KFind::isInWord(QChar ch)
    00319 {
    00320     return ch.isLetter() || ch.isDigit() || ch == '_';
    00321 }
    00322
    00323 bool KFind::isWholeWords(const QString &text, int starts, int matchedLength)
    00324 {
    00325     if ((starts == 0) || (!isInWord(text[starts - 1])))
    00326     {
    00327         int ends = starts + matchedLength;
    00328
    00329         if ((ends == (int)text.length()) || (!isInWord(text[ends])))
    00330             return true;
    00331     }
    00332     return false;
    00333 }
    00334
    00335 void KFind::slotFindNext()
    00336 {
    00337     emit findNext();
    00338 }
    00339
    00340 void KFind::slotDialogClosed()
    00341 {
    00342     emit dialogClosed();
    00343     m_dialogClosed = true;
    00344 }
    00345
    00346 void KFind::displayFinalDialog() const
    00347 {
    00348     QString message;
    00349     if ( numMatches() )
    00350         message = i18n( "1 match found.", "%n matches found.", numMatches() );
    00351     else
    00352         message = i18n("<qt>No matches found for '<b>%1</b>'.</qt>").arg(m_pattern);
    00353     KMessageBox::information(parentWidget(), message);
    00354 }
    00355
    00356 bool KFind::shouldRestart( bool forceAsking, bool /*showNumMatches*/ ) const
    00357 {
    00358     // Only ask if we did a "find from cursor", otherwise it's pointless.
    00359     // Well, unless the user can modify the document during a search operation,
    00360     // hence the force boolean.
    00361     if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 )
    00362     {
    00363         displayFinalDialog();
    00364         return false;
    00365     }
    00368     QString message;
    00369     if ( m_options & KFindDialog::FindBackwards )
    00370         message = i18n( "Beginning of document reached.\n"\
    00371                         "Continue from the end?" );
    00372     else
    00373         message = i18n( "End of document reached.\n"\
    00374                         "Continue from the beginning?" );
    00375
    00376     int ret = KMessageBox::questionYesNo( parentWidget(), QString("<qt>")+message+QString("</qt>") );
    00377     bool yes = ( ret == KMessageBox::Yes );
    00378     if ( yes )
    00379         const_cast<KFind*>(this)->m_options &= ~KFindDialog::FromCursor; // clear FromCursor option
    00380     return yes;
    00381 }
    00382
    00383 void KFind::setOptions( long options )
    00384 {
    00385     m_options = options;
    00386 }
    00387
    00388 void KFind::closeFindNextDialog()
    00389 {
    00390     delete m_dialog;
    00391     m_dialog = 0L;
    00392     m_dialogClosed = true;
    00393 }
    00394
    00395 #include "kfind.moc"

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:28:04 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001