KDEUI
kreplace.cpp
Go to the documentation of this file.
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 "kreplace.h" 00022 #include "kfind_p.h" 00023 00024 #include <QtGui/QLabel> 00025 #include <kapplication.h> 00026 #include <kdebug.h> 00027 00028 #include <klocale.h> 00029 #include <kmessagebox.h> 00030 #include "kreplacedialog.h" 00031 #include <QtCore/QRegExp> 00032 00033 //#define DEBUG_REPLACE 00034 #define INDEX_NOMATCH -1 00035 00036 class KReplaceNextDialog : public KDialog 00037 { 00038 public: 00039 explicit KReplaceNextDialog( QWidget *parent ); 00040 void setLabel( const QString& pattern, const QString& replacement ); 00041 private: 00042 QLabel* m_mainLabel; 00043 }; 00044 00045 KReplaceNextDialog::KReplaceNextDialog(QWidget *parent) : 00046 KDialog(parent) 00047 { 00048 setModal( false ); 00049 setCaption( i18n("Replace") ); 00050 setButtons( User3 | User2 | User1 | Close ); 00051 setButtonGuiItem( User1, KGuiItem(i18nc("@action:button Replace all occurrences", "&All")) ); 00052 setButtonGuiItem( User2, KGuiItem(i18n("&Skip")) ); 00053 setButtonGuiItem( User3, KGuiItem(i18n("Replace")) ); 00054 setDefaultButton( User3 ); 00055 00056 m_mainLabel = new QLabel( this ); 00057 setMainWidget( m_mainLabel ); 00058 } 00059 00060 void KReplaceNextDialog::setLabel( const QString& pattern, const QString& replacement ) 00061 { 00062 m_mainLabel->setText( i18n("Replace '%1' with '%2'?", pattern, replacement) ); 00063 } 00064 00066 00067 class KReplacePrivate 00068 { 00069 public: 00070 KReplacePrivate(KReplace *q, const QString& replacement) 00071 : q(q) 00072 , m_replacement( replacement ) 00073 , m_replacements( 0 ) 00074 {} 00075 00076 KReplaceNextDialog* dialog(); 00077 void doReplace(); 00078 00079 void _k_slotSkip(); 00080 void _k_slotReplace(); 00081 void _k_slotReplaceAll(); 00082 00083 KReplace *q; 00084 QString m_replacement; 00085 unsigned m_replacements; 00086 }; 00087 00088 00090 00091 KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent) : 00092 KFind( pattern, options, parent ), 00093 d( new KReplacePrivate(this, replacement) ) 00094 { 00095 } 00096 00097 KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget *dlg) : 00098 KFind( pattern, options, parent, dlg ), 00099 d( new KReplacePrivate(this, replacement) ) 00100 { 00101 } 00102 00103 KReplace::~KReplace() 00104 { 00105 delete d; 00106 } 00107 00108 int KReplace::numReplacements() const 00109 { 00110 return d->m_replacements; 00111 } 00112 00113 KDialog* KReplace::replaceNextDialog( bool create ) 00114 { 00115 if ( KFind::d->dialog || create ) 00116 return d->dialog(); 00117 return 0L; 00118 } 00119 00120 KReplaceNextDialog* KReplacePrivate::dialog() 00121 { 00122 if ( !q->KFind::d->dialog ) 00123 { 00124 q->KFind::d->dialog = new KReplaceNextDialog( q->parentWidget() ); 00125 q->connect( q->KFind::d->dialog, SIGNAL( user1Clicked() ), q, SLOT( _k_slotReplaceAll() ) ); 00126 q->connect( q->KFind::d->dialog, SIGNAL( user2Clicked() ), q, SLOT( _k_slotSkip() ) ); 00127 q->connect( q->KFind::d->dialog, SIGNAL( user3Clicked() ), q, SLOT( _k_slotReplace() ) ); 00128 q->connect( q->KFind::d->dialog, SIGNAL( finished() ), q, SLOT( _k_slotDialogClosed() ) ); 00129 } 00130 return static_cast<KReplaceNextDialog *>(q->KFind::d->dialog); 00131 } 00132 00133 void KReplace::displayFinalDialog() const 00134 { 00135 if ( !d->m_replacements ) 00136 KMessageBox::information(parentWidget(), i18n("No text was replaced.")); 00137 else 00138 KMessageBox::information(parentWidget(), i18np("1 replacement done.", "%1 replacements done.", d->m_replacements ) ); 00139 } 00140 00141 static int replaceHelper(QString &text, const QString &replacement, int index, long options, int length, const QRegExp* regExp) 00142 { 00143 QString rep(replacement); 00144 if (options & KReplaceDialog::BackReference) { 00145 // Backreferences: replace \0 with the right portion of 'text' 00146 rep.replace( "\\0", text.mid( index, length ) ); 00147 00148 // Other backrefs 00149 if (regExp) { 00150 const QStringList caps = regExp->capturedTexts(); 00151 for (int i = 0; i < caps.count(); ++i) { 00152 rep.replace( "\\" + QString::number(i), caps.at(i) ); 00153 } 00154 } 00155 } 00156 00157 // Then replace rep into the text 00158 text.replace(index, length, rep); 00159 return rep.length(); 00160 } 00161 00162 KFind::Result KReplace::replace() 00163 { 00164 KFind::Private* df = KFind::d; 00165 #ifdef DEBUG_REPLACE 00166 kDebug() << "d->index=" << df->index; 00167 #endif 00168 if ( df->index == INDEX_NOMATCH && df->lastResult == Match ) 00169 { 00170 df->lastResult = NoMatch; 00171 return NoMatch; 00172 } 00173 00174 do // this loop is only because validateMatch can fail 00175 { 00176 #ifdef DEBUG_REPLACE 00177 kDebug() << "beginning of loop: df->index=" << df->index; 00178 #endif 00179 // Find the next match. 00180 if ( df->options & KFind::RegularExpression ) 00181 df->index = KFind::find(df->text, *df->regExp, df->index, df->options, &df->matchedLength); 00182 else 00183 df->index = KFind::find(df->text, df->pattern, df->index, df->options, &df->matchedLength); 00184 00185 #ifdef DEBUG_REPLACE 00186 kDebug() << "KFind::find returned df->index=" << df->index; 00187 #endif 00188 if ( df->index != -1 ) 00189 { 00190 // Flexibility: the app can add more rules to validate a possible match 00191 if ( validateMatch( df->text, df->index, df->matchedLength ) ) 00192 { 00193 if ( df->options & KReplaceDialog::PromptOnReplace ) 00194 { 00195 #ifdef DEBUG_REPLACE 00196 kDebug() << "PromptOnReplace"; 00197 #endif 00198 // Display accurate initial string and replacement string, they can vary 00199 QString matchedText (df->text.mid( df->index, df->matchedLength )); 00200 QString rep (matchedText); 00201 replaceHelper(rep, d->m_replacement, 0, df->options, df->matchedLength, df->regExp); 00202 d->dialog()->setLabel( matchedText, rep ); 00203 d->dialog()->show(); // TODO kde5: virtual void showReplaceNextDialog(QString,QString), so that kreplacetest can skip the show() 00204 00205 // Tell the world about the match we found, in case someone wants to 00206 // highlight it. 00207 emit highlight(df->text, df->index, df->matchedLength); 00208 00209 df->lastResult = Match; 00210 return Match; 00211 } 00212 else 00213 { 00214 d->doReplace(); // this moves on too 00215 } 00216 } 00217 else 00218 { 00219 // not validated -> move on 00220 if (df->options & KFind::FindBackwards) 00221 df->index--; 00222 else 00223 df->index++; 00224 } 00225 } else 00226 df->index = INDEX_NOMATCH; // will exit the loop 00227 } 00228 while (df->index != INDEX_NOMATCH); 00229 00230 df->lastResult = NoMatch; 00231 return NoMatch; 00232 } 00233 00234 int KReplace::replace(QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength) 00235 { 00236 int matchedLength; 00237 00238 index = KFind::find(text, pattern, index, options, &matchedLength); 00239 if (index != -1) 00240 { 00241 *replacedLength = replaceHelper(text, replacement, index, options, matchedLength, NULL); 00242 if (options & KFind::FindBackwards) 00243 index--; 00244 else 00245 index += *replacedLength; 00246 } 00247 return index; 00248 } 00249 00250 int KReplace::replace(QString &text, const QRegExp &pattern, const QString &replacement, int index, long options, int *replacedLength) 00251 { 00252 int matchedLength; 00253 00254 index = KFind::find(text, pattern, index, options, &matchedLength); 00255 if (index != -1) 00256 { 00257 *replacedLength = replaceHelper(text, replacement, index, options, matchedLength, &pattern); 00258 if (options & KFind::FindBackwards) 00259 index--; 00260 else 00261 index += *replacedLength; 00262 } 00263 return index; 00264 } 00265 00266 void KReplacePrivate::_k_slotReplaceAll() 00267 { 00268 doReplace(); 00269 q->KFind::d->options &= ~KReplaceDialog::PromptOnReplace; 00270 emit q->optionsChanged(); 00271 emit q->findNext(); 00272 } 00273 00274 void KReplacePrivate::_k_slotSkip() 00275 { 00276 if (q->KFind::d->options & KFind::FindBackwards) 00277 q->KFind::d->index--; 00278 else 00279 q->KFind::d->index++; 00280 if ( q->KFind::d->dialogClosed ) { 00281 q->KFind::d->dialog->deleteLater(); q->KFind::d->dialog = 0L; // hide it again 00282 } else 00283 emit q->findNext(); 00284 } 00285 00286 void KReplacePrivate::_k_slotReplace() 00287 { 00288 doReplace(); 00289 if ( q->KFind::d->dialogClosed ) { 00290 q->KFind::d->dialog->deleteLater(); q->KFind::d->dialog = 0L; // hide it again 00291 } else 00292 emit q->findNext(); 00293 } 00294 00295 void KReplacePrivate::doReplace() 00296 { 00297 KFind::Private* df = q->KFind::d; 00298 Q_ASSERT(df->index >= 0); 00299 const int replacedLength = replaceHelper(df->text, m_replacement, df->index, df->options, df->matchedLength, df->regExp); 00300 00301 // Tell the world about the replacement we made, in case someone wants to 00302 // highlight it. 00303 emit q->replace(df->text, df->index, replacedLength, df->matchedLength); 00304 #ifdef DEBUG_REPLACE 00305 kDebug() << "after replace() signal: KFind::d->index=" << df->index << " replacedLength=" << replacedLength; 00306 #endif 00307 m_replacements++; 00308 if (df->options & KFind::FindBackwards) { 00309 Q_ASSERT(df->index >= 0); 00310 df->index--; 00311 } else { 00312 df->index += replacedLength; 00313 // when replacing the empty pattern, move on. See also kjs/regexp.cpp for how this should be done for regexps. 00314 if ( df->pattern.isEmpty() ) 00315 ++(df->index); 00316 } 00317 #ifdef DEBUG_REPLACE 00318 kDebug() << "after adjustement: KFind::d->index=" << df->index; 00319 #endif 00320 } 00321 00322 void KReplace::resetCounts() 00323 { 00324 KFind::resetCounts(); 00325 d->m_replacements = 0; 00326 } 00327 00328 bool KReplace::shouldRestart( bool forceAsking, bool showNumMatches ) const 00329 { 00330 // Only ask if we did a "find from cursor", otherwise it's pointless. 00331 // ... Or if the prompt-on-replace option was set. 00332 // Well, unless the user can modify the document during a search operation, 00333 // hence the force boolean. 00334 if ( !forceAsking && (KFind::d->options & KFind::FromCursor) == 0 00335 && (KFind::d->options & KReplaceDialog::PromptOnReplace) == 0 ) 00336 { 00337 displayFinalDialog(); 00338 return false; 00339 } 00340 QString message; 00341 if ( showNumMatches ) 00342 { 00343 if ( !d->m_replacements ) 00344 message = i18n("No text was replaced."); 00345 else 00346 message = i18np("1 replacement done.", "%1 replacements done.", d->m_replacements ); 00347 } 00348 else 00349 { 00350 if ( KFind::d->options & KFind::FindBackwards ) 00351 message = i18n( "Beginning of document reached." ); 00352 else 00353 message = i18n( "End of document reached." ); 00354 } 00355 00356 message += '\n'; 00357 // Hope this word puzzle is ok, it's a different sentence 00358 message += 00359 ( KFind::d->options & KFind::FindBackwards ) ? 00360 i18n("Do you want to restart search from the end?") 00361 : i18n("Do you want to restart search at the beginning?"); 00362 00363 int ret = KMessageBox::questionYesNo( parentWidget(), message, QString(), 00364 KGuiItem(i18nc("@action:button Restart find & replace", "Restart")), 00365 KGuiItem(i18nc("@action:button Stop find & replace", "Stop")) ); 00366 return( ret == KMessageBox::Yes ); 00367 } 00368 00369 void KReplace::closeReplaceNextDialog() 00370 { 00371 closeFindNextDialog(); 00372 } 00373 00374 #include "kreplace.moc"
KDE 4.6 API Reference