diff options
Diffstat (limited to 'kutils/kfind.h')
-rw-r--r-- | kutils/kfind.h | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/kutils/kfind.h b/kutils/kfind.h new file mode 100644 index 000000000..a2159f6b1 --- /dev/null +++ b/kutils/kfind.h @@ -0,0 +1,426 @@ +/* + Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. + Copyright (C) 2002, David Faure <david@mandrakesoft.com> + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KFIND_H +#define KFIND_H + +#include <kdialogbase.h> +#include <qrect.h> + +/** + * @ingroup main + * @ingroup findreplace + * @brief A generic implementation of the "find" function. + * + * @author S.R.Haque <srhaque@iee.org>, David Faure <faure@kde.org>, + * Arend van Beelen jr. <arend@auton.nl> + * + * \b Detail: + * + * This class includes prompt handling etc. Also provides some + * static functions which can be used to create custom behavior + * instead of using the class directly. + * + * \b Example: + * + * To use the class to implement a complete find feature: + * + * In the slot connected to the find action, after using KFindDialog: + * \code + * + * // This creates a find-next-prompt dialog if needed. + * m_find = new KFind(pattern, options, this); + * + * // Connect highlight signal to code which handles highlighting + * // of found text. + * connect( m_find, SIGNAL( highlight( const QString &, int, int ) ), + * this, SLOT( slotHighlight( const QString &, int, int ) ) ); + * // Connect findNext signal - called when pressing the button in the dialog + * connect( m_find, SIGNAL( findNext() ), + * this, SLOT( slotFindNext() ) ); + * \endcode + * + * If you are using a non-modal find dialog (the recommended new way + * in KDE-3.2), you should call right away m_find->closeFindNextDialog(). + * + * Then initialize the variables determining the "current position" + * (to the cursor, if the option FromCursor is set, + * to the beginning of the selection if the option SelectedText is set, + * and to the beginning of the document otherwise). + * Initialize the "end of search" variables as well (end of doc or end of selection). + * Swap begin and end if FindBackwards. + * Finally, call slotFindNext(); + * + * \code + * void slotFindNext() + * { + * KFind::Result res = KFind::NoMatch; + * while ( res == KFind::NoMatch && <position not at end> ) { + * if ( m_find->needData() ) + * m_find->setData( <current text fragment> ); + * + * // Let KFind inspect the text fragment, and display a dialog if a match is found + * res = m_find->find(); + * + * if ( res == KFind::NoMatch ) { + * <Move to the next text fragment, honoring the FindBackwards setting for the direction> + * } + * } + * + * if ( res == KFind::NoMatch ) // i.e. at end + * <Call either m_find->displayFinalDialog(); delete m_find; m_find = 0L; + * or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); } + * else { m_find->closeFindNextDialog(); }> + * } + * \endcode + * + * Don't forget to delete m_find in the destructor of your class, + * unless you gave it a parent widget on construction. + * + * This implementation allows to have a "Find Next" action, which resumes the + * search, even if the user closed the "Find Next" dialog. + * + * A "Find Previous" action can simply switch temporarily the value of + * FindBackwards and call slotFindNext() - and reset the value afterwards. + */ +class KUTILS_EXPORT KFind : + public QObject +{ + Q_OBJECT + +public: + + /** + * Only use this constructor if you don't use KFindDialog, or if + * you use it as a modal dialog. + * @param pattern The pattern to look for. + * @param options Options for the find dialog. @see KFindDialog. + * @param parent The parent widget. + */ + KFind(const QString &pattern, long options, QWidget *parent); + + /** + * This is the recommended constructor if you also use KFindDialog (non-modal). + * You should pass the pointer to it here, so that when a message box + * appears it has the right parent. Don't worry about deletion, KFind + * will notice if the find dialog is closed. + * @param pattern The pattern to look for. + * @param options Options for the find dialog. @see KFindDialog. + * @param parent The parent widget. + * @param findDialog A pointer to the KFindDialog object. + */ + KFind(const QString &pattern, long options, QWidget *parent, QWidget* findDialog); + + /** + * Destructor. + */ + virtual ~KFind(); + + /** + * Result enum. Holds information if the find was successful. + */ + enum Result { + NoMatch, ///< No match was found. + Match ///< A match was found. + }; + + /** + * @return @c true if the application must supply a new text fragment + * It also means the last call returned "NoMatch". But by storing this here + * the application doesn't have to store it in a member variable (between + * calls to slotFindNext()). + */ + bool needData() const; + + /** + * Call this when needData returns @c true, before calling find(). + * @param data the text fragment (line) + * @param startPos if set, the index at which the search should start. + * This is only necessary for the very first call to setData usually, + * for the 'find in selection' feature. A value of -1 (the default value) + * means "process all the data", i.e. either 0 or data.length()-1 depending + * on FindBackwards. + */ + void setData( const QString& data, int startPos = -1 ); + + /** + * Call this when needData returns @c true, before calling find(). The use of + * ID's is especially useful if you're using the FindIncremental option. + * @param id the id of the text fragment + * @param data the text fragment (line) + * @param startPos if set, the index at which the search should start. + * This is only necessary for the very first call to setData usually, + * for the 'find in selection' feature. A value of -1 (the default value) + * means "process all the data", i.e. either 0 or data.length()-1 depending + * on FindBackwards. + * + * @since 3.3 + */ + void setData( int id, const QString& data, int startPos = -1 ); + + /** + * Walk the text fragment (e.g. text-processor line, kspread cell) looking for matches. + * For each match, emits the highlight() signal and displays the find-again dialog + * proceeding. + * @return Whether or not there has been a match. + */ + Result find(); + + /** + * Return the current options. + * + * Warning: this is usually the same value as the one passed to the constructor, + * but options might change _during_ the replace operation: + * e.g. the "All" button resets the PromptOnReplace flag. + * + * @return The current options. @see KFindDialog. + */ + long options() const { return m_options; } + + /** + * Set new options. Usually this is used for setting or clearing the + * FindBackwards options. + * + * @see KFindDialog. + */ + virtual void setOptions( long options ); + + /** + * @return the pattern we're currently looking for + */ + QString pattern() const { return m_pattern; } + + /** + * Change the pattern we're looking for + * @param pattern The new pattern. + */ + void setPattern( const QString& pattern ); + + /** + * Return the number of matches found (i.e. the number of times + * the highlight signal was emitted). + * If 0, can be used in a dialog box to tell the user "no match was found". + * The final dialog does so already, unless you used setDisplayFinalDialog(false). + * @return The number of matches. + */ + int numMatches() const { return m_matches; } + + /** + * Call this to reset the numMatches count + * (and the numReplacements count for a KReplace). + * Can be useful if reusing the same KReplace for different operations, + * or when restarting from the beginning of the document. + */ + virtual void resetCounts() { m_matches = 0; } + + /** + * Virtual method, which allows applications to add extra checks for + * validating a candidate match. It's only necessary to reimplement this + * if the find dialog extension has been used to provide additional + * criterias. + * + * @param text The current text fragment + * @param index The starting index where the candidate match was found + * @param matchedlength The length of the candidate match + */ + virtual bool validateMatch( const QString & text, int index, int matchedlength ) { + Q_UNUSED(text); Q_UNUSED(index); Q_UNUSED(matchedlength); return true; } + + /** + * Returns @c true if we should restart the search from scratch. + * Can ask the user, or return @c false (if we already searched the whole document). + * + * @param forceAsking set to @c true if the user modified the document during the + * search. In that case it makes sense to restart the search again. + * + * @param showNumMatches set to @c true if the dialog should show the number of + * matches. Set to @c false if the application provides a "find previous" action, + * in which case the match count will be erroneous when hitting the end, + * and we could even be hitting the beginning of the document (so not all + * matches have even been seen). + * + * @return @c true, if the search should be restarted. + */ + virtual bool shouldRestart( bool forceAsking = false, bool showNumMatches = true ) const; + + /** + * Search the given string, and returns whether a match was found. If one is, + * the length of the string matched is also returned. + * + * A performance optimised version of the function is provided for use + * with regular expressions. + * + * @param text The string to search. + * @param pattern The pattern to look for. + * @param index The starting index into the string. + * @param options The options to use. + * @param matchedlength The length of the string that was matched + * @return The index at which a match was found, or -1 if no match was found. + */ + static int find( const QString &text, const QString &pattern, int index, long options, int *matchedlength ); + + /** + * Search the given regular expression, and returns whether a match was found. If one is, + * the length of the matched string is also returned. + * + * Another version of the function is provided for use with strings. + * + * @param text The string to search. + * @param pattern The regular expression pattern to look for. + * @param index The starting index into the string. + * @param options The options to use. + * @param matchedlength The length of the string that was matched + * @return The index at which a match was found, or -1 if no match was found. + */ + static int find( const QString &text, const QRegExp &pattern, int index, long options, int *matchedlength ); + + /** + * Displays the final dialog saying "no match was found", if that was the case. + * Call either this or shouldRestart(). + */ + virtual void displayFinalDialog() const; + + /** + * Return (or create) the dialog that shows the "find next?" prompt. + * Usually you don't need to call this. + * One case where it can be useful, is when the user selects the "Find" + * menu item while a find operation is under way. In that case, the + * program may want to call setActiveWindow() on that dialog. + * @return The find next dialog. + */ + KDialogBase* findNextDialog( bool create = false ); + + /** + * Close the "find next?" dialog. The application should do this when + * the last match was hit. If the application deletes the KFind, then + * "find previous" won't be possible anymore. + * + * IMPORTANT: you should also call this if you are using a non-modal + * find dialog, to tell KFind not to pop up its own dialog. + */ + void closeFindNextDialog(); + + /** + * @return the current matching index ( or -1 ). + * Same as the matchingIndex parameter passed to highlight. + * You usually don't need to use this, except maybe when updating the current data, + * so you need to call setData( newData, index() ). + * @since 3.2 + */ + int index() const; + +signals: + + /** + * Connect to this signal to implement highlighting of found text during the find + * operation. + * + * If you've set data with setData(id, text), use the signal highlight(id, + * matchingIndex, matchedLength) + * + * @warning If you're using the FindIncremental option, the text argument + * passed by this signal is not necessarily the data last set through + * setData(), but can also be an earlier set data block. + * + * @param text The found text. + * @param matchingIndex The index of the found text's occurrence. + * @param matchedLength The length of the matched text. + * @see setData() + */ + void highlight(const QString &text, int matchingIndex, int matchedLength); + + /** + * Connect to this signal to implement highlighting of found text during the find + * operation. + * + * Use this signal if you've set your data with setData(id, text), otherwise + * use the signal with highlight(text, matchingIndex, matchedLength). + * + * @warning If you're using the FindIncremental option, the id argument + * passed by this signal is not necessarily the id of the data last set + * through setData(), but can also be of an earlier set data block. + * + * @param id The ID of the text fragment, as used in setData(). + * @param matchingIndex The index of the found text's occurrence. + * @param matchedLength The length of the matched text. + * @see setData() + * + * @since 3.3 + */ + void highlight(int id, int matchingIndex, int matchedLength); + + // ## TODO docu + // findprevious will also emit findNext, after temporarily switching the value + // of FindBackwards + void findNext(); + + /** + * Emitted when the options have changed. + * This can happen e.g. with "Replace All", or if our 'find next' dialog + * gets a "find previous" one day. + */ + void optionsChanged(); + + /** + * Emitted when the 'find next' dialog is being closed. + * Some apps might want to remove the highlighted text when this happens. + * Apps without support for "Find Next" can also do m_find->deleteLater() + * to terminate the find operation. + */ + void dialogClosed(); + +protected: + + QWidget* parentWidget() const { return (QWidget *)parent(); } + QWidget* dialogsParent() const; + +protected slots: + + void slotFindNext(); + void slotDialogClosed(); + +private: + void init( const QString& pattern ); + void startNewIncrementalSearch(); + + static bool isInWord( QChar ch ); + static bool isWholeWords( const QString &text, int starts, int matchedLength ); + + friend class KReplace; + + + QString m_pattern; + QRegExp *m_regExp; + KDialogBase* m_dialog; + long m_options; + unsigned m_matches; + + QString m_text; // the text set by setData + int m_index; + int m_matchedLength; + bool m_dialogClosed; + bool m_lastResult; + + // Binary compatible extensibility. + struct Private; + Private *d; +}; + +#endif |