diff options
Diffstat (limited to 'kutils/kreplace.cpp')
-rw-r--r-- | kutils/kreplace.cpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/kutils/kreplace.cpp b/kutils/kreplace.cpp new file mode 100644 index 000000000..6740c297a --- /dev/null +++ b/kutils/kreplace.cpp @@ -0,0 +1,328 @@ +/* + Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. + Copyright (C) 2002, David Faure <david@mandrakesoft.com> + 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 <qlabel.h> +#include <kapplication.h> +#include <kdebug.h> + +#include <klocale.h> +#include <kmessagebox.h> +#include "kreplace.h" +#include "kreplacedialog.h" +#include <qregexp.h> + +//#define DEBUG_REPLACE +#define INDEX_NOMATCH -1 + +class KReplaceNextDialog : public KDialogBase +{ +public: + KReplaceNextDialog( QWidget *parent ); + void setLabel( const QString& pattern, const QString& replacement ); +private: + QLabel* m_mainLabel; +}; + +KReplaceNextDialog::KReplaceNextDialog(QWidget *parent) : + KDialogBase(parent, 0, false, // non-modal! + i18n("Replace"), + User3 | User2 | User1 | Close, + User3, + false, + i18n("&All"), i18n("&Skip"), i18n("Replace")) +{ + m_mainLabel = new QLabel( this ); + setMainWidget( m_mainLabel ); + resize(minimumSize()); +} + +void KReplaceNextDialog::setLabel( const QString& pattern, const QString& replacement ) +{ + m_mainLabel->setText( i18n("Replace '%1' with '%2'?").arg(pattern).arg(replacement) ); +} + +//// + +KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent) : + KFind( pattern, options, parent ) +{ + m_replacements = 0; + m_replacement = replacement; +} + +KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget *dlg) : + KFind( pattern, options, parent, dlg ) +{ + m_replacements = 0; + m_replacement = replacement; +} + +KReplace::~KReplace() +{ + // KFind::~KFind will delete m_dialog +} + +KDialogBase* KReplace::replaceNextDialog( bool create ) +{ + if ( m_dialog || create ) + return dialog(); + return 0L; +} + +KReplaceNextDialog* KReplace::dialog() +{ + if ( !m_dialog ) + { + m_dialog = new KReplaceNextDialog( parentWidget() ); + connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotReplaceAll() ) ); + connect( m_dialog, SIGNAL( user2Clicked() ), this, SLOT( slotSkip() ) ); + connect( m_dialog, SIGNAL( user3Clicked() ), this, SLOT( slotReplace() ) ); + connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) ); + } + return static_cast<KReplaceNextDialog *>(m_dialog); +} + +void KReplace::displayFinalDialog() const +{ + if ( !m_replacements ) + KMessageBox::information(parentWidget(), i18n("No text was replaced.")); + else + KMessageBox::information(parentWidget(), i18n("1 replacement done.", "%n replacements done.", m_replacements ) ); +} + +KFind::Result KReplace::replace() +{ +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "m_index=" << m_index << endl; +#endif + if ( m_index == INDEX_NOMATCH && m_lastResult == Match ) + { + m_lastResult = NoMatch; + return NoMatch; + } + + do // this loop is only because validateMatch can fail + { +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "beginning of loop: m_index=" << m_index << endl; +#endif + // Find the next match. + if ( m_options & KReplaceDialog::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); +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "KFind::find returned m_index=" << m_index << endl; +#endif + if ( m_index != -1 ) + { + // Flexibility: the app can add more rules to validate a possible match + if ( validateMatch( m_text, m_index, m_matchedLength ) ) + { + if ( m_options & KReplaceDialog::PromptOnReplace ) + { +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "PromptOnReplace" << endl; +#endif + // Display accurate initial string and replacement string, they can vary + QString matchedText = m_text.mid( m_index, m_matchedLength ); + QString rep = matchedText; + KReplace::replace(rep, m_replacement, 0, m_options, m_matchedLength); + dialog()->setLabel( matchedText, rep ); + dialog()->show(); + + // Tell the world about the match we found, in case someone wants to + // highlight it. + emit highlight(m_text, m_index, m_matchedLength); + + m_lastResult = Match; + return Match; + } + else + { + doReplace(); // this moves on too + } + } + else + { + // not validated -> move on + if (m_options & KFindDialog::FindBackwards) + m_index--; + else + m_index++; + } + } else + m_index = INDEX_NOMATCH; // will exit the loop + } + while (m_index != INDEX_NOMATCH); + + m_lastResult = NoMatch; + return NoMatch; +} + +int KReplace::replace(QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength) +{ + int matchedLength; + + index = KFind::find(text, pattern, index, options, &matchedLength); + if (index != -1) + { + *replacedLength = replace(text, replacement, index, options, matchedLength); + if (options & KReplaceDialog::FindBackwards) + index--; + else + index += *replacedLength; + } + return index; +} + +int KReplace::replace(QString &text, const QRegExp &pattern, const QString &replacement, int index, long options, int *replacedLength) +{ + int matchedLength; + + index = KFind::find(text, pattern, index, options, &matchedLength); + if (index != -1) + { + *replacedLength = replace(text, replacement, index, options, matchedLength); + if (options & KReplaceDialog::FindBackwards) + index--; + else + index += *replacedLength; + } + return index; +} + +int KReplace::replace(QString &text, const QString &replacement, int index, long options, int length) +{ + QString rep = replacement; + // Backreferences: replace \0 with the right portion of 'text' + if ( options & KReplaceDialog::BackReference ) + rep.replace( "\\0", text.mid( index, length ) ); + // Then replace rep into the text + text.replace(index, length, rep); + return rep.length(); +} + +void KReplace::slotReplaceAll() +{ + doReplace(); + m_options &= ~KReplaceDialog::PromptOnReplace; + emit optionsChanged(); + emit findNext(); +} + +void KReplace::slotSkip() +{ + if (m_options & KReplaceDialog::FindBackwards) + m_index--; + else + m_index++; + if ( m_dialogClosed ) { + delete m_dialog; // hide it again + m_dialog = 0L; + } else + emit findNext(); +} + +void KReplace::slotReplace() +{ + doReplace(); + if ( m_dialogClosed ) { + delete m_dialog; // hide it again + m_dialog = 0L; + } else + emit findNext(); +} + +void KReplace::doReplace() +{ + int replacedLength = KReplace::replace(m_text, m_replacement, m_index, m_options, m_matchedLength); + + // Tell the world about the replacement we made, in case someone wants to + // highlight it. + emit replace(m_text, m_index, replacedLength, m_matchedLength); +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "after replace() signal: m_index=" << m_index << " replacedLength=" << replacedLength << endl; +#endif + m_replacements++; + if (m_options & KReplaceDialog::FindBackwards) + m_index--; + else { + m_index += replacedLength; + // when replacing the empty pattern, move on. See also kjs/regexp.cpp for how this should be done for regexps. + if ( m_pattern.isEmpty() ) + ++m_index; + } +#ifdef DEBUG_REPLACE + kdDebug() << k_funcinfo << "after adjustement: m_index=" << m_index << endl; +#endif +} + +void KReplace::resetCounts() +{ + KFind::resetCounts(); + m_replacements = 0; +} + +bool KReplace::shouldRestart( bool forceAsking, bool showNumMatches ) const +{ + // Only ask if we did a "find from cursor", otherwise it's pointless. + // ... Or if the prompt-on-replace option was set. + // Well, unless the user can modify the document during a search operation, + // hence the force boolean. + if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 + && (m_options & KReplaceDialog::PromptOnReplace) == 0 ) + { + displayFinalDialog(); + return false; + } + QString message; + if ( showNumMatches ) + { + if ( !m_replacements ) + message = i18n("No text was replaced."); + else + message = i18n("1 replacement done.", "%n replacements done.", m_replacements ); + } + else + { + if ( m_options & KFindDialog::FindBackwards ) + message = i18n( "Beginning of document reached." ); + else + message = i18n( "End of document reached." ); + } + + message += "\n"; + // Hope this word puzzle is ok, it's a different sentence + message += + ( m_options & KFindDialog::FindBackwards ) ? + i18n("Do you want to restart search from the end?") + : i18n("Do you want to restart search at the beginning?"); + + int ret = KMessageBox::questionYesNo( parentWidget(), message, QString::null, i18n("Restart"), i18n("Stop") ); + return( ret == KMessageBox::Yes ); +} + +void KReplace::closeReplaceNextDialog() +{ + closeFindNextDialog(); +} + +#include "kreplace.moc" |