File indexing completed on 2024-09-15 09:27:38
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2001 S.R. Haque <srhaque@iee.org>. 0004 SPDX-FileCopyrightText: 2002 David Faure <david@mandrakesoft.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-only 0007 */ 0008 0009 #ifndef KFIND_H 0010 #define KFIND_H 0011 0012 #include "ktextwidgets_export.h" 0013 0014 #include <QObject> 0015 #include <memory> 0016 0017 class QDialog; 0018 class KFindPrivate; 0019 0020 /** 0021 * @class KFind kfind.h <KFind> 0022 * 0023 * @brief A generic implementation of the "find" function. 0024 * 0025 * @author S.R.Haque <srhaque@iee.org>, David Faure <faure@kde.org>, 0026 * Arend van Beelen jr. <arend@auton.nl> 0027 * 0028 * \b Detail: 0029 * 0030 * This class includes prompt handling etc. Also provides some 0031 * static functions which can be used to create custom behavior 0032 * instead of using the class directly. 0033 * 0034 * \b Example: 0035 * 0036 * To use the class to implement a complete find feature: 0037 * 0038 * In the slot connected to the find action, after using KFindDialog: 0039 * \code 0040 * 0041 * // This creates a find-next-prompt dialog if needed. 0042 * m_find = new KFind(pattern, options, this); 0043 * 0044 * // Connect textFound() signal to code which handles highlighting of found text. 0045 * connect(m_find, &KFind::textFound, this, [this](const QString &text, int matchingIndex, int matchedLength) { 0046 * slotHighlight(text, matchingIndex, matchedLength); 0047 * })); 0048 * // Connect findNext signal - called when pressing the button in the dialog 0049 * connect(m_find, SIGNAL(findNext()), 0050 * this, SLOT(slotFindNext())); 0051 * \endcode 0052 * 0053 * Then initialize the variables determining the "current position" 0054 * (to the cursor, if the option FromCursor is set, 0055 * to the beginning of the selection if the option SelectedText is set, 0056 * and to the beginning of the document otherwise). 0057 * Initialize the "end of search" variables as well (end of doc or end of selection). 0058 * Swap begin and end if FindBackwards. 0059 * Finally, call slotFindNext(); 0060 * 0061 * \code 0062 * void slotFindNext() 0063 * { 0064 * KFind::Result res = KFind::NoMatch; 0065 * while (res == KFind::NoMatch && <position not at end>) { 0066 * if (m_find->needData()) 0067 * m_find->setData(<current text fragment>); 0068 * 0069 * // Let KFind inspect the text fragment, and display a dialog if a match is found 0070 * res = m_find->find(); 0071 * 0072 * if (res == KFind::NoMatch) { 0073 * <Move to the next text fragment, honoring the FindBackwards setting for the direction> 0074 * } 0075 * } 0076 * 0077 * if (res == KFind::NoMatch) // i.e. at end 0078 * <Call either m_find->displayFinalDialog(); m_find->deleteLater(); m_find = nullptr; 0079 * or if (m_find->shouldRestart()) { reinit (w/o FromCursor); m_find->resetCounts(); slotFindNext(); } 0080 * else { m_find->closeFindNextDialog(); }> 0081 * } 0082 * \endcode 0083 * 0084 * Don't forget to delete m_find in the destructor of your class, 0085 * unless you gave it a parent widget on construction. 0086 * 0087 * This implementation allows to have a "Find Next" action, which resumes the 0088 * search, even if the user closed the "Find Next" dialog. 0089 * 0090 * A "Find Previous" action can simply switch temporarily the value of 0091 * FindBackwards and call slotFindNext() - and reset the value afterwards. 0092 */ 0093 class KTEXTWIDGETS_EXPORT KFind : public QObject 0094 { 0095 Q_OBJECT 0096 0097 public: 0098 /** 0099 * @see SearchOptions 0100 */ 0101 enum Options { 0102 WholeWordsOnly = 1, ///< Match whole words only. 0103 FromCursor = 2, ///< Start from current cursor position. 0104 SelectedText = 4, ///< Only search selected area. 0105 CaseSensitive = 8, ///< Consider case when matching. 0106 FindBackwards = 16, ///< Go backwards. 0107 RegularExpression = 32, ///< Interpret the pattern as a regular expression. 0108 FindIncremental = 64, ///< Find incremental. 0109 // Note that KReplaceDialog uses 256 and 512 0110 // User extensions can use boolean options above this value. 0111 MinimumUserOption = 65536, ///< user options start with this bit 0112 }; 0113 /** 0114 * Stores a combination of #Options values. 0115 */ 0116 Q_DECLARE_FLAGS(SearchOptions, Options) 0117 0118 /** 0119 * Only use this constructor if you don't use KFindDialog, or if 0120 * you use it as a modal dialog. 0121 */ 0122 KFind(const QString &pattern, long options, QWidget *parent); 0123 0124 /** 0125 * This is the recommended constructor if you also use KFindDialog (non-modal). 0126 * You should pass the pointer to it here, so that when a message box 0127 * appears it has the right parent. Don't worry about deletion, KFind 0128 * will notice if the find dialog is closed. 0129 */ 0130 KFind(const QString &pattern, long options, QWidget *parent, QWidget *findDialog); 0131 ~KFind() override; 0132 0133 enum Result { 0134 NoMatch, 0135 Match, 0136 }; 0137 0138 /** 0139 * @return true if the application must supply a new text fragment 0140 * It also means the last call returned "NoMatch". But by storing this here 0141 * the application doesn't have to store it in a member variable (between 0142 * calls to slotFindNext()). 0143 */ 0144 bool needData() const; 0145 0146 /** 0147 * Call this when needData returns true, before calling find(). 0148 * @param data the text fragment (line) 0149 * @param startPos if set, the index at which the search should start. 0150 * This is only necessary for the very first call to setData usually, 0151 * for the 'find in selection' feature. A value of -1 (the default value) 0152 * means "process all the data", i.e. either 0 or data.length()-1 depending 0153 * on FindBackwards. 0154 */ 0155 void setData(const QString &data, int startPos = -1); 0156 0157 /** 0158 * Call this when needData returns true, before calling find(). The use of 0159 * ID's is especially useful if you're using the FindIncremental option. 0160 * @param id the id of the text fragment 0161 * @param data the text fragment (line) 0162 * @param startPos if set, the index at which the search should start. 0163 * This is only necessary for the very first call to setData usually, 0164 * for the 'find in selection' feature. A value of -1 (the default value) 0165 * means "process all the data", i.e. either 0 or data.length()-1 depending 0166 * on FindBackwards. 0167 */ 0168 void setData(int id, const QString &data, int startPos = -1); 0169 0170 /** 0171 * Walk the text fragment (e.g. in a text-processor line or spreadsheet 0172 * cell ...etc) looking for matches. 0173 * For each match, emits the textFound() signal and displays the find-again 0174 * dialog to ask if the user wants to find the same text again. 0175 */ 0176 Result find(); 0177 0178 /** 0179 * Return the current options. 0180 * 0181 * Warning: this is usually the same value as the one passed to the constructor, 0182 * but options might change _during_ the replace operation: 0183 * e.g. the "All" button resets the PromptOnReplace flag. 0184 * 0185 * @see KFind::Options 0186 */ 0187 long options() const; 0188 0189 /** 0190 * Set new options. Usually this is used for setting or clearing the 0191 * FindBackwards options. 0192 * 0193 * @see KFind::Options 0194 */ 0195 virtual void setOptions(long options); 0196 0197 /** 0198 * @return the pattern we're currently looking for 0199 */ 0200 QString pattern() const; 0201 0202 /** 0203 * Change the pattern we're looking for 0204 */ 0205 void setPattern(const QString &pattern); 0206 0207 /** 0208 * Returns the number of matches found (i.e. the number of times the textFound() 0209 * signal was emitted). 0210 * If 0, can be used in a dialog box to tell the user "no match was found". 0211 * The final dialog does so already, unless you used setDisplayFinalDialog(false). 0212 */ 0213 int numMatches() const; 0214 0215 /** 0216 * Call this to reset the numMatches count 0217 * (and the numReplacements count for a KReplace). 0218 * Can be useful if reusing the same KReplace for different operations, 0219 * or when restarting from the beginning of the document. 0220 */ 0221 virtual void resetCounts(); 0222 0223 /** 0224 * Virtual method, which allows applications to add extra checks for 0225 * validating a candidate match. It's only necessary to reimplement this 0226 * if the find dialog extension has been used to provide additional 0227 * criteria. 0228 * 0229 * @param text The current text fragment 0230 * @param index The starting index where the candidate match was found 0231 * @param matchedlength The length of the candidate match 0232 */ 0233 virtual bool validateMatch(const QString &text, int index, int matchedlength); 0234 0235 /** 0236 * Returns true if we should restart the search from scratch. 0237 * Can ask the user, or return false (if we already searched the whole document). 0238 * 0239 * @param forceAsking set to true if the user modified the document during the 0240 * search. In that case it makes sense to restart the search again. 0241 * 0242 * @param showNumMatches set to true if the dialog should show the number of 0243 * matches. Set to false if the application provides a "find previous" action, 0244 * in which case the match count will be erroneous when hitting the end, 0245 * and we could even be hitting the beginning of the document (so not all 0246 * matches have even been seen). 0247 */ 0248 virtual bool shouldRestart(bool forceAsking = false, bool showNumMatches = true) const; 0249 0250 #if KTEXTWIDGETS_ENABLE_DEPRECATED_SINCE(5, 70) 0251 /** 0252 * Search the given string, and returns whether a match was found. If one is, 0253 * the length of the string matched is also returned. 0254 * 0255 * A performance optimised version of the function is provided for use 0256 * with regular expressions. 0257 * 0258 * @param text The string to search. 0259 * @param pattern The pattern to look for. 0260 * @param index The starting index into the string. 0261 * @param options The options to use. 0262 * @param matchedlength The length of the string that was matched 0263 * @return The index at which a match was found, or -1 if no match was found. 0264 * 0265 * @deprecated Since 5.70 0266 */ 0267 KTEXTWIDGETS_DEPRECATED_VERSION(5, 0268 70, 0269 "Use find(const QString &text, const QString &pattern, int index, long options, \ 0270 int *matchedLength, QRegularExpressionMatch *rmatch).") 0271 static int find(const QString &text, const QString &pattern, int index, long options, int *matchedlength); 0272 #endif 0273 0274 #if KTEXTWIDGETS_ENABLE_DEPRECATED_SINCE(5, 70) 0275 /** 0276 * @deprecated Since 5.70, for lack of direct use 0277 */ 0278 KTEXTWIDGETS_DEPRECATED_VERSION_BELATED(5, 0279 71, 0280 5, 0281 70, 0282 "Use find(const QString &, const QString &, int, long, \ 0283 int *, QRegularExpressionMatch *).") 0284 static int find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedlength); 0285 #endif 0286 0287 /** 0288 * Search @p text for @p pattern. If a match is found, the length of the matched 0289 * string will be stored in @p matchedLength and the index of the matched string 0290 * will be returned. If no match is found -1 is returned. 0291 * 0292 * If the KFind::RegularExpression flag is set, the @p pattern will be iterpreted 0293 * as a regular expression (using QRegularExpression). 0294 * 0295 * @note Unicode support is always enabled (by setting the QRegularExpression::UseUnicodePropertiesOption flag). 0296 * 0297 * @param text The string to search in 0298 * @param pattern The pattern to search for 0299 * @param index The index in @p text from which to start the search 0300 * @param options The options to use 0301 * @param matchedlength If there is a match, its length will be stored in this parameter 0302 * @param rmatch If there is a regular expression match (implies that the KFind::RegularExpression 0303 * flag is set) and @p rmatch is not a nullptr the match result will be stored 0304 * in this QRegularExpressionMatch object 0305 * @return The index at which a match was found otherwise -1 0306 * 0307 * @since 5.70 0308 */ 0309 static int find(const QString &text, const QString &pattern, int index, long options, int *matchedLength, QRegularExpressionMatch *rmatch); 0310 0311 /** 0312 * Displays the final dialog saying "no match was found", if that was the case. 0313 * Call either this or shouldRestart(). 0314 */ 0315 virtual void displayFinalDialog() const; 0316 0317 /** 0318 * Return (or create) the dialog that shows the "find next?" prompt. 0319 * Usually you don't need to call this. 0320 * One case where it can be useful, is when the user selects the "Find" 0321 * menu item while a find operation is under way. In that case, the 0322 * program may want to call setActiveWindow() on that dialog. 0323 */ 0324 QDialog *findNextDialog(bool create = false); 0325 0326 /** 0327 * Close the "find next?" dialog. The application should do this when 0328 * the last match was hit. If the application deletes the KFind, then 0329 * "find previous" won't be possible anymore. 0330 * 0331 * IMPORTANT: you should also call this if you are using a non-modal 0332 * find dialog, to tell KFind not to pop up its own dialog. 0333 */ 0334 void closeFindNextDialog(); 0335 0336 /** 0337 * @return the current matching index (or -1). 0338 * Same as the matchingIndex parameter passed to the textFound() signal. 0339 * You usually don't need to use this, except maybe when updating the current data, 0340 * so you need to call setData(newData, index()). 0341 */ 0342 int index() const; 0343 0344 Q_SIGNALS: 0345 #if KTEXTWIDGETS_ENABLE_DEPRECATED_SINCE(5, 81) 0346 /** 0347 * Connect to this signal to implement highlighting of found text during the find 0348 * operation. 0349 * 0350 * If you've set data with setData(id, text), use the signal highlight(id, 0351 * matchingIndex, matchedLength) 0352 * 0353 * WARNING: If you're using the FindIncremental option, the text argument 0354 * passed by this signal is not necessarily the data last set through 0355 * setData(), but can also be an earlier set data block. 0356 * 0357 * @see setData() 0358 * 0359 * @deprecated since 5.81, use the KFind::textFound(const QString &, int, int) signal instead. 0360 */ 0361 KTEXTWIDGETS_DEPRECATED_VERSION(5, 81, "Use the KFind::textFound(const QString &, int, int) signal instead.") 0362 void highlight(const QString &text, int matchingIndex, int matchedLength); // clazy:exclude=overloaded-signal 0363 #endif 0364 0365 /** 0366 * Connect to this signal to implement highlighting of found text during the find 0367 * operation. 0368 * 0369 * If you've set data with setData(id, text), use the textFoundAtId(int, int, int) signal. 0370 * 0371 * WARNING: If you're using the FindIncremental option, the text argument 0372 * passed by this signal is not necessarily the data last set through 0373 * setData(), but can also be an earlier set data block. 0374 * 0375 * @see setData() 0376 * 0377 * @since 5.81 0378 */ 0379 void textFound(const QString &text, int matchingIndex, int matchedLength); 0380 0381 #if KTEXTWIDGETS_ENABLE_DEPRECATED_SINCE(5, 81) 0382 /** 0383 * Connect to this signal to implement highlighting of found text during the find 0384 * operation. 0385 * 0386 * Use this signal if you've set your data with setData(id, text), otherwise 0387 * use the textFound(text, matchingIndex, matchedLength) signal. 0388 * 0389 * WARNING: If you're using the FindIncremental option, the id argument 0390 * passed by this signal is not necessarily the id of the data last set 0391 * through setData(), but can also be of an earlier set data block. 0392 * 0393 * @see setData() 0394 * 0395 * @deprecated since 5.81, use the KFind::textFoundAtId(int id, int matchingIndex, int matchedLength) signal instead. 0396 */ 0397 KTEXTWIDGETS_DEPRECATED_VERSION(5, 81, "Use the KFind::textFoundAtId(int id, int matchingIndex, int matchedLength) signal instead.") 0398 void highlight(int id, int matchingIndex, int matchedLength); // clazy:exclude=overloaded-signal 0399 #endif 0400 0401 /** 0402 * Connect to this signal to implement highlighting of found text during 0403 * the find operation. 0404 * 0405 * Use this signal if you've set your data with setData(id, text), 0406 * otherwise use the textFound(text, matchingIndex, matchedLength) signal. 0407 * 0408 * WARNING: If you're using the FindIncremental option, the id argument 0409 * passed by this signal is not necessarily the id of the data last set 0410 * through setData(), but can also be of an earlier set data block. 0411 * 0412 * @see setData() 0413 * 0414 * @since 5.81 0415 */ 0416 void textFoundAtId(int id, int matchingIndex, int matchedLength); 0417 0418 // ## TODO docu 0419 // findprevious will also emit findNext, after temporarily switching the value 0420 // of FindBackwards 0421 void findNext(); 0422 0423 /** 0424 * Emitted when the options have changed. 0425 * This can happen e.g. with "Replace All", or if our 'find next' dialog 0426 * gets a "find previous" one day. 0427 */ 0428 void optionsChanged(); 0429 0430 /** 0431 * Emitted when the 'find next' dialog is being closed. 0432 * Some apps might want to remove the highlighted text when this happens. 0433 * Apps without support for "Find Next" can also do m_find->deleteLater() 0434 * to terminate the find operation. 0435 */ 0436 void dialogClosed(); 0437 0438 protected: 0439 QWidget *parentWidget() const; 0440 QWidget *dialogsParent() const; 0441 0442 protected: 0443 KTEXTWIDGETS_NO_EXPORT KFind(KFindPrivate &dd, const QString &pattern, long options, QWidget *parent); 0444 KTEXTWIDGETS_NO_EXPORT KFind(KFindPrivate &dd, const QString &pattern, long options, QWidget *parent, QWidget *findDialog); 0445 0446 private: 0447 friend class KReplace; 0448 Q_DECLARE_PRIVATE_D(d, KFind) 0449 std::unique_ptr<class KFindPrivate> const d; 0450 // KF6 TODO: change private d to protected d_ptr, use normal Q_DECLARE_PRIVATE, remove friend 0451 }; 0452 0453 Q_DECLARE_OPERATORS_FOR_FLAGS(KFind::SearchOptions) 0454 0455 #endif