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