23 #include "kfinddialog.h"
24 #include <tdeapplication.h>
26 #include <tdemessagebox.h>
29 #include <tqstylesheet.h>
30 #include <tqguardedptr.h>
31 #include <tqptrvector.h>
36 #define INDEX_NOMATCH -1
41 KFindNextDialog(
const TQString &pattern, TQWidget *parent);
45 KFindNextDialog::KFindNextDialog(
const TQString &pattern, TQWidget *parent) :
53 setMainWidget(
new TQLabel( i18n(
"<qt>Find next occurrence of '<b>%1</b>'?</qt>").arg(pattern),
this ) );
62 patternChanged(false),
64 incrementalPath(29, true),
69 incrementalPath.setAutoDelete(
true);
70 data.setAutoDelete(
true);
81 Match(
int dataId,
int index,
int matchedLength) :
84 matchedLength(matchedLength)
94 Data() : id(-1), dirty(false) { }
95 Data(
int id,
const TQString &text,
bool dirty =
false) :
106 TQGuardedPtr<TQWidget> findDialog;
108 TQString matchedPattern;
109 TQDict<Match> incrementalPath;
111 TQPtrVector<Data> data;
118 KFind::KFind(
const TQString &pattern,
long options, TQWidget *parent )
121 d =
new KFind::Private;
126 KFind::KFind(
const TQString &pattern,
long options, TQWidget *parent, TQWidget *findDialog )
129 d =
new KFind::Private;
130 d->findDialog = findDialog;
135 void KFind::init(
const TQString& pattern )
140 m_dialogClosed =
false;
141 m_index = INDEX_NOMATCH;
162 return ( m_index < 0 && m_lastResult !=
Match );
166 return m_index == INDEX_NOMATCH;
182 id = d->currentId + 1;
184 if (
id >= (
int) d->data.size() )
185 d->data.resize(
id + 100 );
187 d->data.insert(
id,
new Private::Data(
id, data,
true) );
190 if ( !(m_options & KFindDialog::FindIncremental) ||
needData() )
194 if ( startPos != -1 )
197 m_index = m_text.length();
201 kdDebug() <<
"setData: '" << m_text <<
"' m_index=" << m_index <<
endl;
203 Q_ASSERT( m_index != INDEX_NOMATCH );
212 if ( !m_dialog && create )
214 m_dialog =
new KFindNextDialog( m_pattern, parentWidget() );
215 connect( m_dialog, TQT_SIGNAL( user1Clicked() ),
this, TQT_SLOT( slotFindNext() ) );
216 connect( m_dialog, TQT_SIGNAL( finished() ),
this, TQT_SLOT( slotDialogClosed() ) );
223 Q_ASSERT( m_index != INDEX_NOMATCH || d->patternChanged );
225 if ( m_lastResult ==
Match && !d->patternChanged )
238 d->patternChanged =
false;
244 if ( m_pattern.length() < d->matchedPattern.length() )
246 Private::Match *match = m_pattern.isEmpty() ? d->emptyMatch : d->incrementalPath[m_pattern];
247 TQString previousPattern = d->matchedPattern;
248 d->matchedPattern = m_pattern;
254 while ( d->data[match->dataId]->dirty ==
true &&
255 !m_pattern.isEmpty() )
257 m_pattern.truncate( m_pattern.length() - 1 );
259 match = d->incrementalPath[m_pattern];
265 while ( m_pattern.length() < previousPattern.length() )
267 d->incrementalPath.remove(previousPattern);
268 previousPattern.truncate(previousPattern.length() - 1);
272 m_text = d->data[match->dataId]->text;
273 m_index = match->index;
274 m_matchedLength = match->matchedLength;
275 d->currentId = match->dataId;
281 emit
highlight(d->currentId, m_index, m_matchedLength);
283 emit
highlight(m_text, m_index, m_matchedLength);
285 m_lastResult =
Match;
286 d->matchedPattern = m_pattern;
294 startNewIncrementalSearch();
299 else if ( m_pattern.length() > d->matchedPattern.length() )
302 if ( m_pattern.startsWith(d->matchedPattern) )
306 if ( m_index == INDEX_NOMATCH )
309 TQString temp = m_pattern;
310 m_pattern.truncate(d->matchedPattern.length() + 1);
311 d->matchedPattern = temp;
316 startNewIncrementalSearch();
321 else if ( m_pattern != d->matchedPattern )
323 startNewIncrementalSearch();
328 kdDebug() << k_funcinfo <<
"m_index=" << m_index <<
endl;
338 m_index =
KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength);
340 m_index =
KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength);
342 if ( m_options & KFindDialog::FindIncremental )
343 d->data[d->currentId]->dirty =
false;
345 if ( m_index == -1 && d->currentId < (
int) d->data.count() - 1 )
347 m_text = d->data[++d->currentId]->text;
350 m_index = m_text.length();
365 if ( m_options & KFindDialog::FindIncremental )
367 if ( m_pattern.isEmpty() ) {
368 delete d->emptyMatch;
369 d->emptyMatch =
new Private::Match( d->currentId, m_index, m_matchedLength );
371 d->incrementalPath.replace(m_pattern,
new Private::Match(d->currentId, m_index, m_matchedLength));
373 if ( m_pattern.length() < d->matchedPattern.length() )
375 m_pattern += d->matchedPattern.mid(m_pattern.length(), 1);
386 emit
highlight(d->currentId, m_index, m_matchedLength);
388 emit
highlight(m_text, m_index, m_matchedLength);
390 if ( !m_dialogClosed )
394 kdDebug() << k_funcinfo <<
"Match. Next m_index=" << m_index <<
endl;
396 m_lastResult =
Match;
410 if ( m_options & KFindDialog::FindIncremental )
412 TQString temp = m_pattern;
413 temp.truncate(temp.length() - 1);
414 m_pattern = d->matchedPattern;
415 d->matchedPattern = temp;
418 m_index = INDEX_NOMATCH;
421 while (m_index != INDEX_NOMATCH);
424 kdDebug() << k_funcinfo <<
"NoMatch. m_index=" << m_index <<
endl;
430 void KFind::startNewIncrementalSearch()
432 Private::Match *match = d->emptyMatch;
435 m_text = TQString::null;
441 m_text = d->data[match->dataId]->text;
442 m_index = match->index;
443 d->currentId = match->dataId;
446 d->incrementalPath.clear();
447 delete d->emptyMatch;
449 d->matchedPattern = m_pattern;
450 m_pattern = TQString::null;
454 int KFind::find(
const TQString &text,
const TQString &pattern,
int index,
long options,
int *matchedLength)
461 return find(text, regExp, index, options, matchedLength);
474 index = text.findRev(pattern, index, caseSensitive);
479 *matchedLength = pattern.length();
480 if (isWholeWords(text, index, *matchedLength))
488 while (index < (
int)text.length())
491 index = text.find(pattern, index, caseSensitive);
496 *matchedLength = pattern.length();
497 if (isWholeWords(text, index, *matchedLength))
501 if (index >= (
int)text.length())
510 index = text.findRev(pattern, index, caseSensitive);
514 index = text.find(pattern, index, caseSensitive);
518 *matchedLength = pattern.length();
525 int KFind::find(
const TQString &text,
const TQRegExp &pattern,
int index,
long options,
int *matchedLength)
535 index = text.findRev(pattern, index);
541 pattern.search( text.mid(index) );
542 *matchedLength = pattern.matchedLength();
543 if (isWholeWords(text, index, *matchedLength))
551 while (index < (
int)text.length())
554 index = text.find(pattern, index);
560 pattern.search( text.mid(index) );
561 *matchedLength = pattern.matchedLength();
562 if (isWholeWords(text, index, *matchedLength))
566 if (index >= (
int)text.length())
575 index = text.findRev(pattern, index);
579 index = text.find(pattern, index);
584 pattern.search( text.mid(index) );
585 *matchedLength = pattern.matchedLength();
591 bool KFind::isInWord(TQChar ch)
593 return ch.isLetter() || ch.isDigit() || ch ==
'_';
596 bool KFind::isWholeWords(
const TQString &text,
int starts,
int matchedLength)
598 if ((starts == 0) || (!isInWord(text[starts - 1])))
600 int ends = starts + matchedLength;
602 if ((ends == (
int)text.length()) || (!isInWord(text[ends])))
608 void KFind::slotFindNext()
613 void KFind::slotDialogClosed()
616 m_dialogClosed =
true;
623 message = i18n(
"1 match found.",
"%n matches found.",
numMatches() );
625 message = i18n(
"<qt>No matches found for '<b>%1</b>'.</qt>").arg(TQStyleSheet::escape(m_pattern));
640 if ( showNumMatches )
643 message = i18n(
"1 match found.",
"%n matches found.",
numMatches() );
645 message = i18n(
"No matches found for '<b>%1</b>'.").arg(TQStyleSheet::escape(m_pattern));
650 message = i18n(
"Beginning of document reached." );
652 message = i18n(
"End of document reached." );
655 message +=
"<br><br>";
659 i18n(
"Continue from the end?")
660 : i18n(
"Continue from the beginning?");
664 bool yes = ( ret == KMessageBox::Yes );
666 const_cast<KFind*
>(
this)->m_options &= ~KFindDialog::FromCursor;
685 m_dialogClosed =
true;
696 d->patternChanged =
true;
702 TQWidget* KFind::dialogsParent()
const
707 return d->findDialog ? (TQWidget*)d->findDialog : ( m_dialog ? m_dialog : parentWidget() );
virtual ~KFind()
Destructor.
static int questionYesNo(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const KGuiItem &buttonYes=KStdGuiItem::yes(), const KGuiItem &buttonNo=KStdGuiItem::no(), const TQString &dontAskAgainName=TQString::null, int options=Notify)
Result find()
Walk the text fragment (e.g.
virtual void setOptions(long options)
Set new options.
kdbgstream kdDebug(int area=0)
virtual bool validateMatch(const TQString &text, int index, int matchedlength)
Virtual method, which allows applications to add extra checks for validating a candidate match...
Interpret the pattern as a regular expression.
void highlight(const TQString &text, int matchingIndex, int matchedLength)
Connect to this signal to implement highlighting of found text during the find operation.
void setPattern(const TQString &pattern)
Change the pattern we're looking for.
virtual void displayFinalDialog() const
Displays the final dialog saying "no match was found", if that was the case.
static void information(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const TQString &dontShowAgainName=TQString::null, int options=Notify)
int numMatches() const
Return the number of matches found (i.e.
KDialogBase * findNextDialog(bool create=false)
Return (or create) the dialog that shows the "find next?" prompt.
const TDEShortcut & find()
A generic implementation of the "find" function.
virtual bool shouldRestart(bool forceAsking=false, bool showNumMatches=true) const
Returns true if we should restart the search from scratch.
void closeFindNextDialog()
Close the "find next?" dialog.
Consider case when matching.
KFind(const TQString &pattern, long options, TQWidget *parent)
Only use this constructor if you don't use KFindDialog, or if you use it as a modal dialog...
Start from current cursor position.
long options() const
Return the current options.
kndbgstream & endl(kndbgstream &s)
void dialogClosed()
Emitted when the 'find next' dialog is being closed.
void setData(const TQString &data, int startPos=-1)
Call this when needData returns true, before calling find().