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 <qstylesheet.h>
00029 #include <qguardedptr.h>
00030 #include <kdebug.h>
00031
00032
00033
00034 #define INDEX_NOMATCH -1
00035
00036 class KFindNextDialog : public KDialogBase
00037 {
00038 public:
00039 KFindNextDialog(const QString &pattern, QWidget *parent);
00040 };
00041
00042
00043 KFindNextDialog::KFindNextDialog(const QString &pattern, QWidget *parent) :
00044 KDialogBase(parent, 0, false,
00045 i18n("Find Next"),
00046 User1 | Close,
00047 User1,
00048 false,
00049 i18n("&Find"))
00050 {
00051 setMainWidget( new QLabel( i18n("<qt>Find next occurrence of '<b>%1</b>'?</qt>").arg(pattern), this ) );
00052 }
00053
00055
00056 struct KFind::Private {
00057 Private() {
00058 findDialog = 0;
00059 }
00060 QGuardedPtr<QWidget> findDialog;
00061 };
00063
00064 KFind::KFind( const QString &pattern, long options, QWidget *parent )
00065 : QObject( parent )
00066 {
00067 d = new KFind::Private;
00068 m_options = options;
00069 init( pattern );
00070 }
00071
00072 KFind::KFind( const QString &pattern, long options, QWidget *parent, QWidget *findDialog )
00073 : QObject( parent )
00074 {
00075 d = new KFind::Private;
00076 d->findDialog = findDialog;
00077 m_options = options;
00078 init( pattern );
00079 }
00080
00081 void KFind::init( const QString& pattern )
00082 {
00083 m_matches = 0;
00084 m_pattern = pattern;
00085 m_dialog = 0;
00086 m_dialogClosed = false;
00087 m_index = INDEX_NOMATCH;
00088 m_lastResult = NoMatch;
00089 if (m_options & KFindDialog::RegularExpression)
00090 m_regExp = new QRegExp(pattern, m_options & KFindDialog::CaseSensitive);
00091 else {
00092 m_regExp = 0;
00093 }
00094 }
00095
00096 KFind::~KFind()
00097 {
00098 delete m_dialog;
00099 delete d;
00100 }
00101
00102 bool KFind::needData() const
00103 {
00104
00105 if (m_options & KFindDialog::FindBackwards)
00106
00107
00108 return ( m_index < 0 && m_lastResult != Match );
00109 else
00110
00111
00112 return m_index == INDEX_NOMATCH;
00113 }
00114
00115 void KFind::setData( const QString& data, int startPos )
00116 {
00117 m_text = data;
00118 if ( startPos != -1 )
00119 m_index = startPos;
00120 else if (m_options & KFindDialog::FindBackwards)
00121 m_index = QMAX( (int)m_text.length() - 1, 0 );
00122 else
00123 m_index = 0;
00124 #ifdef DEBUG_FIND
00125 kdDebug() << "setData: '" << m_text << "' m_index=" << m_index << endl;
00126 #endif
00127 Q_ASSERT( m_index != INDEX_NOMATCH );
00128 m_lastResult = NoMatch;
00129 }
00130
00131 KDialogBase* KFind::findNextDialog( bool create )
00132 {
00133 if ( !m_dialog && create )
00134 {
00135 m_dialog = new KFindNextDialog( m_pattern, parentWidget() );
00136 connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotFindNext() ) );
00137 connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) );
00138 }
00139 return m_dialog;
00140 }
00141
00142 KFind::Result KFind::find()
00143 {
00144 Q_ASSERT( m_index != INDEX_NOMATCH );
00145 if ( m_lastResult == Match )
00146 {
00147
00148 if (m_options & KFindDialog::FindBackwards) {
00149 m_index--;
00150 if ( m_index == -1 )
00151 {
00152 m_lastResult = NoMatch;
00153 return NoMatch;
00154 }
00155 } else
00156 m_index++;
00157 }
00158
00159 #ifdef DEBUG_FIND
00160 kdDebug() << k_funcinfo << "m_index=" << m_index << endl;
00161 #endif
00162 do
00163 {
00164
00165 if ( m_options & KFindDialog::RegularExpression )
00166 m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength);
00167 else
00168 m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength);
00169 if ( m_index != -1 )
00170 {
00171
00172 if ( validateMatch( m_text, m_index, m_matchedLength ) )
00173 {
00174 m_matches++;
00175
00176
00177 emit highlight(m_text, m_index, m_matchedLength);
00178
00179 if ( !m_dialogClosed )
00180 findNextDialog(true)->show();
00181
00182 #ifdef DEBUG_FIND
00183 kdDebug() << k_funcinfo << "Match. Next m_index=" << m_index << endl;
00184 #endif
00185 m_lastResult = Match;
00186 return Match;
00187 }
00188 else
00189 if (m_options & KFindDialog::FindBackwards)
00190 m_index--;
00191 else
00192 m_index++;
00193 } else
00194 m_index = INDEX_NOMATCH;
00195 }
00196 while (m_index != INDEX_NOMATCH);
00197
00198 #ifdef DEBUG_FIND
00199 kdDebug() << k_funcinfo << "NoMatch. m_index=" << m_index << endl;
00200 #endif
00201 m_lastResult = NoMatch;
00202 return NoMatch;
00203 }
00204
00205
00206 int KFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
00207 {
00208
00209 if (options & KFindDialog::RegularExpression)
00210 {
00211 QRegExp regExp(pattern, options & KFindDialog::CaseSensitive);
00212
00213 return find(text, regExp, index, options, matchedLength);
00214 }
00215
00216 bool caseSensitive = (options & KFindDialog::CaseSensitive);
00217
00218 if (options & KFindDialog::WholeWordsOnly)
00219 {
00220 if (options & KFindDialog::FindBackwards)
00221 {
00222
00223 while (index >= 0)
00224 {
00225
00226 index = text.findRev(pattern, index, caseSensitive);
00227 if (index == -1)
00228 break;
00229
00230
00231 *matchedLength = pattern.length();
00232 if (isWholeWords(text, index, *matchedLength))
00233 break;
00234 index--;
00235 }
00236 }
00237 else
00238 {
00239
00240 while (index < (int)text.length())
00241 {
00242
00243 index = text.find(pattern, index, caseSensitive);
00244 if (index == -1)
00245 break;
00246
00247
00248 *matchedLength = pattern.length();
00249 if (isWholeWords(text, index, *matchedLength))
00250 break;
00251 index++;
00252 }
00253 if (index >= (int)text.length())
00254 index = -1;
00255 }
00256 }
00257 else
00258 {
00259
00260 if (options & KFindDialog::FindBackwards)
00261 {
00262 index = text.findRev(pattern, index, caseSensitive);
00263 }
00264 else
00265 {
00266 index = text.find(pattern, index, caseSensitive);
00267 }
00268 if (index != -1)
00269 {
00270 *matchedLength = pattern.length();
00271 }
00272 }
00273 return index;
00274 }
00275
00276
00277 int KFind::find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
00278 {
00279 if (options & KFindDialog::WholeWordsOnly)
00280 {
00281 if (options & KFindDialog::FindBackwards)
00282 {
00283
00284 while (index >= 0)
00285 {
00286
00287 index = text.findRev(pattern, index);
00288 if (index == -1)
00289 break;
00290
00291
00292
00293 pattern.search( text.mid(index) );
00294 *matchedLength = pattern.matchedLength();
00295 if (isWholeWords(text, index, *matchedLength))
00296 break;
00297 index--;
00298 }
00299 }
00300 else
00301 {
00302
00303 while (index < (int)text.length())
00304 {
00305
00306 index = text.find(pattern, index);
00307 if (index == -1)
00308 break;
00309
00310
00311
00312 pattern.search( text.mid(index) );
00313 *matchedLength = pattern.matchedLength();
00314 if (isWholeWords(text, index, *matchedLength))
00315 break;
00316 index++;
00317 }
00318 if (index >= (int)text.length())
00319 index = -1;
00320 }
00321 }
00322 else
00323 {
00324
00325 if (options & KFindDialog::FindBackwards)
00326 {
00327 index = text.findRev(pattern, index);
00328 }
00329 else
00330 {
00331 index = text.find(pattern, index);
00332 }
00333 if (index != -1)
00334 {
00335
00336 pattern.search( text.mid(index) );
00337 *matchedLength = pattern.matchedLength();
00338 }
00339 }
00340 return index;
00341 }
00342
00343 bool KFind::isInWord(QChar ch)
00344 {
00345 return ch.isLetter() || ch.isDigit() || ch == '_';
00346 }
00347
00348 bool KFind::isWholeWords(const QString &text, int starts, int matchedLength)
00349 {
00350 if ((starts == 0) || (!isInWord(text[starts - 1])))
00351 {
00352 int ends = starts + matchedLength;
00353
00354 if ((ends == (int)text.length()) || (!isInWord(text[ends])))
00355 return true;
00356 }
00357 return false;
00358 }
00359
00360 void KFind::slotFindNext()
00361 {
00362 emit findNext();
00363 }
00364
00365 void KFind::slotDialogClosed()
00366 {
00367 emit dialogClosed();
00368 m_dialogClosed = true;
00369 }
00370
00371 void KFind::displayFinalDialog() const
00372 {
00373 QString message;
00374 if ( numMatches() )
00375 message = i18n( "1 match found.", "%n matches found.", numMatches() );
00376 else
00377 message = i18n("<qt>No matches found for '<b>%1</b>'.</qt>").arg(QStyleSheet::escape(m_pattern));
00378 KMessageBox::information(dialogsParent(), message);
00379 }
00380
00381 bool KFind::shouldRestart( bool forceAsking, bool showNumMatches ) const
00382 {
00383
00384
00385
00386 if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 )
00387 {
00388 displayFinalDialog();
00389 return false;
00390 }
00391 QString message;
00392 if ( showNumMatches )
00393 {
00394 if ( numMatches() )
00395 message = i18n( "1 match found.", "%n matches found.", numMatches() );
00396 else
00397 message = i18n("No matches found for '<b>%1</b>'.").arg(QStyleSheet::escape(m_pattern));
00398 }
00399 else
00400 {
00401 if ( m_options & KFindDialog::FindBackwards )
00402 message = i18n( "Beginning of document reached." );
00403 else
00404 message = i18n( "End of document reached." );
00405 }
00406
00407 message += "\n";
00408
00409 message +=
00410 ( m_options & KFindDialog::FindBackwards ) ?
00411 i18n("Do you want to restart search from the end?")
00412 : i18n("Do you want to restart search at the beginning?");
00413
00414 int ret = KMessageBox::questionYesNo( dialogsParent(), QString("<qt>")+message+QString("</qt>") );
00415 bool yes = ( ret == KMessageBox::Yes );
00416 if ( yes )
00417 const_cast<KFind*>(this)->m_options &= ~KFindDialog::FromCursor;
00418 return yes;
00419 }
00420
00421 void KFind::setOptions( long options )
00422 {
00423 m_options = options;
00424 if (m_options & KFindDialog::RegularExpression)
00425 m_regExp = new QRegExp(m_pattern, m_options & KFindDialog::CaseSensitive);
00426 else {
00427 delete m_regExp;
00428 m_regExp = 0;
00429 }
00430 }
00431
00432 void KFind::closeFindNextDialog()
00433 {
00434 delete m_dialog;
00435 m_dialog = 0L;
00436 m_dialogClosed = true;
00437 }
00438
00439 int KFind::index() const
00440 {
00441 return m_index;
00442 }
00443
00444 void KFind::setPattern( const QString& pattern )
00445 {
00446 m_pattern = pattern;
00447 setOptions( options() );
00448 }
00449
00450 QWidget* KFind::dialogsParent() const
00451 {
00452
00453
00454
00455 return d->findDialog ? (QWidget*)d->findDialog : ( m_dialog ? m_dialog : parentWidget() );
00456 }
00457
00458 #include "kfind.moc"