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 | |
26 | class KReplaceNextDialog; |
27 | class 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 | */ |
96 | class KDEUI_EXPORT KReplace : |
97 | public KFind |
98 | { |
99 | Q_OBJECT |
100 | |
101 | public: |
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 | |
198 | Q_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 | |
216 | private: |
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 | |