File indexing completed on 2024-04-21 03:58:27

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2001 S.R. Haque <srhaque@iee.org>.
0004     SPDX-FileCopyrightText: 2002 David Faure <david@mandrakesoft.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "kreplacedialog.h"
0010 #include "kfinddialog_p.h"
0011 
0012 #include <QCheckBox>
0013 #include <QGridLayout>
0014 #include <QGroupBox>
0015 #include <QLineEdit>
0016 #include <QRegularExpression>
0017 
0018 #include <KHistoryComboBox>
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 
0022 /**
0023  * we need to insert the strings after the dialog is set
0024  * up, otherwise QComboBox will deliver an awful big sizeHint
0025  * for long replacement texts.
0026  */
0027 class KReplaceDialogPrivate : public KFindDialogPrivate
0028 {
0029     Q_DECLARE_PUBLIC(KReplaceDialog)
0030 
0031 public:
0032     explicit KReplaceDialogPrivate(KReplaceDialog *qq)
0033         : KFindDialogPrivate(qq)
0034     {
0035     }
0036 
0037     void slotOk();
0038 
0039     QStringList replaceStrings;
0040     mutable QWidget *replaceExtension = nullptr;
0041     bool initialShowDone = false;
0042 };
0043 
0044 KReplaceDialog::KReplaceDialog(QWidget *parent, long options, const QStringList &findStrings, const QStringList &replaceStrings, bool hasSelection)
0045     : KFindDialog(*new KReplaceDialogPrivate(this), parent, options, findStrings, hasSelection, true /*create replace dialog*/)
0046 {
0047     Q_D(KReplaceDialog);
0048 
0049     d->replaceStrings = replaceStrings;
0050 }
0051 
0052 KReplaceDialog::~KReplaceDialog() = default;
0053 
0054 void KReplaceDialog::showEvent(QShowEvent *e)
0055 {
0056     Q_D(KReplaceDialog);
0057 
0058     if (!d->initialShowDone) {
0059         d->initialShowDone = true; // only once
0060 
0061         if (!d->replaceStrings.isEmpty()) {
0062             setReplacementHistory(d->replaceStrings);
0063             d->replace->lineEdit()->setText(d->replaceStrings[0]);
0064         }
0065     }
0066 
0067     KFindDialog::showEvent(e);
0068 }
0069 
0070 long KReplaceDialog::options() const
0071 {
0072     Q_D(const KReplaceDialog);
0073 
0074     long options = 0;
0075 
0076     options = KFindDialog::options();
0077     if (d->promptOnReplace->isChecked()) {
0078         options |= PromptOnReplace;
0079     }
0080     if (d->backRef->isChecked()) {
0081         options |= BackReference;
0082     }
0083     return options;
0084 }
0085 
0086 QWidget *KReplaceDialog::replaceExtension() const
0087 {
0088     Q_D(const KReplaceDialog);
0089 
0090     if (!d->replaceExtension) {
0091         d->replaceExtension = new QWidget(d->replaceGrp);
0092         d->replaceLayout->addWidget(d->replaceExtension, 3, 0, 1, 2);
0093     }
0094 
0095     return d->replaceExtension;
0096 }
0097 
0098 QString KReplaceDialog::replacement() const
0099 {
0100     Q_D(const KReplaceDialog);
0101 
0102     return d->replace->currentText();
0103 }
0104 
0105 QStringList KReplaceDialog::replacementHistory() const
0106 {
0107     Q_D(const KReplaceDialog);
0108 
0109     QStringList lst = d->replace->historyItems();
0110     // historyItems() doesn't tell us about the case of replacing with an empty string
0111     if (d->replace->lineEdit()->text().isEmpty()) {
0112         lst.prepend(QString());
0113     }
0114     return lst;
0115 }
0116 
0117 void KReplaceDialog::setOptions(long options)
0118 {
0119     Q_D(KReplaceDialog);
0120 
0121     KFindDialog::setOptions(options);
0122     d->promptOnReplace->setChecked(options & PromptOnReplace);
0123     d->backRef->setChecked(options & BackReference);
0124 }
0125 
0126 void KReplaceDialog::setReplacementHistory(const QStringList &strings)
0127 {
0128     Q_D(KReplaceDialog);
0129 
0130     if (!strings.isEmpty()) {
0131         d->replace->setHistoryItems(strings, true);
0132     } else {
0133         d->replace->clearHistory();
0134     }
0135 }
0136 
0137 void KReplaceDialogPrivate::slotOk()
0138 {
0139     Q_Q(KReplaceDialog);
0140 
0141     // If regex and backrefs are enabled, do a sanity check.
0142     if (regExp->isChecked() && backRef->isChecked()) {
0143         const QRegularExpression re(q->pattern(), QRegularExpression::UseUnicodePropertiesOption);
0144         const int caps = re.captureCount();
0145 
0146         const QString rep = q->replacement();
0147         const QRegularExpression check(QStringLiteral("((?:\\\\)+)(\\d+)"));
0148         auto iter = check.globalMatch(rep);
0149         while (iter.hasNext()) {
0150             const QRegularExpressionMatch match = iter.next();
0151             if ((match.captured(1).size() % 2) && match.captured(2).toInt() > caps) {
0152                 KMessageBox::information(q,
0153                                          i18n("Your replacement string is referencing a capture greater than '\\%1', ", caps)
0154                                              + (caps ? i18np("but your pattern only defines 1 capture.", "but your pattern only defines %1 captures.", caps)
0155                                                      : i18n("but your pattern defines no captures."))
0156                                              + i18n("\nPlease correct."));
0157                 return; // abort OKing
0158             }
0159         }
0160     }
0161 
0162     slotOk();
0163     replace->addToHistory(q->replacement());
0164 }
0165 
0166 #include "moc_kreplacedialog.cpp"