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) );
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, TQ_SIGNAL( user1Clicked() ),
this, TQ_SLOT( slotFindNext() ) );
216 connect( m_dialog, TQ_SIGNAL( finished() ),
this, TQ_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);
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();
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;
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)
479 *matchedLength =
pattern.length();
480 if (isWholeWords(text,
index, *matchedLength))
488 while (
index < (
int)text.length())
496 *matchedLength =
pattern.length();
497 if (isWholeWords(text,
index, *matchedLength))
501 if (
index >= (
int)text.length())
518 *matchedLength =
pattern.length();
525 int KFind::find(
const TQString &text,
const TQRegExp &pattern,
int index,
long options,
int *matchedLength)
542 *matchedLength =
pattern.matchedLength();
543 if (isWholeWords(text,
index, *matchedLength))
551 while (
index < (
int)text.length())
561 *matchedLength =
pattern.matchedLength();
562 if (isWholeWords(text,
index, *matchedLength))
566 if (
index >= (
int)text.length())
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 );
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() );
@ FindIncremental
Find incremental.
@ FromCursor
Start from current cursor position.
@ WholeWordsOnly
Match whole words only.
@ CaseSensitive
Consider case when matching.
@ RegularExpression
Interpret the pattern as a regular expression.
@ FindBackwards
Go backwards.
A generic implementation of the "find" function.
virtual bool validateMatch(const TQString &text, int index, int matchedlength)
Virtual method, which allows applications to add extra checks for validating a candidate match.
KDialogBase * findNextDialog(bool create=false)
Return (or create) the dialog that shows the "find next?" prompt.
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.
@ NoMatch
No match was found.
@ Match
A match was found.
int numMatches() const
Return the number of matches found (i.e.
virtual ~KFind()
Destructor.
void closeFindNextDialog()
Close the "find next?" dialog.
void dialogClosed()
Emitted when the 'find next' dialog is being closed.
virtual bool shouldRestart(bool forceAsking=false, bool showNumMatches=true) const
Returns true if we should restart the search from scratch.
void setPattern(const TQString &pattern)
Change the pattern we're looking for.
virtual void setOptions(long options)
Set new options.
void highlight(const TQString &text, int matchingIndex, int matchedLength)
Connect to this signal to implement highlighting of found text during the find operation.
void setData(const TQString &data, int startPos=-1)
Call this when needData returns true, before calling find().
virtual void displayFinalDialog() const
Displays the final dialog saying "no match was found", if that was the case.
Result find()
Walk the text fragment (e.g.
long options() const
Return the current options.
static void information(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const TQString &dontShowAgainName=TQString::null, int options=Notify)
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)
kndbgstream & endl(kndbgstream &s)
kdbgstream kdDebug(int area=0)
const TDEShortcut & find()