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 SPDX-FileCopyrightText: 2004 Arend van Beelen jr. <arend@auton.nl>
6
7 SPDX-License-Identifier: LGPL-2.0-only
8*/
9
10#ifndef KREPLACE_H
11#define KREPLACE_H
12
13#include "kfind.h"
14
15#include "ktextwidgets_export.h"
16
17class KReplacePrivate;
18
19/*!
20 * \class KReplace
21 * \inmodule KTextWidgets
22 *
23 * \brief A generic implementation of the "replace" function.
24 *
25 * \b Detail:
26 *
27 * This class includes prompt handling etc. Also provides some
28 * static functions which can be used to create custom behavior
29 * instead of using the class directly.
30 *
31 * \b Example:
32 *
33 * To use the class to implement a complete replace feature:
34 *
35 * In the slot connect to the replace action, after using KReplaceDialog:
36 * \code
37 *
38 * // This creates a replace-on-prompt dialog if needed.
39 * m_replace = new KReplace(pattern, replacement, options, this);
40 *
41 * // Connect signals to code which handles highlighting of found text, and
42 * // on-the-fly replacement.
43 * connect(m_replace, &KFind::textFound, this, [this](const QString &text, int matchingIndex, int matchedLength) {
44 * slotHighlight(text, matchingIndex, matchedLength);
45 * });
46 * // Connect findNext signal - called when pressing the button in the dialog
47 * connect( m_replace, SIGNAL( findNext() ),
48 * this, SLOT( slotReplaceNext() ) );
49 * // Connect to the textReplaced() signal - emitted when a replacement is done
50 * connect( m_replace, &KReplace::textReplaced, this, [this](const QString &text, int replacementIndex, int replacedLength, int matchedLength) {
51 * slotReplace(text, replacementIndex, replacedLength, matchedLength);
52 * });
53 * \endcode
54 * Then initialize the variables determining the "current position"
55 * (to the cursor, if the option FromCursor is set,
56 * to the beginning of the selection if the option SelectedText is set,
57 * and to the beginning of the document otherwise).
58 * Initialize the "end of search" variables as well (end of doc or end of selection).
59 * Swap begin and end if FindBackwards.
60 * Finally, call slotReplaceNext();
61 *
62 * \code
63 * void slotReplaceNext()
64 * {
65 * KFind::Result res = KFind::NoMatch;
66 * while ( res == KFind::NoMatch && <position not at end> ) {
67 * if ( m_replace->needData() )
68 * m_replace->setData( <current text fragment> );
69 *
70 * // Let KReplace inspect the text fragment, and display a dialog if a match is found
71 * res = m_replace->replace();
72 *
73 * if ( res == KFind::NoMatch ) {
74 * <Move to the next text fragment, honoring the FindBackwards setting for the direction>
75 * }
76 * }
77 *
78 * if ( res == KFind::NoMatch ) // i.e. at end
79 * <Call either m_replace->displayFinalDialog(); delete m_replace; m_replace = nullptr;
80 * or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
81 * else { m_replace->closeReplaceNextDialog(); }>
82 * }
83 * \endcode
84 *
85 * Don't forget delete m_find in the destructor of your class,
86 * unless you gave it a parent widget on construction.
87 *
88 */
89class KTEXTWIDGETS_EXPORT KReplace : public KFind
90{
91 Q_OBJECT
92
93public:
94 /*!
95 * Only use this constructor if you don't use KFindDialog, or if
96 * you use it as a modal dialog.
97 */
98 KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent = nullptr);
99 /*!
100 * This is the recommended constructor if you also use KReplaceDialog (non-modal).
101 * You should pass the pointer to it here, so that when a message box
102 * appears it has the right parent. Don't worry about deletion, KReplace
103 * will notice if the find dialog is closed.
104 */
105 KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget *replaceDialog);
106
107 ~KReplace() override;
108
109 /*!
110 * Return the number of replacements made (i.e. the number of times
111 * the textReplaced() signal was emitted).
112 *
113 * Can be used in a dialog box to tell the user how many replacements were made.
114 * The final dialog does so already, unless you used setDisplayFinalDialog(false).
115 */
116 int numReplacements() const;
117
118 /*!
119 * Call this to reset the numMatches & numReplacements counts.
120 * Can be useful if reusing the same KReplace for different operations,
121 * or when restarting from the beginning of the document.
122 */
123 void resetCounts() override;
124
125 /*!
126 * Walk the text fragment (e.g. kwrite line, kspread cell) looking for matches.
127 * For each match, if prompt-on-replace is specified, emits the textFound() signal
128 * and displays the prompt-for-replace dialog before doing the replace.
129 */
130 Result replace();
131
132 /*!
133 * Returns (or creates if \a create is true) the dialog that shows the "find next?" prompt.
134 * Usually you don't need to call this.
135 * One case where it can be useful, is when the user selects the "Find"
136 * menu item while a find operation is under way. In that case, the
137 * program may want to call setActiveWindow() on that dialog.
138 */
139 QDialog *replaceNextDialog(bool create = false);
140
141 /*!
142 * Close the "replace next?" dialog. The application should do this when
143 * the last match was hit. If the application deletes the KReplace, then
144 * "find previous" won't be possible anymore.
145 */
146 void closeReplaceNextDialog();
147
148 /*!
149 * Searches the given \a text for \a pattern; if a match is found it is replaced
150 * with \a replacement and the index of the replacement string is returned.
151 *
152 * \a text The string to search
153 *
154 * \a pattern The pattern to search for
155 *
156 * \a replacement The replacement string to insert into the text
157 *
158 * \a index The starting index into the string
159 *
160 * \a options The options to use
161 *
162 * \a replacedLength Output parameter, contains the length of the replaced string
163 * Not always the same as replacement.length(), when backreferences are used
164 *
165 * Returns The index at which a match was found, or -1 otherwise
166 */
167 static int replace(QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength);
168
169 /*!
170 * Returns true if we should restart the search from scratch.
171 * Can ask the user, or return false (if we already searched/replaced the
172 * whole document without the PromptOnReplace option).
173 *
174 * \a forceAsking set to true if the user modified the document during the
175 * search. In that case it makes sense to restart the search again.
176 *
177 * \a showNumMatches set to true if the dialog should show the number of
178 * matches. Set to false if the application provides a "find previous" action,
179 * in which case the match count will be erroneous when hitting the end,
180 * and we could even be hitting the beginning of the document (so not all
181 * matches have even been seen).
182 */
183 bool shouldRestart(bool forceAsking = false, bool showNumMatches = true) const override;
184
185 /*!
186 * Displays the final dialog telling the user how many replacements were made.
187 * Call either this or shouldRestart().
188 */
189 void displayFinalDialog() const override;
190
191Q_SIGNALS:
192 /*!
193 * Connect to this signal to implement updating of replaced text during the replace
194 * operation.
195 *
196 * Extra care must be taken to properly implement the "no prompt-on-replace" case.
197 * For instance, the textFound() signal isn't emitted in that case (some code
198 * might rely on it), and for performance reasons one should repaint after
199 * replace() ONLY if prompt-on-replace was selected.
200 *
201 * \a text The text, in which the replacement has already been done
202 *
203 * \a replacementIndex Starting index of the matched substring
204 *
205 * \a replacedLength Length of the replacement string
206 *
207 * \a matchedLength Length of the matched string
208 *
209 * \since 5.83
210 */
211 void textReplaced(const QString &text, int replacementIndex, int replacedLength, int matchedLength);
212
213private:
214 Q_DECLARE_PRIVATE(KReplace)
215};
216#endif
217

source code of ktextwidgets/src/findreplace/kreplace.h