diff options
Diffstat (limited to 'kutils/kfind.cpp')
-rw-r--r-- | kutils/kfind.cpp | 710 |
1 files changed, 0 insertions, 710 deletions
diff --git a/kutils/kfind.cpp b/kutils/kfind.cpp deleted file mode 100644 index 7b2da6899..000000000 --- a/kutils/kfind.cpp +++ /dev/null @@ -1,710 +0,0 @@ -/* - Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. - Copyright (C) 2002, David Faure <david@mandrakesoft.com> - Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl> - This file is part of the KDE project - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License version 2, as published by the Free Software Foundation. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "kfind.h" -#include "kfinddialog.h" -#include <kapplication.h> -#include <klocale.h> -#include <kmessagebox.h> -#include <tqlabel.h> -#include <tqregexp.h> -#include <tqstylesheet.h> -#include <tqguardedptr.h> -#include <tqptrvector.h> -#include <kdebug.h> - -//#define DEBUG_FIND - -#define INDEX_NOMATCH -1 - -class KFindNextDialog : public KDialogBase -{ -public: - KFindNextDialog(const TQString &pattern, TQWidget *parent); -}; - -// Create the dialog. -KFindNextDialog::KFindNextDialog(const TQString &pattern, TQWidget *parent) : - KDialogBase(parent, 0, false, // non-modal! - i18n("Find Next"), - User1 | Close, - User1, - false, - KStdGuiItem::find()) -{ - setMainWidget( new TQLabel( i18n("<qt>Find next occurrence of '<b>%1</b>'?</qt>").arg(pattern), this ) ); -} - -//// - -struct KFind::Private -{ - Private() : - findDialog(0), - patternChanged(false), - matchedPattern(""), - incrementalPath(29, true), - emptyMatch(0), - currentId(0), - customIds(false) - { - incrementalPath.setAutoDelete(true); - data.setAutoDelete(true); - } - - ~Private() - { - delete emptyMatch; - emptyMatch = 0; - } - - struct Match - { - Match(int dataId, int index, int matchedLength) : - dataId(dataId), - index(index), - matchedLength(matchedLength) - { } - - int dataId; - int index; - int matchedLength; - }; - - struct Data - { - Data() : id(-1), dirty(false) { } - Data(int id, const TQString &text, bool dirty = false) : - id(id), - text(text), - dirty(dirty) - { } - - int id; - TQString text; - bool dirty; - }; - - TQGuardedPtr<TQWidget> findDialog; - bool patternChanged; - TQString matchedPattern; - TQDict<Match> incrementalPath; - Match * emptyMatch; - TQPtrVector<Data> data; - int currentId; - bool customIds; -}; - -//// - -KFind::KFind( const TQString &pattern, long options, TQWidget *parent ) - : TQObject( parent ) -{ - d = new KFind::Private; - m_options = options; - init( pattern ); -} - -KFind::KFind( const TQString &pattern, long options, TQWidget *parent, TQWidget *findDialog ) - : TQObject( parent ) -{ - d = new KFind::Private; - d->findDialog = findDialog; - m_options = options; - init( pattern ); -} - -void KFind::init( const TQString& pattern ) -{ - m_matches = 0; - m_pattern = pattern; - m_dialog = 0; - m_dialogClosed = false; - m_index = INDEX_NOMATCH; - m_lastResult = NoMatch; - if (m_options & KFindDialog::RegularExpression) - m_regExp = new TQRegExp(pattern, m_options & KFindDialog::CaseSensitive); - else { - m_regExp = 0; - } -} - -KFind::~KFind() -{ - delete m_dialog; - delete d; -} - -bool KFind::needData() const -{ - // always true when m_text is empty. - if (m_options & KFindDialog::FindBackwards) - // m_index==-1 and m_lastResult==Match means we haven't answered nomatch yet - // This is important in the "replace with a prompt" case. - return ( m_index < 0 && m_lastResult != Match ); - else - // "index over length" test removed: we want to get a nomatch before we set data again - // This is important in the "replace with a prompt" case. - return m_index == INDEX_NOMATCH; -} - -void KFind::setData( const TQString& data, int startPos ) -{ - setData( -1, data, startPos ); -} - -void KFind::setData( int id, const TQString& data, int startPos ) -{ - // cache the data for incremental find - if ( m_options & KFindDialog::FindIncremental ) - { - if ( id != -1 ) - d->customIds = true; - else - id = d->currentId + 1; - - if ( id >= (int) d->data.size() ) - d->data.resize( id + 100 ); - - d->data.insert( id, new Private::Data(id, data, true) ); - } - - if ( !(m_options & KFindDialog::FindIncremental) || needData() ) - { - m_text = data; - - if ( startPos != -1 ) - m_index = startPos; - else if (m_options & KFindDialog::FindBackwards) - m_index = m_text.length(); - else - m_index = 0; -#ifdef DEBUG_FIND - kdDebug() << "setData: '" << m_text << "' m_index=" << m_index << endl; -#endif - Q_ASSERT( m_index != INDEX_NOMATCH ); - m_lastResult = NoMatch; - - d->currentId = id; - } -} - -KDialogBase* KFind::findNextDialog( bool create ) -{ - if ( !m_dialog && create ) - { - m_dialog = new KFindNextDialog( m_pattern, parentWidget() ); - connect( m_dialog, TQT_SIGNAL( user1Clicked() ), this, TQT_SLOT( slotFindNext() ) ); - connect( m_dialog, TQT_SIGNAL( finished() ), this, TQT_SLOT( slotDialogClosed() ) ); - } - return m_dialog; -} - -KFind::Result KFind::find() -{ - Q_ASSERT( m_index != INDEX_NOMATCH || d->patternChanged ); - - if ( m_lastResult == Match && !d->patternChanged ) - { - // Move on before looking for the next match, _if_ we just found a match - if (m_options & KFindDialog::FindBackwards) { - m_index--; - if ( m_index == -1 ) // don't call KFind::find with -1, it has a special meaning - { - m_lastResult = NoMatch; - return NoMatch; - } - } else - m_index++; - } - d->patternChanged = false; - - if ( m_options & KFindDialog::FindIncremental ) - { - // if the current pattern is shorter than the matchedPattern we can - // probably look up the match in the incrementalPath - if ( m_pattern.length() < d->matchedPattern.length() ) - { - Private::Match *match = m_pattern.isEmpty() ? d->emptyMatch : d->incrementalPath[m_pattern]; - TQString previousPattern = d->matchedPattern; - d->matchedPattern = m_pattern; - if ( match != 0 ) - { - bool clean = true; - - // find the first result backwards on the path that isn't dirty - while ( d->data[match->dataId]->dirty == true && - !m_pattern.isEmpty() ) - { - m_pattern.truncate( m_pattern.length() - 1 ); - - match = d->incrementalPath[m_pattern]; - - clean = false; - } - - // remove all matches that lie after the current match - while ( m_pattern.length() < previousPattern.length() ) - { - d->incrementalPath.remove(previousPattern); - previousPattern.truncate(previousPattern.length() - 1); - } - - // set the current text, index, etc. to the found match - m_text = d->data[match->dataId]->text; - m_index = match->index; - m_matchedLength = match->matchedLength; - d->currentId = match->dataId; - - // if the result is clean we can return it now - if ( clean ) - { - if ( d->customIds ) - emit highlight(d->currentId, m_index, m_matchedLength); - else - emit highlight(m_text, m_index, m_matchedLength); - - m_lastResult = Match; - d->matchedPattern = m_pattern; - return Match; - } - } - // if we couldn't look up the match, the new pattern isn't a - // substring of the matchedPattern, so we start a new search - else - { - startNewIncrementalSearch(); - } - } - // if the new pattern is longer than the matchedPattern we might be - // able to proceed from the last search - else if ( m_pattern.length() > d->matchedPattern.length() ) - { - // continue from the previous pattern - if ( m_pattern.startsWith(d->matchedPattern) ) - { - // we can't proceed from the previous position if the previous - // position already failed - if ( m_index == INDEX_NOMATCH ) - return NoMatch; - - TQString temp = m_pattern; - m_pattern.truncate(d->matchedPattern.length() + 1); - d->matchedPattern = temp; - } - // start a new search - else - { - startNewIncrementalSearch(); - } - } - // if the new pattern is as long as the matchedPattern, we reset if - // they are not equal - else if ( m_pattern != d->matchedPattern ) - { - startNewIncrementalSearch(); - } - } - -#ifdef DEBUG_FIND - kdDebug() << k_funcinfo << "m_index=" << m_index << endl; -#endif - do - { - // if we have multiple data blocks in our cache, walk through these - // blocks till we either searched all blocks or we find a match - do - { - // Find the next candidate match. - if ( m_options & KFindDialog::RegularExpression ) - m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength); - else - m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength); - - if ( m_options & KFindDialog::FindIncremental ) - d->data[d->currentId]->dirty = false; - - if ( m_index == -1 && d->currentId < (int) d->data.count() - 1 ) - { - m_text = d->data[++d->currentId]->text; - - if ( m_options & KFindDialog::FindBackwards ) - m_index = m_text.length(); - else - m_index = 0; - } - else - break; - } while ( !(m_options & KFindDialog::RegularExpression) ); - - if ( m_index != -1 ) - { - // Flexibility: the app can add more rules to validate a possible match - if ( validateMatch( m_text, m_index, m_matchedLength ) ) - { - bool done = true; - - if ( m_options & KFindDialog::FindIncremental ) - { - if ( m_pattern.isEmpty() ) { - delete d->emptyMatch; - d->emptyMatch = new Private::Match( d->currentId, m_index, m_matchedLength ); - } else - d->incrementalPath.replace(m_pattern, new Private::Match(d->currentId, m_index, m_matchedLength)); - - if ( m_pattern.length() < d->matchedPattern.length() ) - { - m_pattern += d->matchedPattern.mid(m_pattern.length(), 1); - done = false; - } - } - - if ( done ) - { - m_matches++; - // Tell the world about the match we found, in case someone wants to - // highlight it. - if ( d->customIds ) - emit highlight(d->currentId, m_index, m_matchedLength); - else - emit highlight(m_text, m_index, m_matchedLength); - - if ( !m_dialogClosed ) - findNextDialog(true)->show(); - -#ifdef DEBUG_FIND - kdDebug() << k_funcinfo << "Match. Next m_index=" << m_index << endl; -#endif - m_lastResult = Match; - return Match; - } - } - else // Skip match - { - if (m_options & KFindDialog::FindBackwards) - m_index--; - else - m_index++; - } - } - else - { - if ( m_options & KFindDialog::FindIncremental ) - { - TQString temp = m_pattern; - temp.truncate(temp.length() - 1); - m_pattern = d->matchedPattern; - d->matchedPattern = temp; - } - - m_index = INDEX_NOMATCH; - } - } - while (m_index != INDEX_NOMATCH); - -#ifdef DEBUG_FIND - kdDebug() << k_funcinfo << "NoMatch. m_index=" << m_index << endl; -#endif - m_lastResult = NoMatch; - return NoMatch; -} - -void KFind::startNewIncrementalSearch() -{ - Private::Match *match = d->emptyMatch; - if(match == 0) - { - m_text = TQString::null; - m_index = 0; - d->currentId = 0; - } - else - { - m_text = d->data[match->dataId]->text; - m_index = match->index; - d->currentId = match->dataId; - } - m_matchedLength = 0; - d->incrementalPath.clear(); - delete d->emptyMatch; - d->emptyMatch = 0; - d->matchedPattern = m_pattern; - m_pattern = TQString::null; -} - -// static -int KFind::find(const TQString &text, const TQString &pattern, int index, long options, int *matchedLength) -{ - // Handle regular expressions in the appropriate way. - if (options & KFindDialog::RegularExpression) - { - TQRegExp regExp(pattern, options & KFindDialog::CaseSensitive); - - return find(text, regExp, index, options, matchedLength); - } - - bool caseSensitive = (options & KFindDialog::CaseSensitive); - - if (options & KFindDialog::WholeWordsOnly) - { - if (options & KFindDialog::FindBackwards) - { - // Backward search, until the beginning of the line... - while (index >= 0) - { - // ...find the next match. - index = text.findRev(pattern, index, caseSensitive); - if (index == -1) - break; - - // Is the match delimited correctly? - *matchedLength = pattern.length(); - if (isWholeWords(text, index, *matchedLength)) - break; - index--; - } - } - else - { - // Forward search, until the end of the line... - while (index < (int)text.length()) - { - // ...find the next match. - index = text.find(pattern, index, caseSensitive); - if (index == -1) - break; - - // Is the match delimited correctly? - *matchedLength = pattern.length(); - if (isWholeWords(text, index, *matchedLength)) - break; - index++; - } - if (index >= (int)text.length()) // end of line - index = -1; // not found - } - } - else - { - // Non-whole-word search. - if (options & KFindDialog::FindBackwards) - { - index = text.findRev(pattern, index, caseSensitive); - } - else - { - index = text.find(pattern, index, caseSensitive); - } - if (index != -1) - { - *matchedLength = pattern.length(); - } - } - return index; -} - -// static -int KFind::find(const TQString &text, const TQRegExp &pattern, int index, long options, int *matchedLength) -{ - if (options & KFindDialog::WholeWordsOnly) - { - if (options & KFindDialog::FindBackwards) - { - // Backward search, until the beginning of the line... - while (index >= 0) - { - // ...find the next match. - index = text.findRev(pattern, index); - if (index == -1) - break; - - // Is the match delimited correctly? - //pattern.match(text, index, matchedLength, false); - /*int pos =*/ pattern.search( text.mid(index) ); - *matchedLength = pattern.matchedLength(); - if (isWholeWords(text, index, *matchedLength)) - break; - index--; - } - } - else - { - // Forward search, until the end of the line... - while (index < (int)text.length()) - { - // ...find the next match. - index = text.find(pattern, index); - if (index == -1) - break; - - // Is the match delimited correctly? - //pattern.match(text, index, matchedLength, false); - /*int pos =*/ pattern.search( text.mid(index) ); - *matchedLength = pattern.matchedLength(); - if (isWholeWords(text, index, *matchedLength)) - break; - index++; - } - if (index >= (int)text.length()) // end of line - index = -1; // not found - } - } - else - { - // Non-whole-word search. - if (options & KFindDialog::FindBackwards) - { - index = text.findRev(pattern, index); - } - else - { - index = text.find(pattern, index); - } - if (index != -1) - { - //pattern.match(text, index, matchedLength, false); - /*int pos =*/ pattern.search( text.mid(index) ); - *matchedLength = pattern.matchedLength(); - } - } - return index; -} - -bool KFind::isInWord(TQChar ch) -{ - return ch.isLetter() || ch.isDigit() || ch == '_'; -} - -bool KFind::isWholeWords(const TQString &text, int starts, int matchedLength) -{ - if ((starts == 0) || (!isInWord(text[starts - 1]))) - { - int ends = starts + matchedLength; - - if ((ends == (int)text.length()) || (!isInWord(text[ends]))) - return true; - } - return false; -} - -void KFind::slotFindNext() -{ - emit findNext(); -} - -void KFind::slotDialogClosed() -{ - emit dialogClosed(); - m_dialogClosed = true; -} - -void KFind::displayFinalDialog() const -{ - TQString message; - if ( numMatches() ) - message = i18n( "1 match found.", "%n matches found.", numMatches() ); - else - message = i18n("<qt>No matches found for '<b>%1</b>'.</qt>").arg(TQStyleSheet::escape(m_pattern)); - KMessageBox::information(dialogsParent(), message); -} - -bool KFind::shouldRestart( bool forceAsking, bool showNumMatches ) const -{ - // Only ask if we did a "find from cursor", otherwise it's pointless. - // Well, unless the user can modify the document during a search operation, - // hence the force boolean. - if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 ) - { - displayFinalDialog(); - return false; - } - TQString message; - if ( showNumMatches ) - { - if ( numMatches() ) - message = i18n( "1 match found.", "%n matches found.", numMatches() ); - else - message = i18n("No matches found for '<b>%1</b>'.").arg(TQStyleSheet::escape(m_pattern)); - } - else - { - if ( m_options & KFindDialog::FindBackwards ) - message = i18n( "Beginning of document reached." ); - else - message = i18n( "End of document reached." ); - } - - message += "<br><br>"; // can't be in the i18n() of the first if() because of the plural form. - // Hope this word puzzle is ok, it's a different sentence - message += - ( m_options & KFindDialog::FindBackwards ) ? - i18n("Continue from the end?") - : i18n("Continue from the beginning?"); - - int ret = KMessageBox::questionYesNo( dialogsParent(), TQString("<qt>")+message+TQString("</qt>"), - TQString::null, KStdGuiItem::cont(), KStdGuiItem::stop() ); - bool yes = ( ret == KMessageBox::Yes ); - if ( yes ) - const_cast<KFind*>(this)->m_options &= ~KFindDialog::FromCursor; // clear FromCursor option - return yes; -} - -void KFind::setOptions( long options ) -{ - m_options = options; - - delete m_regExp; - if (m_options & KFindDialog::RegularExpression) - m_regExp = new TQRegExp(m_pattern, m_options & KFindDialog::CaseSensitive); - else - m_regExp = 0; -} - -void KFind::closeFindNextDialog() -{ - delete m_dialog; - m_dialog = 0L; - m_dialogClosed = true; -} - -int KFind::index() const -{ - return m_index; -} - -void KFind::setPattern( const TQString& pattern ) -{ - if ( m_options & KFindDialog::FindIncremental && m_pattern != pattern ) - d->patternChanged = true; - - m_pattern = pattern; - setOptions( options() ); // rebuild m_regExp if necessary -} - -TQWidget* KFind::dialogsParent() const -{ - // If the find dialog is still up, it should get the focus when closing a message box - // Otherwise, maybe the "find next?" dialog is up - // Otherwise, the "view" is the parent. - return d->findDialog ? (TQWidget*)d->findDialog : ( m_dialog ? m_dialog : parentWidget() ); -} - -#include "kfind.moc" |