diff --git a/doc/api/qtcreator-api.qdoc b/doc/api/qtcreator-api.qdoc index aca770d8b7b118a715642e42ee274ae5477b9b3d..f22ef491fee124990c9cce5a20d2665beed5c01b 100644 --- a/doc/api/qtcreator-api.qdoc +++ b/doc/api/qtcreator-api.qdoc @@ -142,7 +142,7 @@ \row \o Add a find filter for the find dialog. \o Implement any kind of search term based search. - \o \l{Find::IFindFilter}, \l{Find::SearchResultWindow}, \l{Find::ResultWindowItem} + \o \l{Find::IFindFilter}, \l{Find::SearchResultWindow} \row \o Add support for the find tool bar to a widget. diff --git a/src/plugins/find/find.pro b/src/plugins/find/find.pro index bfdb979332a06cab9a1db6e9809b52d23b87c5a1..d0f67a405c27e05e34a3fb257bdab4f9e2f00d70 100644 --- a/src/plugins/find/find.pro +++ b/src/plugins/find/find.pro @@ -27,7 +27,8 @@ SOURCES += findtoolwindow.cpp \ searchresulttreeitems.cpp \ searchresulttreemodel.cpp \ searchresulttreeview.cpp \ - searchresultwindow.cpp + searchresultwindow.cpp \ + ifindfilter.cpp FORMS += findwidget.ui \ finddialog.ui RESOURCES += find.qrc diff --git a/src/plugins/find/ifindfilter.cpp b/src/plugins/find/ifindfilter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2eef03065a5db62027ae76ed618a9a9e29af0bf --- /dev/null +++ b/src/plugins/find/ifindfilter.cpp @@ -0,0 +1,174 @@ +#include "ifindfilter.h" + +/*! + \class Find::IFindFilter + \brief The IFindFilter class is the base class for find implementations + that are invoked via the \gui{Edit -> Find/Replace -> Advanced Find} + dialog. + + Implementations of this class add an additional "Scope" to the Advanced + Find dialog. That can be any search that requires the user to provide + a text based search term (potentially with find flags like + searching case sensitively or using regular expressions). Existing + scopes are e.g. "All Projects" (i.e. search in all files in all projects) + and "Files on File System" where the user provides a directory and file + patterns to search. + + To make your find scope available to the user, you need to implement this + class, and register an instance of your subclass in the plugin manager. + + A common way to present the search results to the user, is to use the + shared \gui{Search Results} panel. + + If you want to implement a find filter that is doing a file based text + search, you should use Find::BaseFileFind, which already implements all + the details for this kind of search, only requiring you to provide an + iterator over the file names of the files that should be searched. + + If you want to implement a more specialized find filter, you'll need + to + \list + \o Start your search in a separate thread + \o Make this known to the Core::ProgressManager, for a progress bar + and the ability to cancel the search + \o Interface with the shared \gui{Search Results} panel, to show + the search results, handle the event that the user click on one + of the search result items, and possible handle a global replace + of all or some of the search result items. + \endlist + + Luckily QtConcurrent and the search result panel provide the frameworks + that make it relatively easy to implement, + while ensuring a common way for the user. + + The common pattern is roughly this: + + Implement the actual search within a QtConcurrent based method, i.e. + a method that takes a \c{QFutureInterface<MySearchResult> &future} + as the first parameter and the other information needed for the search + as additional parameters. It should set useful progress information + on the QFutureInterface, regularly check for \c{future.isPaused()} + and \c{future.isCanceled()}, and report the search results + (possibly in chunks) via \c{future.reportResult}. + + In the find filter's find/replaceAll method, get the shared + \gui{Search Results} window, initiate a new search and connect the + signals for handling selection of results and the replace action + (see the Find::SearchResultWindow class for details). + Start your search implementation via the corresponding QtConcurrent + methods. Add the returned QFuture object to the Core::ProgressManager. + Use a QFutureWatcher on the returned QFuture object to receive a signal + when your search implementation reports search results, and add these + to the shared \gui{Search Results} window. +*/ + +/*! + \fn IFindFilter::~IFindFilter() + \internal +*/ + +/*! + \fn QString IFindFilter::id() const + \brief Unique string identifier for this find filter. + + Usually should be something like "MyPlugin.MyFindFilter". +*/ + +/*! + \fn QString IFindFilter::displayName() const + \brief The name of the find filter/scope as presented to the user. + + This is the name that e.g. appears in the scope selection combo box. + Always return a translatable string (i.e. use tr() for the return value). +*/ + +/*! + \fn bool IFindFilter::isEnabled() const + \brief Returns if the user should be able to select this find filter + at the moment. + + This is used e.g. for the "Current Projects" scope - if the user hasn't + opened a project, the scope is disabled. + + \sa changed() +*/ + +/*! + \fn QKeySequence IFindFilter::defaultShortcut() const + \brief Returns the shortcut that can be used to open the advanced find + dialog with this filter/scope preselected. + + Usually return an empty shortcut here, the user can still choose and + assign a specific shortcut to this find scope via the preferences. +*/ + +/*! + \fn bool IFindFilter::isReplaceSupported() const + \brief Returns if the find filter supports search and replace. + + The default value is false, override this method to return true, if + your find filter supports global search and replace. +*/ + +/*! + \fn void IFindFilter::findAll(const QString &txt, QTextDocument::FindFlags findFlags) + \brief This method is called when the user selected this find scope and + initiated a search. + + You should start a thread which actually performs the search for \a txt + using the given \a findFlags + (add it to Core::ProgressManager for a progress bar!) and presents the + search results to the user (using the \gui{Search Results} output pane). + For more information see the descriptions of this class, + Core::ProgressManager, and Find::SearchResultWindow. + + \sa replaceAll() + \sa Core::ProgressManager + \sa Find::SearchResultWindow +*/ + +/*! + \fn void IFindFilter::replaceAll(const QString &txt, QTextDocument::FindFlags findFlags) + \brief Override this method if you want to support search and replace. + + This method is called when the user selected this find scope and + initiated a search and replace. + The default implementation does nothing. + + You should start a thread which actually performs the search for \a txt + using the given \a findFlags + (add it to Core::ProgressManager for a progress bar!) and presents the + search results to the user (using the \gui{Search Results} output pane). + For more information see the descriptions of this class, + Core::ProgressManager, and Find::SearchResultWindow. + + \sa findAll() + \sa Core::ProgressManager + \sa Find::SearchResultWindow +*/ + +/*! + \fn QWidget *IFindFilter::createConfigWidget() + \brief Return a widget that contains additional controls for options + for this find filter. + + The widget will be shown below the common options in the Advanced Find + dialog. It will be reparented and deleted by the find plugin. +*/ + +/*! + \fn void IFindFilter::writeSettings(QSettings *settings) + \brief Called at shutdown to write the state of the additional options + for this find filter to the \a settings. +*/ + +/*! + \fn void IFindFilter::readSettings(QSettings *settings) + \brief Called at startup to read the state of the additional options + for this find filter from the \a settings. +*/ + +/*! + \fn void IFindFilter::changed() + \brief Signals that the enabled state of this find filter has changed. +*/ diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp index 4ee0eea4bc13daa9fb031596b64b1c56d79c6ff9..40ebf6f209d6dc3c85c8b00990d2a9a02eafff39 100644 --- a/src/plugins/find/searchresultwindow.cpp +++ b/src/plugins/find/searchresultwindow.cpp @@ -211,8 +211,85 @@ namespace Internal { using namespace Find::Internal; +/*! + \enum Find::SearchResultWindow::SearchMode + Specifies if a search should show the replace UI or not. + + \value SearchOnly + The search doesn't support replace. + \value SearchAndReplace + The search supports replace, so show the UI for it. +*/ + +/*! + \class Find::SearchResult + \brief Reports user interaction like activation of a search result item. + + Whenever a new search is initiated via startNewSearch, an instance of this + class is returned to provide the initiator with the hooks for handling user + interaction. +*/ + +/*! + \fn void SearchResult::activated(const Find::SearchResultItem &item) + \brief Sent if the user activated (e.g. double-clicked) a search result + \a item. +*/ + +/*! + \fn void SearchResult::replaceButtonClicked(const QString &replaceText, const QList<Find::SearchResultItem> &checkedItems) + \brief Sent when the user initiated a replace, e.g. by pressing the replace + all button. + + The signal reports the text to use for replacement in \a replaceText, + and the list of search result items that were selected by the user + in \a checkedItems. + The handler of this signal should apply the replace only on the selected + items. +*/ + +/*! + \class Find::SearchResultWindow + \brief The SearchResultWindow class is the implementation of a commonly + shared \gui{Search Results} output pane. Use it to show search results + to a user. + + Whenever you want to show the user a list of search results, or want + to present UI for a global search and replace, use the single instance + of this class. + + Except for being an implementation of a output pane, the + SearchResultWindow has a few methods and one enum that allows other + plugins to show their search results and hook into the user actions for + selecting an entry and performing a global replace. + + Whenever you start a search, call startNewSearch(SearchMode) to initialize + the search result window. The parameter determines if the GUI for + replacing should be shown. + The method returns a SearchResult object that is your + hook into the signals from user interaction for this search. + When you produce search results, call addResults or addResult to add them + to the search result window. + After the search has finished call finishSearch to inform the search + result window about it. + + After that you get activated signals via your SearchResult instance when + the user selects a search result item, and, if you started the search + with the SearchAndReplace option, the replaceButtonClicked signal + when the user requests a replace. +*/ + +/*! + \fn QString SearchResultWindow::displayName() const + \internal +*/ + SearchResultWindow *SearchResultWindow::m_instance = 0; +/*! + \fn SearchResultWindow::SearchResultWindow() + \internal +*/ SearchResultWindow::SearchResultWindow() : d(new SearchResultWindowPrivate) { m_instance = this; @@ -263,6 +340,10 @@ SearchResultWindow::SearchResultWindow() : d(new SearchResultWindowPrivate) setShowReplaceUI(false); } +/*! + \fn SearchResultWindow::~SearchResultWindow() + \internal +*/ SearchResultWindow::~SearchResultWindow() { writeSettings(); @@ -274,21 +355,38 @@ SearchResultWindow::~SearchResultWindow() delete d; } +/*! + \fn SearchResultWindow *SearchResultWindow::instance() + \brief Returns the single shared instance of the Search Results window. +*/ SearchResultWindow *SearchResultWindow::instance() { return m_instance; } +/*! + \fn void SearchResultWindow::setTextToReplace(const QString &textToReplace) + \brief Sets the value in the UI element that allows the user to type + the text that should replace text in search results to \a textToReplace. +*/ void SearchResultWindow::setTextToReplace(const QString &textToReplace) { d->m_replaceTextEdit->setText(textToReplace); } +/*! + \fn QString SearchResultWindow::textToReplace() const + \brief Returns the text that should replace the text in search results. +*/ QString SearchResultWindow::textToReplace() const { return d->m_replaceTextEdit->text(); } +/*! + \fn void SearchResultWindow::setShowReplaceUI(bool show) + \internal +*/ void SearchResultWindow::setShowReplaceUI(bool show) { d->m_searchResultTreeView->model()->setShowReplaceUI(show); @@ -298,6 +396,10 @@ void SearchResultWindow::setShowReplaceUI(bool show) d->m_isShowingReplaceUI = show; } +/*! + \fn void SearchResultWindow::handleReplaceButton() + \internal +*/ void SearchResultWindow::handleReplaceButton() { QTC_ASSERT(d->m_currentSearch, return); @@ -307,6 +409,10 @@ void SearchResultWindow::handleReplaceButton() d->m_currentSearch->replaceButtonClicked(d->m_replaceTextEdit->text(), checkedItems()); } +/*! + \fn QList<SearchResultItem> SearchResultWindow::checkedItems() const + \internal +*/ QList<SearchResultItem> SearchResultWindow::checkedItems() const { QList<SearchResultItem> result; @@ -326,20 +432,42 @@ QList<SearchResultItem> SearchResultWindow::checkedItems() const return result; } +/*! + \fn void SearchResultWindow::visibilityChanged(bool) + \internal +*/ void SearchResultWindow::visibilityChanged(bool /*visible*/) { } +/*! + \fn QWidget *SearchResultWindow::outputWidget(QWidget *) + \internal +*/ QWidget *SearchResultWindow::outputWidget(QWidget *) { return d->m_widget; } +/*! + \fn QList<QWidget*> SearchResultWindow::toolBarWidgets() const + \internal +*/ QList<QWidget*> SearchResultWindow::toolBarWidgets() const { return QList<QWidget*>() << d->m_expandCollapseButton << d->m_replaceLabel << d->m_replaceTextEdit << d->m_replaceButton; } +/*! + \fn SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndReplace) + \brief Tells the search results window to start a new search. + + This will clear the contents of the previous search and initialize the UI + with regard to showing the replace UI or not (depending on the search mode + in \a searchOrSearchAndReplace). + Returns a SearchResult object that is used for signaling user interaction + with the results of this search. +*/ SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndReplace) { clearContents(); @@ -349,6 +477,11 @@ SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndRep return d->m_currentSearch; } +/*! + \fn void SearchResultWindow::finishSearch() + \brief Notifies the search result window that the current search + has finished, and the UI should reflect that. +*/ void SearchResultWindow::finishSearch() { if (d->m_items.count()) { @@ -358,6 +491,10 @@ void SearchResultWindow::finishSearch() } } +/*! + \fn void SearchResultWindow::clearContents() + \brief Clears the current contents in the search result window. +*/ void SearchResultWindow::clearContents() { d->m_replaceTextEdit->setEnabled(false); @@ -369,6 +506,10 @@ void SearchResultWindow::clearContents() navigateStateChanged(); } +/*! + \fn void SearchResultWindow::showNoMatchesFound() + \internal +*/ void SearchResultWindow::showNoMatchesFound() { d->m_replaceTextEdit->setEnabled(false); @@ -376,26 +517,47 @@ void SearchResultWindow::showNoMatchesFound() d->m_widget->setCurrentWidget(d->m_noMatchesFoundDisplay); } +/*! + \fn bool SearchResultWindow::isEmpty() const + Returns if the search result window currently doesn't show any results. +*/ bool SearchResultWindow::isEmpty() const { return (d->m_searchResultTreeView->model()->rowCount() < 1); } +/*! + \fn int SearchResultWindow::numberOfResults() const + Returns the number of search results currently shown in the search + results window. +*/ int SearchResultWindow::numberOfResults() const { return d->m_items.count(); } +/*! + \fn bool SearchResultWindow::hasFocus() + \internal +*/ bool SearchResultWindow::hasFocus() { return d->m_searchResultTreeView->hasFocus() || (d->m_isShowingReplaceUI && d->m_replaceTextEdit->hasFocus()); } +/*! + \fn bool SearchResultWindow::canFocus() + \internal +*/ bool SearchResultWindow::canFocus() { return !d->m_items.isEmpty(); } +/*! + \fn void SearchResultWindow::setFocus() + \internal +*/ void SearchResultWindow::setFocus() { if (!d->m_items.isEmpty()) { @@ -414,17 +576,37 @@ void SearchResultWindow::setFocus() } } +/*! + \fn void SearchResultWindow::setTextEditorFont(const QFont &font) + \internal +*/ void SearchResultWindow::setTextEditorFont(const QFont &font) { d->m_searchResultTreeView->setTextEditorFont(font); } +/*! + \fn void SearchResultWindow::handleJumpToSearchResult(int index, bool) + \internal +*/ void SearchResultWindow::handleJumpToSearchResult(int index, bool /* checked */) { QTC_ASSERT(d->m_currentSearch, return); d->m_currentSearch->activated(d->m_items.at(index)); } +/*! + \fn void SearchResultWindow::addResult(const QString &fileName, int lineNumber, const QString &rowText, int searchTermStart, int searchTermLength, const QVariant &userData) + \brief Adds a single result line to the search results. + + The \a fileName, \a lineNumber and \a rowText are shown in the result line. + \a searchTermStart and \a searchTermLength specify the region that + should be visually marked (string position and length in \a rowText). + You can attach arbitrary \a userData to the search result, which can + be used e.g. when reacting to the signals of the SearchResult for your search. + + \sa addResults() +*/ void SearchResultWindow::addResult(const QString &fileName, int lineNumber, const QString &rowText, int searchTermStart, int searchTermLength, const QVariant &userData) { @@ -438,6 +620,13 @@ void SearchResultWindow::addResult(const QString &fileName, int lineNumber, cons addResults(QList<SearchResultItem>() << item); } +/*! + \fn void SearchResultWindow::addResults(QList<SearchResultItem> &items) + \brief Adds all of the given search result \a items to the search + results window. + + \sa addResult() +*/ void SearchResultWindow::addResults(QList<SearchResultItem> &items) { int index = d->m_items.size(); @@ -460,6 +649,10 @@ void SearchResultWindow::addResults(QList<SearchResultItem> &items) } } +/*! + \fn void SearchResultWindow::handleExpandCollapseToolButton(bool checked) + \internal +*/ void SearchResultWindow::handleExpandCollapseToolButton(bool checked) { d->m_searchResultTreeView->setAutoExpandResults(checked); @@ -469,6 +662,10 @@ void SearchResultWindow::handleExpandCollapseToolButton(bool checked) d->m_searchResultTreeView->collapseAll(); } +/*! + \fn void SearchResultWindow::readSettings() + \internal +*/ void SearchResultWindow::readSettings() { QSettings *s = Core::ICore::instance()->settings(); @@ -479,6 +676,10 @@ void SearchResultWindow::readSettings() } } +/*! + \fn void SearchResultWindow::writeSettings() + \internal +*/ void SearchResultWindow::writeSettings() { QSettings *s = Core::ICore::instance()->settings(); @@ -489,21 +690,37 @@ void SearchResultWindow::writeSettings() } } +/*! + \fn int SearchResultWindow::priorityInStatusBar() const + \internal +*/ int SearchResultWindow::priorityInStatusBar() const { return 80; } +/*! + \fn bool SearchResultWindow::canNext() + \internal +*/ bool SearchResultWindow::canNext() { return d->m_items.count() > 0; } +/*! + \fn bool SearchResultWindow::canPrevious() + \internal +*/ bool SearchResultWindow::canPrevious() { return d->m_items.count() > 0; } +/*! + \fn void SearchResultWindow::goToNext() + \internal +*/ void SearchResultWindow::goToNext() { if (d->m_items.count() == 0) @@ -514,6 +731,11 @@ void SearchResultWindow::goToNext() d->m_searchResultTreeView->emitJumpToSearchResult(idx); } } + +/*! + \fn void SearchResultWindow::goToPrev() + \internal +*/ void SearchResultWindow::goToPrev() { if (!d->m_searchResultTreeView->model()->rowCount()) @@ -525,6 +747,10 @@ void SearchResultWindow::goToPrev() } } +/*! + \fn bool SearchResultWindow::canNavigate() + \internal +*/ bool SearchResultWindow::canNavigate() { return true;