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 KFIND_H |
22 | #define KFIND_H |
23 | |
24 | #include <kdialog.h> |
25 | #include <QtCore/QRect> |
26 | |
27 | /** |
28 | * @brief A generic implementation of the "find" function. |
29 | * |
30 | * @author S.R.Haque <srhaque@iee.org>, David Faure <faure@kde.org>, |
31 | * Arend van Beelen jr. <arend@auton.nl> |
32 | * |
33 | * \b Detail: |
34 | * |
35 | * This class includes prompt handling etc. Also provides some |
36 | * static functions which can be used to create custom behavior |
37 | * instead of using the class directly. |
38 | * |
39 | * \b Example: |
40 | * |
41 | * To use the class to implement a complete find feature: |
42 | * |
43 | * In the slot connected to the find action, after using KFindDialog: |
44 | * \code |
45 | * |
46 | * // This creates a find-next-prompt dialog if needed. |
47 | * m_find = new KFind(pattern, options, this); |
48 | * |
49 | * // Connect highlight signal to code which handles highlighting |
50 | * // of found text. |
51 | * connect( m_find, SIGNAL( highlight( const QString &, int, int ) ), |
52 | * this, SLOT( slotHighlight( const QString &, int, int ) ) ); |
53 | * // Connect findNext signal - called when pressing the button in the dialog |
54 | * connect( m_find, SIGNAL( findNext() ), |
55 | * this, SLOT( slotFindNext() ) ); |
56 | * \endcode |
57 | * |
58 | * If you are using a non-modal find dialog (the recommended new way |
59 | * in KDE-3.2), you should call right away m_find->closeFindNextDialog(). |
60 | * |
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 slotFindNext(); |
68 | * |
69 | * \code |
70 | * void slotFindNext() |
71 | * { |
72 | * KFind::Result res = KFind::NoMatch; |
73 | * while ( res == KFind::NoMatch && <position not at end> ) { |
74 | * if ( m_find->needData() ) |
75 | * m_find->setData( <current text fragment> ); |
76 | * |
77 | * // Let KFind inspect the text fragment, and display a dialog if a match is found |
78 | * res = m_find->find(); |
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_find->displayFinalDialog(); m_find->deleteLater(); m_find = 0L; |
87 | * or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); } |
88 | * else { m_find->closeFindNextDialog(); }> |
89 | * } |
90 | * \endcode |
91 | * |
92 | * Don't forget to delete m_find in the destructor of your class, |
93 | * unless you gave it a parent widget on construction. |
94 | * |
95 | * This implementation allows to have a "Find Next" action, which resumes the |
96 | * search, even if the user closed the "Find Next" dialog. |
97 | * |
98 | * A "Find Previous" action can simply switch temporarily the value of |
99 | * FindBackwards and call slotFindNext() - and reset the value afterwards. |
100 | */ |
101 | class KDEUI_EXPORT KFind : |
102 | public QObject |
103 | { |
104 | Q_OBJECT |
105 | |
106 | public: |
107 | |
108 | /// the options |
109 | enum Options |
110 | { |
111 | WholeWordsOnly = 1, ///< Match whole words only. |
112 | FromCursor = 2, ///< Start from current cursor position. |
113 | SelectedText = 4, ///< Only search selected area. |
114 | CaseSensitive = 8, ///< Consider case when matching. |
115 | FindBackwards = 16, ///< Go backwards. |
116 | RegularExpression = 32, ///< Interpret the pattern as a regular expression. |
117 | FindIncremental = 64, ///< Find incremental. |
118 | // Note that KReplaceDialog uses 256 and 512 |
119 | // User extensions can use boolean options above this value. |
120 | MinimumUserOption = 65536 ///< user options start with this bit |
121 | }; |
122 | Q_DECLARE_FLAGS(SearchOptions, Options) |
123 | |
124 | /** |
125 | * Only use this constructor if you don't use KFindDialog, or if |
126 | * you use it as a modal dialog. |
127 | */ |
128 | KFind(const QString &pattern, long options, QWidget *parent); |
129 | |
130 | /** |
131 | * This is the recommended constructor if you also use KFindDialog (non-modal). |
132 | * You should pass the pointer to it here, so that when a message box |
133 | * appears it has the right parent. Don't worry about deletion, KFind |
134 | * will notice if the find dialog is closed. |
135 | */ |
136 | KFind(const QString &pattern, long options, QWidget *parent, QWidget* findDialog); |
137 | virtual ~KFind(); |
138 | |
139 | enum Result { NoMatch, Match }; |
140 | |
141 | /** |
142 | * @return true if the application must supply a new text fragment |
143 | * It also means the last call returned "NoMatch". But by storing this here |
144 | * the application doesn't have to store it in a member variable (between |
145 | * calls to slotFindNext()). |
146 | */ |
147 | bool needData() const; |
148 | |
149 | /** |
150 | * Call this when needData returns true, before calling find(). |
151 | * @param data the text fragment (line) |
152 | * @param startPos if set, the index at which the search should start. |
153 | * This is only necessary for the very first call to setData usually, |
154 | * for the 'find in selection' feature. A value of -1 (the default value) |
155 | * means "process all the data", i.e. either 0 or data.length()-1 depending |
156 | * on FindBackwards. |
157 | */ |
158 | void setData( const QString& data, int startPos = -1 ); |
159 | |
160 | /** |
161 | * Call this when needData returns true, before calling find(). The use of |
162 | * ID's is especially useful if you're using the FindIncremental option. |
163 | * @param id the id of the text fragment |
164 | * @param data the text fragment (line) |
165 | * @param startPos if set, the index at which the search should start. |
166 | * This is only necessary for the very first call to setData usually, |
167 | * for the 'find in selection' feature. A value of -1 (the default value) |
168 | * means "process all the data", i.e. either 0 or data.length()-1 depending |
169 | * on FindBackwards. |
170 | */ |
171 | void setData( int id, const QString& data, int startPos = -1 ); |
172 | |
173 | /** |
174 | * Walk the text fragment (e.g. text-processor line, kspread cell) looking for matches. |
175 | * For each match, emits the highlight() signal and displays the find-again dialog |
176 | * proceeding. |
177 | */ |
178 | Result find(); |
179 | |
180 | /** |
181 | * Return the current options. |
182 | * |
183 | * Warning: this is usually the same value as the one passed to the constructor, |
184 | * but options might change _during_ the replace operation: |
185 | * e.g. the "All" button resets the PromptOnReplace flag. |
186 | * |
187 | * @see KFind::Options |
188 | */ |
189 | long options() const; |
190 | |
191 | /** |
192 | * Set new options. Usually this is used for setting or clearing the |
193 | * FindBackwards options. |
194 | * |
195 | * @see KFind::Options |
196 | */ |
197 | virtual void setOptions( long options ); |
198 | |
199 | /** |
200 | * @return the pattern we're currently looking for |
201 | */ |
202 | QString pattern() const; |
203 | |
204 | /** |
205 | * Change the pattern we're looking for |
206 | */ |
207 | void setPattern( const QString& pattern ); |
208 | |
209 | /** |
210 | * Return the number of matches found (i.e. the number of times |
211 | * the highlight signal was emitted). |
212 | * If 0, can be used in a dialog box to tell the user "no match was found". |
213 | * The final dialog does so already, unless you used setDisplayFinalDialog(false). |
214 | */ |
215 | int numMatches() const; |
216 | |
217 | /** |
218 | * Call this to reset the numMatches count |
219 | * (and the numReplacements count for a KReplace). |
220 | * Can be useful if reusing the same KReplace for different operations, |
221 | * or when restarting from the beginning of the document. |
222 | */ |
223 | virtual void resetCounts(); |
224 | |
225 | /** |
226 | * Virtual method, which allows applications to add extra checks for |
227 | * validating a candidate match. It's only necessary to reimplement this |
228 | * if the find dialog extension has been used to provide additional |
229 | * criterias. |
230 | * |
231 | * @param text The current text fragment |
232 | * @param index The starting index where the candidate match was found |
233 | * @param matchedlength The length of the candidate match |
234 | */ |
235 | virtual bool validateMatch( const QString & text, |
236 | int index, |
237 | int matchedlength ); |
238 | |
239 | /** |
240 | * Returns true if we should restart the search from scratch. |
241 | * Can ask the user, or return false (if we already searched the whole document). |
242 | * |
243 | * @param forceAsking set to true if the user modified the document during the |
244 | * search. In that case it makes sense to restart the search again. |
245 | * |
246 | * @param showNumMatches set to true if the dialog should show the number of |
247 | * matches. Set to false if the application provides a "find previous" action, |
248 | * in which case the match count will be erroneous when hitting the end, |
249 | * and we could even be hitting the beginning of the document (so not all |
250 | * matches have even been seen). |
251 | */ |
252 | virtual bool shouldRestart( bool forceAsking = false, bool showNumMatches = true ) const; |
253 | |
254 | /** |
255 | * Search the given string, and returns whether a match was found. If one is, |
256 | * the length of the string matched is also returned. |
257 | * |
258 | * A performance optimised version of the function is provided for use |
259 | * with regular expressions. |
260 | * |
261 | * @param text The string to search. |
262 | * @param pattern The pattern to look for. |
263 | * @param index The starting index into the string. |
264 | * @param options The options to use. |
265 | * @param matchedlength The length of the string that was matched |
266 | * @return The index at which a match was found, or -1 if no match was found. |
267 | */ |
268 | static int find( const QString &text, const QString &pattern, int index, long options, int *matchedlength ); |
269 | |
270 | static int find( const QString &text, const QRegExp &pattern, int index, long options, int *matchedlength ); |
271 | |
272 | /** |
273 | * Displays the final dialog saying "no match was found", if that was the case. |
274 | * Call either this or shouldRestart(). |
275 | */ |
276 | virtual void displayFinalDialog() const; |
277 | |
278 | /** |
279 | * Return (or create) the dialog that shows the "find next?" prompt. |
280 | * Usually you don't need to call this. |
281 | * One case where it can be useful, is when the user selects the "Find" |
282 | * menu item while a find operation is under way. In that case, the |
283 | * program may want to call setActiveWindow() on that dialog. |
284 | */ |
285 | KDialog* findNextDialog( bool create = false ); |
286 | |
287 | /** |
288 | * Close the "find next?" dialog. The application should do this when |
289 | * the last match was hit. If the application deletes the KFind, then |
290 | * "find previous" won't be possible anymore. |
291 | * |
292 | * IMPORTANT: you should also call this if you are using a non-modal |
293 | * find dialog, to tell KFind not to pop up its own dialog. |
294 | */ |
295 | void closeFindNextDialog(); |
296 | |
297 | /** |
298 | * @return the current matching index ( or -1 ). |
299 | * Same as the matchingIndex parameter passed to highlight. |
300 | * You usually don't need to use this, except maybe when updating the current data, |
301 | * so you need to call setData( newData, index() ). |
302 | */ |
303 | int index() const; |
304 | |
305 | Q_SIGNALS: |
306 | |
307 | /** |
308 | * Connect to this signal to implement highlighting of found text during the find |
309 | * operation. |
310 | * |
311 | * If you've set data with setData(id, text), use the signal highlight(id, |
312 | * matchingIndex, matchedLength) |
313 | * |
314 | * WARNING: If you're using the FindIncremental option, the text argument |
315 | * passed by this signal is not necessarily the data last set through |
316 | * setData(), but can also be an earlier set data block. |
317 | * |
318 | * @see setData() |
319 | */ |
320 | void highlight(const QString &text, int matchingIndex, int matchedLength); |
321 | |
322 | /** |
323 | * Connect to this signal to implement highlighting of found text during the find |
324 | * operation. |
325 | * |
326 | * Use this signal if you've set your data with setData(id, text), otherwise |
327 | * use the signal with highlight(text, matchingIndex, matchedLength). |
328 | * |
329 | * WARNING: If you're using the FindIncremental option, the id argument |
330 | * passed by this signal is not necessarily the id of the data last set |
331 | * through setData(), but can also be of an earlier set data block. |
332 | * |
333 | * @see setData() |
334 | */ |
335 | void highlight(int id, int matchingIndex, int matchedLength); |
336 | |
337 | // ## TODO docu |
338 | // findprevious will also emit findNext, after temporarily switching the value |
339 | // of FindBackwards |
340 | void findNext(); |
341 | |
342 | /** |
343 | * Emitted when the options have changed. |
344 | * This can happen e.g. with "Replace All", or if our 'find next' dialog |
345 | * gets a "find previous" one day. |
346 | */ |
347 | void optionsChanged(); |
348 | |
349 | /** |
350 | * Emitted when the 'find next' dialog is being closed. |
351 | * Some apps might want to remove the highlighted text when this happens. |
352 | * Apps without support for "Find Next" can also do m_find->deleteLater() |
353 | * to terminate the find operation. |
354 | */ |
355 | void dialogClosed(); |
356 | |
357 | protected: |
358 | |
359 | QWidget* parentWidget() const; |
360 | QWidget* dialogsParent() const; |
361 | |
362 | private: |
363 | friend class KReplace; |
364 | friend class KReplacePrivate; |
365 | |
366 | struct Private; |
367 | Private* const d; |
368 | |
369 | Q_PRIVATE_SLOT( d, void _k_slotFindNext() ) |
370 | Q_PRIVATE_SLOT( d, void _k_slotDialogClosed() ) |
371 | }; |
372 | |
373 | Q_DECLARE_OPERATORS_FOR_FLAGS(KFind::SearchOptions) |
374 | |
375 | #endif |
376 | |