kreplace.cpp
00001 /* 00002 Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. 00003 Copyright (C) 2002, David Faure <david@mandrakesoft.com> 00004 This file is part of the KDE project 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License version 2, as published by the Free Software Foundation. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <qlabel.h> 00022 #include <kapplication.h> 00023 #include <kdebug.h> 00024 00025 #include <klocale.h> 00026 #include <kmessagebox.h> 00027 #include "kreplace.h" 00028 #include "kreplacedialog.h" 00029 #include <qregexp.h> 00030 00031 //#define DEBUG_REPLACE 00032 #define INDEX_NOMATCH -1 00033 00034 class KReplaceNextDialog : public KDialogBase 00035 { 00036 public: 00037 KReplaceNextDialog( QWidget *parent ); 00038 void setLabel( const QString& pattern, const QString& replacement ); 00039 private: 00040 QLabel* m_mainLabel; 00041 }; 00042 00043 KReplaceNextDialog::KReplaceNextDialog(QWidget *parent) : 00044 KDialogBase(parent, 0, false, // non-modal! 00045 i18n("Replace"), 00046 User3 | User2 | User1 | Close, 00047 User3, 00048 false, 00049 i18n("&All"), i18n("&Skip"), i18n("Replace")) 00050 { 00051 m_mainLabel = new QLabel( this ); 00052 setMainWidget( m_mainLabel ); 00053 resize(minimumSize()); 00054 } 00055 00056 void KReplaceNextDialog::setLabel( const QString& pattern, const QString& replacement ) 00057 { 00058 m_mainLabel->setText( i18n("Replace '%1' with '%2'?").arg(pattern).arg(replacement) ); 00059 } 00060 00062 00063 KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent) : 00064 KFind( pattern, options, parent ) 00065 { 00066 m_replacements = 0; 00067 m_replacement = replacement; 00068 } 00069 00070 KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget *dlg) : 00071 KFind( pattern, options, parent, dlg ) 00072 { 00073 m_replacements = 0; 00074 m_replacement = replacement; 00075 } 00076 00077 KReplace::~KReplace() 00078 { 00079 // KFind::~KFind will delete m_dialog 00080 } 00081 00082 KDialogBase* KReplace::replaceNextDialog( bool create ) 00083 { 00084 if ( m_dialog || create ) 00085 return dialog(); 00086 return 0L; 00087 } 00088 00089 KReplaceNextDialog* KReplace::dialog() 00090 { 00091 if ( !m_dialog ) 00092 { 00093 m_dialog = new KReplaceNextDialog( parentWidget() ); 00094 connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotReplaceAll() ) ); 00095 connect( m_dialog, SIGNAL( user2Clicked() ), this, SLOT( slotSkip() ) ); 00096 connect( m_dialog, SIGNAL( user3Clicked() ), this, SLOT( slotReplace() ) ); 00097 connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) ); 00098 } 00099 return static_cast<KReplaceNextDialog *>(m_dialog); 00100 } 00101 00102 void KReplace::displayFinalDialog() const 00103 { 00104 if ( !m_replacements ) 00105 KMessageBox::information(parentWidget(), i18n("No text was replaced.")); 00106 else 00107 KMessageBox::information(parentWidget(), i18n("1 replacement done.", "%n replacements done.", m_replacements ) ); 00108 } 00109 00110 KFind::Result KReplace::replace() 00111 { 00112 #ifdef DEBUG_REPLACE 00113 kdDebug() << k_funcinfo << "m_index=" << m_index << endl; 00114 #endif 00115 if ( m_index == INDEX_NOMATCH && m_lastResult == Match ) 00116 { 00117 m_lastResult = NoMatch; 00118 return NoMatch; 00119 } 00120 00121 do // this loop is only because validateMatch can fail 00122 { 00123 #ifdef DEBUG_REPLACE 00124 kdDebug() << k_funcinfo << "beginning of loop: m_index=" << m_index << endl; 00125 #endif 00126 // Find the next match. 00127 if ( m_options & KReplaceDialog::RegularExpression ) 00128 m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength); 00129 else 00130 m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength); 00131 #ifdef DEBUG_REPLACE 00132 kdDebug() << k_funcinfo << "KFind::find returned m_index=" << m_index << endl; 00133 #endif 00134 if ( m_index != -1 ) 00135 { 00136 // Flexibility: the app can add more rules to validate a possible match 00137 if ( validateMatch( m_text, m_index, m_matchedLength ) ) 00138 { 00139 if ( m_options & KReplaceDialog::PromptOnReplace ) 00140 { 00141 #ifdef DEBUG_REPLACE 00142 kdDebug() << k_funcinfo << "PromptOnReplace" << endl; 00143 #endif 00144 // Display accurate initial string and replacement string, they can vary 00145 QString matchedText = m_text.mid( m_index, m_matchedLength ); 00146 QString rep = matchedText; 00147 KReplace::replace(rep, m_replacement, 0, m_options, m_matchedLength); 00148 dialog()->setLabel( matchedText, rep ); 00149 dialog()->show(); 00150 00151 // Tell the world about the match we found, in case someone wants to 00152 // highlight it. 00153 emit highlight(m_text, m_index, m_matchedLength); 00154 00155 m_lastResult = Match; 00156 return Match; 00157 } 00158 else 00159 { 00160 doReplace(); // this moves on too 00161 } 00162 } 00163 else 00164 { 00165 // not validated -> move on 00166 if (m_options & KFindDialog::FindBackwards) 00167 m_index--; 00168 else 00169 m_index++; 00170 } 00171 } else 00172 m_index = INDEX_NOMATCH; // will exit the loop 00173 } 00174 while (m_index != INDEX_NOMATCH); 00175 00176 m_lastResult = NoMatch; 00177 return NoMatch; 00178 } 00179 00180 int KReplace::replace(QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength) 00181 { 00182 int matchedLength; 00183 00184 index = KFind::find(text, pattern, index, options, &matchedLength); 00185 if (index != -1) 00186 { 00187 *replacedLength = replace(text, replacement, index, options, matchedLength); 00188 if (options & KReplaceDialog::FindBackwards) 00189 index--; 00190 else 00191 index += *replacedLength; 00192 } 00193 return index; 00194 } 00195 00196 int KReplace::replace(QString &text, const QRegExp &pattern, const QString &replacement, int index, long options, int *replacedLength) 00197 { 00198 int matchedLength; 00199 00200 index = KFind::find(text, pattern, index, options, &matchedLength); 00201 if (index != -1) 00202 { 00203 *replacedLength = replace(text, replacement, index, options, matchedLength); 00204 if (options & KReplaceDialog::FindBackwards) 00205 index--; 00206 else 00207 index += *replacedLength; 00208 } 00209 return index; 00210 } 00211 00212 int KReplace::replace(QString &text, const QString &replacement, int index, long options, int length) 00213 { 00214 QString rep = replacement; 00215 // Backreferences: replace \0 with the right portion of 'text' 00216 if ( options & KReplaceDialog::BackReference ) 00217 rep.replace( "\\0", text.mid( index, length ) ); 00218 // Then replace rep into the text 00219 text.replace(index, length, rep); 00220 return rep.length(); 00221 } 00222 00223 void KReplace::slotReplaceAll() 00224 { 00225 doReplace(); 00226 m_options &= ~KReplaceDialog::PromptOnReplace; 00227 emit optionsChanged(); 00228 emit findNext(); 00229 } 00230 00231 void KReplace::slotSkip() 00232 { 00233 if (m_options & KReplaceDialog::FindBackwards) 00234 m_index--; 00235 else 00236 m_index++; 00237 if ( m_dialogClosed ) { 00238 delete m_dialog; // hide it again 00239 m_dialog = 0L; 00240 } else 00241 emit findNext(); 00242 } 00243 00244 void KReplace::slotReplace() 00245 { 00246 doReplace(); 00247 if ( m_dialogClosed ) { 00248 delete m_dialog; // hide it again 00249 m_dialog = 0L; 00250 } else 00251 emit findNext(); 00252 } 00253 00254 void KReplace::doReplace() 00255 { 00256 int replacedLength = KReplace::replace(m_text, m_replacement, m_index, m_options, m_matchedLength); 00257 00258 // Tell the world about the replacement we made, in case someone wants to 00259 // highlight it. 00260 emit replace(m_text, m_index, replacedLength, m_matchedLength); 00261 #ifdef DEBUG_REPLACE 00262 kdDebug() << k_funcinfo << "after replace() signal: m_index=" << m_index << " replacedLength=" << replacedLength << endl; 00263 #endif 00264 m_replacements++; 00265 if (m_options & KReplaceDialog::FindBackwards) 00266 m_index--; 00267 else { 00268 m_index += replacedLength; 00269 // when replacing the empty pattern, move on. See also kjs/regexp.cpp for how this should be done for regexps. 00270 if ( m_pattern.isEmpty() ) 00271 ++m_index; 00272 } 00273 #ifdef DEBUG_REPLACE 00274 kdDebug() << k_funcinfo << "after adjustement: m_index=" << m_index << endl; 00275 #endif 00276 } 00277 00278 void KReplace::resetCounts() 00279 { 00280 KFind::resetCounts(); 00281 m_replacements = 0; 00282 } 00283 00284 bool KReplace::shouldRestart( bool forceAsking, bool showNumMatches ) const 00285 { 00286 // Only ask if we did a "find from cursor", otherwise it's pointless. 00287 // ... Or if the prompt-on-replace option was set. 00288 // Well, unless the user can modify the document during a search operation, 00289 // hence the force boolean. 00290 if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 00291 && (m_options & KReplaceDialog::PromptOnReplace) == 0 ) 00292 { 00293 displayFinalDialog(); 00294 return false; 00295 } 00296 QString message; 00297 if ( showNumMatches ) 00298 { 00299 if ( !m_replacements ) 00300 message = i18n("No text was replaced."); 00301 else 00302 message = i18n("1 replacement done.", "%n replacements done.", m_replacements ); 00303 } 00304 else 00305 { 00306 if ( m_options & KFindDialog::FindBackwards ) 00307 message = i18n( "Beginning of document reached." ); 00308 else 00309 message = i18n( "End of document reached." ); 00310 } 00311 00312 message += "\n"; 00313 // Hope this word puzzle is ok, it's a different sentence 00314 message += 00315 ( m_options & KFindDialog::FindBackwards ) ? 00316 i18n("Do you want to restart search from the end?") 00317 : i18n("Do you want to restart search at the beginning?"); 00318 00319 int ret = KMessageBox::questionYesNo( parentWidget(), message, QString::null, i18n("Restart"), i18n("Stop") ); 00320 return( ret == KMessageBox::Yes ); 00321 } 00322 00323 void KReplace::closeReplaceNextDialog() 00324 { 00325 closeFindNextDialog(); 00326 } 00327 00328 #include "kreplace.moc"