1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2001 S.R. Haque <srhaque@iee.org>. |
4 | SPDX-FileCopyrightText: 2002 David Faure <david@mandrakesoft.com> |
5 | |
6 | SPDX-License-Identifier: LGPL-2.0-only |
7 | */ |
8 | |
9 | #include "kreplacedialog.h" |
10 | #include "kfinddialog_p.h" |
11 | |
12 | #include <QCheckBox> |
13 | #include <QGridLayout> |
14 | #include <QGroupBox> |
15 | #include <QLineEdit> |
16 | #include <QRegularExpression> |
17 | |
18 | #include <KHistoryComboBox> |
19 | #include <KLocalizedString> |
20 | #include <KMessageBox> |
21 | |
22 | /** |
23 | * we need to insert the strings after the dialog is set |
24 | * up, otherwise QComboBox will deliver an awful big sizeHint |
25 | * for long replacement texts. |
26 | */ |
27 | class KReplaceDialogPrivate : public KFindDialogPrivate |
28 | { |
29 | Q_DECLARE_PUBLIC(KReplaceDialog) |
30 | |
31 | public: |
32 | explicit KReplaceDialogPrivate(KReplaceDialog *qq) |
33 | : KFindDialogPrivate(qq) |
34 | { |
35 | } |
36 | |
37 | void slotOk(); |
38 | |
39 | QStringList replaceStrings; |
40 | mutable QWidget *replaceExtension = nullptr; |
41 | bool initialShowDone = false; |
42 | }; |
43 | |
44 | KReplaceDialog::KReplaceDialog(QWidget *parent, long options, const QStringList &findStrings, const QStringList &replaceStrings, bool hasSelection) |
45 | : KFindDialog(*new KReplaceDialogPrivate(this), parent, options, findStrings, hasSelection, true /*create replace dialog*/) |
46 | { |
47 | Q_D(KReplaceDialog); |
48 | |
49 | d->replaceStrings = replaceStrings; |
50 | } |
51 | |
52 | KReplaceDialog::~KReplaceDialog() = default; |
53 | |
54 | void KReplaceDialog::showEvent(QShowEvent *e) |
55 | { |
56 | Q_D(KReplaceDialog); |
57 | |
58 | if (!d->initialShowDone) { |
59 | d->initialShowDone = true; // only once |
60 | |
61 | if (!d->replaceStrings.isEmpty()) { |
62 | setReplacementHistory(d->replaceStrings); |
63 | d->replace->lineEdit()->setText(d->replaceStrings[0]); |
64 | } |
65 | } |
66 | |
67 | KFindDialog::showEvent(e); |
68 | } |
69 | |
70 | long KReplaceDialog::options() const |
71 | { |
72 | Q_D(const KReplaceDialog); |
73 | |
74 | long options = 0; |
75 | |
76 | options = KFindDialog::options(); |
77 | if (d->promptOnReplace->isChecked()) { |
78 | options |= PromptOnReplace; |
79 | } |
80 | if (d->backRef->isChecked()) { |
81 | options |= BackReference; |
82 | } |
83 | return options; |
84 | } |
85 | |
86 | QWidget *KReplaceDialog::replaceExtension() const |
87 | { |
88 | Q_D(const KReplaceDialog); |
89 | |
90 | if (!d->replaceExtension) { |
91 | d->replaceExtension = new QWidget(d->replaceGrp); |
92 | d->replaceLayout->addWidget(d->replaceExtension, row: 3, column: 0, rowSpan: 1, columnSpan: 2); |
93 | } |
94 | |
95 | return d->replaceExtension; |
96 | } |
97 | |
98 | QString KReplaceDialog::replacement() const |
99 | { |
100 | Q_D(const KReplaceDialog); |
101 | |
102 | return d->replace->currentText(); |
103 | } |
104 | |
105 | QStringList KReplaceDialog::replacementHistory() const |
106 | { |
107 | Q_D(const KReplaceDialog); |
108 | |
109 | QStringList lst = d->replace->historyItems(); |
110 | // historyItems() doesn't tell us about the case of replacing with an empty string |
111 | if (d->replace->lineEdit()->text().isEmpty()) { |
112 | lst.prepend(t: QString()); |
113 | } |
114 | return lst; |
115 | } |
116 | |
117 | void KReplaceDialog::setOptions(long options) |
118 | { |
119 | Q_D(KReplaceDialog); |
120 | |
121 | KFindDialog::setOptions(options); |
122 | d->promptOnReplace->setChecked(options & PromptOnReplace); |
123 | d->backRef->setChecked(options & BackReference); |
124 | } |
125 | |
126 | void KReplaceDialog::setReplacementHistory(const QStringList &strings) |
127 | { |
128 | Q_D(KReplaceDialog); |
129 | |
130 | if (!strings.isEmpty()) { |
131 | d->replace->setHistoryItems(items: strings, setCompletionList: true); |
132 | } else { |
133 | d->replace->clearHistory(); |
134 | } |
135 | } |
136 | |
137 | void KReplaceDialogPrivate::slotOk() |
138 | { |
139 | Q_Q(KReplaceDialog); |
140 | |
141 | // If regex and backrefs are enabled, do a sanity check. |
142 | if (regExp->isChecked() && backRef->isChecked()) { |
143 | const QRegularExpression re(q->pattern(), QRegularExpression::UseUnicodePropertiesOption); |
144 | const int caps = re.captureCount(); |
145 | |
146 | const QString rep = q->replacement(); |
147 | const QRegularExpression check(QStringLiteral("((?:\\\\)+)(\\d+)" )); |
148 | auto iter = check.globalMatch(subject: rep); |
149 | while (iter.hasNext()) { |
150 | const QRegularExpressionMatch match = iter.next(); |
151 | if ((match.captured(nth: 1).size() % 2) && match.captured(nth: 2).toInt() > caps) { |
152 | KMessageBox::information(parent: q, |
153 | i18n("Your replacement string is referencing a capture greater than '\\%1', " , caps) |
154 | + (caps ? i18np("but your pattern only defines 1 capture." , "but your pattern only defines %1 captures." , caps) |
155 | : i18n("but your pattern defines no captures." )) |
156 | + i18n("\nPlease correct." )); |
157 | return; // abort OKing |
158 | } |
159 | } |
160 | } |
161 | |
162 | slotOk(); |
163 | replace->addToHistory(item: q->replacement()); |
164 | } |
165 | |
166 | #include "moc_kreplacedialog.cpp" |
167 | |