00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00041 KFindNextDialog::KFindNextDialog(const QString &pattern, QWidget *parent) :
00042 KDialogBase(parent, 0, false,
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
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
00123 if (m_options & KFindDialog::FindBackwards) {
00124 m_index--;
00125 if ( m_index == -1 )
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
00138 {
00139
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
00147 if ( validateMatch( m_text, m_index, m_matchedLength ) )
00148 {
00149 m_matches++;
00150
00151
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
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
00181 int KFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
00182 {
00183
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
00198 while (index >= 0)
00199 {
00200
00201 index = text.findRev(pattern, index, caseSensitive);
00202 if (index == -1)
00203 break;
00204
00205
00206 *matchedLength = pattern.length();
00207 if (isWholeWords(text, index, *matchedLength))
00208 break;
00209 index--;
00210 }
00211 }
00212 else
00213 {
00214
00215 while (index < (int)text.length())
00216 {
00217
00218 index = text.find(pattern, index, caseSensitive);
00219 if (index == -1)
00220 break;
00221
00222
00223 *matchedLength = pattern.length();
00224 if (isWholeWords(text, index, *matchedLength))
00225 break;
00226 index++;
00227 }
00228 if (index >= (int)text.length())
00229 index = -1;
00230 }
00231 }
00232 else
00233 {
00234
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
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
00259 while (index >= 0)
00260 {
00261
00262 index = text.findRev(pattern, index);
00263 if (index == -1)
00264 break;
00265
00266
00267
00268 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
00278 while (index < (int)text.length())
00279 {
00280
00281 index = text.find(pattern, index);
00282 if (index == -1)
00283 break;
00284
00285
00286
00287 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())
00294 index = -1;
00295 }
00296 }
00297 else
00298 {
00299
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
00311 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 ) const
00357 {
00358
00359
00360
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;
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"