From fdeb7620f1e7f33e5bb5749a28f84870c9af70ac Mon Sep 17 00:00:00 2001
From: Eike Ziller <eike.ziller@nokia.com>
Date: Fri, 9 Sep 2011 16:10:57 +0200
Subject: [PATCH] Keep multiple search results in history.

Change-Id: I7350c78479343e85b1ca4957e08bccefb5756d20
Reviewed-on: http://codereview.qt-project.org/4556
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Leandro T. C. Melo <leandro.melo@nokia.com>
---
 src/plugins/cpptools/cppfindreferences.cpp    |  31 ++-
 src/plugins/cpptools/cppfindreferences.h      |   2 +-
 src/plugins/cpptools/symbolsfindfilter.cpp    |  11 +-
 src/plugins/cpptools/symbolsfindfilter.h      |   3 +-
 src/plugins/find/searchresultwidget.cpp       |  10 +-
 src/plugins/find/searchresultwidget.h         |   1 -
 src/plugins/find/searchresultwindow.cpp       | 193 ++++++++++++------
 src/plugins/find/searchresultwindow.h         |  13 +-
 .../qmljseditor/qmljsfindreferences.cpp       |  22 +-
 src/plugins/qmljseditor/qmljsfindreferences.h |   3 +-
 src/plugins/texteditor/basefilefind.cpp       |  17 +-
 src/plugins/texteditor/basefilefind.h         |   6 +-
 12 files changed, 205 insertions(+), 107 deletions(-)

diff --git a/src/plugins/cpptools/cppfindreferences.cpp b/src/plugins/cpptools/cppfindreferences.cpp
index 9bcd6fbea1e..16e62d0f139 100644
--- a/src/plugins/cpptools/cppfindreferences.cpp
+++ b/src/plugins/cpptools/cppfindreferences.cpp
@@ -161,8 +161,7 @@ public:
 
 CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
     : QObject(modelManager),
-      _modelManager(modelManager),
-      m_currentSearch(0)
+      _modelManager(modelManager)
 {
     m_watcher.setPendingResultsLimit(1);
     connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
@@ -227,9 +226,11 @@ static void find_helper(QFutureInterface<Usage> &future,
 
 void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
 {
-    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(Find::SearchResultWindow::SearchOnly);
     Overview overview;
-    m_currentSearch->setInfo(tr("C++ Usages:"), QString(), overview(context.fullyQualifiedName(symbol)));
+    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
+                                                QString(),
+                                                overview(context.fullyQualifiedName(symbol)),
+                                                Find::SearchResultWindow::SearchOnly);
     connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)),
             this, SLOT(openEditor(Find::SearchResultItem)));
 
@@ -243,10 +244,12 @@ void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus:
         const QString textToReplace = replacement.isEmpty()
                 ? QString::fromUtf8(id->chars(), id->size()) : replacement;
 
-        m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
-                Find::SearchResultWindow::SearchAndReplace, QLatin1String("CppEditor"));
         Overview overview;
-        m_currentSearch->setInfo(tr("C++ Usages:"), QString(), overview(context.fullyQualifiedName(symbol)));
+        m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
+                    tr("C++ Usages:"),
+                    QString(),
+                    overview(context.fullyQualifiedName(symbol)),
+                    Find::SearchResultWindow::SearchAndReplace, QLatin1String("CppEditor"));
         m_currentSearch->setTextToReplace(textToReplace);
 
         connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)),
@@ -293,6 +296,10 @@ void CppFindReferences::onReplaceButtonClicked(const QString &text,
 
 void CppFindReferences::displayResults(int first, int last)
 {
+    if (!m_currentSearch) {
+        m_watcher.cancel();
+        return;
+    }
     for (int index = first; index != last; ++index) {
         Usage result = m_watcher.future().resultAt(index);
         m_currentSearch->addResult(result.path,
@@ -305,7 +312,8 @@ void CppFindReferences::displayResults(int first, int last)
 
 void CppFindReferences::searchFinished()
 {
-    m_currentSearch->finishSearch();
+    if (m_currentSearch)
+        m_currentSearch->finishSearch();
     m_currentSearch = 0;
     emit changed();
 }
@@ -422,8 +430,11 @@ static void findMacroUses_helper(QFutureInterface<Usage> &future,
 
 void CppFindReferences::findMacroUses(const Macro &macro)
 {
-    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(Find::SearchResultWindow::SearchOnly);
-    m_currentSearch->setInfo(tr("C++ Macro Usages:"), QString(), QString::fromLocal8Bit(macro.name()));
+    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
+                tr("C++ Macro Usages:"),
+                QString(),
+                macro.name(),
+                Find::SearchResultWindow::SearchOnly);
 
     Find::SearchResultWindow::instance()->popup(true);
 
diff --git a/src/plugins/cpptools/cppfindreferences.h b/src/plugins/cpptools/cppfindreferences.h
index 1071e0e6dba..09b1828ee45 100644
--- a/src/plugins/cpptools/cppfindreferences.h
+++ b/src/plugins/cpptools/cppfindreferences.h
@@ -94,7 +94,7 @@ private:
 
 private:
     QPointer<CPlusPlus::CppModelManagerInterface> _modelManager;
-    Find::SearchResult *m_currentSearch;
+    QPointer<Find::SearchResult> m_currentSearch;
     QFutureWatcher<CPlusPlus::Usage> m_watcher;
 
     mutable QMutex m_depsLock;
diff --git a/src/plugins/cpptools/symbolsfindfilter.cpp b/src/plugins/cpptools/symbolsfindfilter.cpp
index 3aecc1d6ff4..bbb4e637b65 100644
--- a/src/plugins/cpptools/symbolsfindfilter.cpp
+++ b/src/plugins/cpptools/symbolsfindfilter.cpp
@@ -104,7 +104,6 @@ SymbolsFindFilter::SymbolsFindFilter(CppModelManager *manager)
     : m_manager(manager),
       m_isRunning(false),
       m_enabled(true),
-      m_currentSearch(0),
       m_symbolsToSearch(SearchSymbols::AllTypes),
       m_scope(SearchProjectsOnly)
 {
@@ -150,8 +149,7 @@ void SymbolsFindFilter::findAll(const QString &txt, Find::FindFlags findFlags)
     m_isRunning = true;
     emit changed();
     Find::SearchResultWindow *window = Find::SearchResultWindow::instance();
-    m_currentSearch = window->startNewSearch();
-    m_currentSearch->setInfo(label(), toolTip(findFlags), txt);
+    m_currentSearch = window->startNewSearch(label(), toolTip(findFlags), txt);
     connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem)));
     window->popup(true);
 
@@ -177,6 +175,10 @@ void SymbolsFindFilter::findAll(const QString &txt, Find::FindFlags findFlags)
 
 void SymbolsFindFilter::addResults(int begin, int end)
 {
+    if (!m_currentSearch) {
+        m_watcher.cancel();
+        return;
+    }
     QList<Find::SearchResultItem> items;
     for (int i = begin; i < end; ++i)
         items << m_watcher.resultAt(i);
@@ -185,7 +187,8 @@ void SymbolsFindFilter::addResults(int begin, int end)
 
 void SymbolsFindFilter::finish()
 {
-    m_currentSearch->finishSearch();
+    if (m_currentSearch)
+        m_currentSearch->finishSearch();
     m_currentSearch = 0;
     m_isRunning = false;
     emit changed();
diff --git a/src/plugins/cpptools/symbolsfindfilter.h b/src/plugins/cpptools/symbolsfindfilter.h
index 37ebf362acd..f0a241a7be2 100644
--- a/src/plugins/cpptools/symbolsfindfilter.h
+++ b/src/plugins/cpptools/symbolsfindfilter.h
@@ -40,6 +40,7 @@
 
 #include <QtCore/QFutureInterface>
 #include <QtCore/QFutureWatcher>
+#include <QtCore/QPointer>
 #include <QtGui/QWidget>
 #include <QtGui/QCheckBox>
 #include <QtGui/QRadioButton>
@@ -97,7 +98,7 @@ private:
     bool m_isRunning;
     bool m_enabled;
     QFutureWatcher<Find::SearchResultItem> m_watcher;
-    Find::SearchResult *m_currentSearch;
+    QPointer<Find::SearchResult> m_currentSearch;
     SearchSymbols::SymbolTypes m_symbolsToSearch;
     SearchSymbols m_search;
     SearchScope m_scope;
diff --git a/src/plugins/find/searchresultwidget.cpp b/src/plugins/find/searchresultwidget.cpp
index f06e28141c7..13d9d592821 100644
--- a/src/plugins/find/searchresultwidget.cpp
+++ b/src/plugins/find/searchresultwidget.cpp
@@ -254,7 +254,6 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
     m_cancelButton->setText(tr("Cancel"));
     m_cancelButton->setToolButtonStyle(Qt::ToolButtonTextOnly);
     connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(cancel()));
-    m_cancelButton->setVisible(false);
 
     m_replaceLabel = new QLabel(tr("Replace with:"), topWidget);
     m_replaceTextEdit = new WideEnoughLineEdit(topWidget);
@@ -285,11 +284,6 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
     connect(m_replaceButton, SIGNAL(clicked()), this, SLOT(handleReplaceButton()));
 }
 
-void SearchResultWidget::startSearch()
-{
-    m_cancelButton->setVisible(true);
-}
-
 void SearchResultWidget::setInfo(const QString &label, const QString &toolTip, const QString &term)
 {
     m_label->setText(label);
@@ -319,7 +313,7 @@ void SearchResultWidget::addResults(const QList<SearchResultItem> &items, Search
     m_count += items.size();
     m_searchResultTreeView->addResults(items, mode);
     if (firstItems) {
-        if (!m_dontAskAgainGroup.isEmpty() && showWarningMessage()) {
+        if (showWarningMessage()) {
             Core::InfoBarEntry info("warninglabel", tr("This change cannot be undone."));
             info.setCustomButtonInfo(tr("Do not warn again"), this, SLOT(hideNoUndoWarning()));
             m_infoBar.addInfo(info);
@@ -497,6 +491,8 @@ void SearchResultWidget::cancel()
 
 bool SearchResultWidget::showWarningMessage() const
 {
+    if (m_dontAskAgainGroup.isEmpty())
+        return false;
     // read settings
     QSettings *settings = Core::ICore::instance()->settings();
     settings->beginGroup(m_dontAskAgainGroup);
diff --git a/src/plugins/find/searchresultwidget.h b/src/plugins/find/searchresultwidget.h
index ed5543c9126..a43b0b2c68c 100644
--- a/src/plugins/find/searchresultwidget.h
+++ b/src/plugins/find/searchresultwidget.h
@@ -53,7 +53,6 @@ class SearchResultWidget : public QWidget
 public:
     explicit SearchResultWidget(QWidget *parent = 0);
 
-    void startSearch();
     void setInfo(const QString &label, const QString &toolTip, const QString &term);
 
     void addResult(const QString &fileName, int lineNumber, const QString &lineText,
diff --git a/src/plugins/find/searchresultwindow.cpp b/src/plugins/find/searchresultwindow.cpp
index 28e1c4374e1..7692152ce8b 100644
--- a/src/plugins/find/searchresultwindow.cpp
+++ b/src/plugins/find/searchresultwindow.cpp
@@ -47,6 +47,7 @@
 #include <QtGui/QVBoxLayout>
 #include <QtGui/QFont>
 #include <QtGui/QAction>
+#include <QtGui/QComboBox>
 #include <QtGui/QStackedWidget>
 
 static const char SETTINGSKEYSECTIONNAME[] = "SearchResults";
@@ -56,20 +57,59 @@ namespace Find {
 
 namespace Internal {
 
-    struct SearchResultWindowPrivate {
-        SearchResultWindowPrivate();
+    class SearchResultWindowPrivate : public QObject {
+        Q_OBJECT
+    public:
+        SearchResultWindowPrivate(SearchResultWindow *window);
+        bool isSearchVisible() const;
+        int visibleSearchIndex() const;
 
-        Internal::SearchResultWidget *m_searchResultWidget;
+        SearchResultWindow *q;
+        QList<Internal::SearchResultWidget *> m_searchResultWidgets;
         QToolButton *m_expandCollapseButton;
         QAction *m_expandCollapseAction;
         static const bool m_initiallyExpand = false;
+        QWidget *m_spacer;
+        QComboBox *m_recentSearchesBox;
         QStackedWidget *m_widget;
-        SearchResult *m_currentSearch;
+        QList<SearchResult *> m_searchResults;
+        int m_currentIndex;
+
+    public slots:
+        void setCurrentIndex(int index);
     };
 
-    SearchResultWindowPrivate::SearchResultWindowPrivate()
-        : m_currentSearch(0)
+    SearchResultWindowPrivate::SearchResultWindowPrivate(SearchResultWindow *window)
+        : q(window)
+    {
+    }
+
+    bool SearchResultWindowPrivate::isSearchVisible() const
+    {
+        return m_currentIndex > 0;
+    }
+
+    int SearchResultWindowPrivate::visibleSearchIndex() const
     {
+        return m_currentIndex - 1;
+    }
+
+    void SearchResultWindowPrivate::setCurrentIndex(int index)
+    {
+        if (isSearchVisible())
+            m_searchResultWidgets.at(visibleSearchIndex())->notifyVisibilityChanged(false);
+        m_currentIndex = index;
+        m_widget->setCurrentIndex(index);
+        m_recentSearchesBox->setCurrentIndex(index);
+        if (!isSearchVisible()) {
+            m_widget->currentWidget()->setFocus();
+            m_expandCollapseButton->setEnabled(false);
+        } else {
+            m_searchResultWidgets.at(visibleSearchIndex())->setFocusInternally();
+            m_searchResultWidgets.at(visibleSearchIndex())->notifyVisibilityChanged(true);
+            m_expandCollapseButton->setEnabled(true);
+        }
+        q->navigateStateChanged();
     }
 }
 
@@ -155,16 +195,21 @@ SearchResultWindow *SearchResultWindow::m_instance = 0;
     \internal
 */
 SearchResultWindow::SearchResultWindow(QWidget *newSearchPanel)
-    : d(new SearchResultWindowPrivate)
+    : d(new SearchResultWindowPrivate(this))
 {
     m_instance = this;
+    d->m_spacer = new QWidget;
+    d->m_spacer->setMinimumWidth(30);
+    d->m_recentSearchesBox = new QComboBox;
+    d->m_recentSearchesBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+    d->m_recentSearchesBox->addItem(tr("New Search"));
+    connect(d->m_recentSearchesBox, SIGNAL(activated(int)), d, SLOT(setCurrentIndex(int)));
+
     d->m_widget = new QStackedWidget;
     d->m_widget->setWindowTitle(displayName());
 
     d->m_widget->addWidget(newSearchPanel);
-
-    d->m_searchResultWidget = new Internal::SearchResultWidget(d->m_widget);
-    d->m_widget->addWidget(d->m_searchResultWidget);
+    d->m_currentIndex = 0;
 
     d->m_expandCollapseButton = new QToolButton(d->m_widget);
     d->m_expandCollapseButton->setAutoRaise(true);
@@ -179,7 +224,6 @@ SearchResultWindow::SearchResultWindow(QWidget *newSearchPanel)
     d->m_expandCollapseButton->setDefaultAction(cmd->action());
 
     connect(d->m_expandCollapseAction, SIGNAL(toggled(bool)), this, SLOT(handleExpandCollapseToolButton(bool)));
-    connect(d->m_searchResultWidget, SIGNAL(navigateStateChanged()), this, SLOT(navigateStateChanged()));
     readSettings();
 }
 
@@ -190,8 +234,7 @@ SearchResultWindow::SearchResultWindow(QWidget *newSearchPanel)
 SearchResultWindow::~SearchResultWindow()
 {
     writeSettings();
-    delete d->m_currentSearch;
-    d->m_currentSearch = 0;
+    qDeleteAll(d->m_searchResults);
     delete d->m_widget;
     d->m_widget = 0;
     delete d;
@@ -212,7 +255,8 @@ SearchResultWindow *SearchResultWindow::instance()
 */
 void SearchResultWindow::visibilityChanged(bool visible)
 {
-    d->m_searchResultWidget->notifyVisibilityChanged(visible);
+    if (d->isSearchVisible())
+        d->m_searchResultWidgets.at(d->visibleSearchIndex())->notifyVisibilityChanged(visible);
 }
 
 /*!
@@ -230,31 +274,56 @@ QWidget *SearchResultWindow::outputWidget(QWidget *)
 */
 QList<QWidget*> SearchResultWindow::toolBarWidgets() const
 {
-    return QList<QWidget*>() << d->m_expandCollapseButton;
+    return QList<QWidget*>() << d->m_expandCollapseButton << d->m_spacer << d->m_recentSearchesBox;
 }
 
 /*!
     \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).
+    The \a label should be a string that shortly describes the type of
+    search, i.e. search filter and possibly a most relevant search option, followed by a colon ':'.
+    E.g. \code{Project 'myproject':}
+    The \a searchTerm will be shown behind the colon.
+    The \a toolTip should elaborate on the search parameters, like file patterns that are searched and
+    find flags.
     If \a cfgGroup is not empty, it will be used for storing the "do not ask again"
     setting of a "this change cannot be undone" warning (which is implicitly requested
     by passing a non-empty group).
     Returns a SearchResult object that is used for signaling user interaction
     with the results of this search.
+    The search result window owns the returned SearchResult
+    and might delete it any time, even while the search is running
+    (e.g. when the user clears the search result pane, or if the user opens so many other searches
+    that this search falls out of the history).
+
 */
-SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndReplace, const QString &cfgGroup)
+SearchResult *SearchResultWindow::startNewSearch(const QString &label,
+                                                 const QString &toolTip,
+                                                 const QString &searchTerm,
+                                                 SearchMode searchOrSearchAndReplace,
+                                                 const QString &cfgGroup)
 {
-    clearContents();
-    d->m_searchResultWidget->setShowReplaceUI(searchOrSearchAndReplace != SearchOnly);
-    d->m_searchResultWidget->setDontAskAgainGroup(cfgGroup);
-    d->m_searchResultWidget->startSearch();
-    d->m_widget->setCurrentWidget(d->m_searchResultWidget);
-    delete d->m_currentSearch;
-    d->m_currentSearch = new SearchResult(d->m_searchResultWidget);
-    return d->m_currentSearch;
+    if (d->m_searchResults.size() >= 5) {
+        d->m_searchResultWidgets.last()->notifyVisibilityChanged(false);
+        delete d->m_searchResults.takeLast();
+        delete d->m_searchResultWidgets.takeLast();
+        d->m_recentSearchesBox->removeItem(d->m_recentSearchesBox->count()-1);
+    }
+    Internal::SearchResultWidget *widget = new Internal::SearchResultWidget;
+    d->m_searchResultWidgets.prepend(widget);
+    d->m_widget->insertWidget(1, widget);
+    connect(widget, SIGNAL(navigateStateChanged()), this, SLOT(navigateStateChanged()));
+    widget->setShowReplaceUI(searchOrSearchAndReplace != SearchOnly);
+    widget->setInfo(label, toolTip, searchTerm);
+    if (searchOrSearchAndReplace == SearchAndReplace)
+        widget->setDontAskAgainGroup(cfgGroup);
+    SearchResult *result = new SearchResult(widget);
+    d->m_searchResults.prepend(result);
+    d->m_recentSearchesBox->insertItem(1, tr("%1 %2").arg(label, searchTerm));
+    if (d->m_currentIndex > 0)
+        ++d->m_currentIndex; // so setCurrentIndex still knows about the right "currentIndex" and its widget
+    d->setCurrentIndex(1);
+    return result;
 }
 
 /*!
@@ -263,7 +332,18 @@ SearchResult *SearchResultWindow::startNewSearch(SearchMode searchOrSearchAndRep
 */
 void SearchResultWindow::clearContents()
 {
-    d->m_searchResultWidget->clear();
+    for (int i = d->m_recentSearchesBox->count() - 1; i > 0 /* don't want i==0 */; --i)
+        d->m_recentSearchesBox->removeItem(i);
+    foreach (Internal::SearchResultWidget *widget, d->m_searchResultWidgets)
+        widget->notifyVisibilityChanged(false);
+    qDeleteAll(d->m_searchResultWidgets);
+    d->m_searchResultWidgets.clear();
+    qDeleteAll(d->m_searchResults);
+    d->m_searchResults.clear();
+
+    d->m_currentIndex = 0;
+    d->m_widget->currentWidget()->setFocus();
+    d->m_expandCollapseButton->setEnabled(false);
     navigateStateChanged();
 }
 
@@ -273,7 +353,7 @@ void SearchResultWindow::clearContents()
 */
 bool SearchResultWindow::hasFocus()
 {
-    return d->m_searchResultWidget->hasFocusInternally();
+    return d->m_widget->focusWidget() && d->m_widget->focusWidget()->hasFocus();
 }
 
 /*!
@@ -282,7 +362,9 @@ bool SearchResultWindow::hasFocus()
 */
 bool SearchResultWindow::canFocus()
 {
-    return d->m_searchResultWidget->canFocusInternally();
+    if (d->isSearchVisible())
+        return d->m_searchResultWidgets.at(d->visibleSearchIndex())->canFocusInternally();
+    return true;
 }
 
 /*!
@@ -291,11 +373,10 @@ bool SearchResultWindow::canFocus()
 */
 void SearchResultWindow::setFocus()
 {
-    int stackIndex = d->m_widget->currentIndex();
-    if (stackIndex == 0)
+    if (!d->isSearchVisible())
         d->m_widget->currentWidget()->setFocus();
     else
-        d->m_searchResultWidget->setFocusInternally();
+        d->m_searchResultWidgets.at(d->visibleSearchIndex())->setFocusInternally();
 }
 
 /*!
@@ -304,12 +385,13 @@ void SearchResultWindow::setFocus()
 */
 void SearchResultWindow::setTextEditorFont(const QFont &font)
 {
-    d->m_searchResultWidget->setTextEditorFont(font);
+    foreach (Internal::SearchResultWidget *widget, d->m_searchResultWidgets)
+        widget->setTextEditorFont(font);
 }
 
 void SearchResultWindow::openNewSearchPanel()
 {
-    d->m_widget->setCurrentIndex(0);
+    d->setCurrentIndex(0);
     popup();
 }
 
@@ -319,13 +401,15 @@ void SearchResultWindow::openNewSearchPanel()
 */
 void SearchResultWindow::handleExpandCollapseToolButton(bool checked)
 {
-    d->m_searchResultWidget->setAutoExpandResults(checked);
+    if (!d->isSearchVisible())
+        return;
+    d->m_searchResultWidgets.at(d->visibleSearchIndex())->setAutoExpandResults(checked);
     if (checked) {
         d->m_expandCollapseAction->setText(tr("Collapse All"));
-        d->m_searchResultWidget->expandAll();
+        d->m_searchResultWidgets.at(d->visibleSearchIndex())->expandAll();
     } else {
         d->m_expandCollapseAction->setText(tr("Expand All"));
-        d->m_searchResultWidget->collapseAll();
+        d->m_searchResultWidgets.at(d->visibleSearchIndex())->collapseAll();
     }
 }
 
@@ -372,7 +456,9 @@ int SearchResultWindow::priorityInStatusBar() const
 */
 bool SearchResultWindow::canNext()
 {
-    return d->m_searchResultWidget->count() > 0;
+    if (d->isSearchVisible())
+        return d->m_searchResultWidgets.at(d->visibleSearchIndex())->count() > 0;
+    return false;
 }
 
 /*!
@@ -381,7 +467,7 @@ bool SearchResultWindow::canNext()
 */
 bool SearchResultWindow::canPrevious()
 {
-    return d->m_searchResultWidget->count() > 0;
+    return canNext();
 }
 
 /*!
@@ -390,7 +476,9 @@ bool SearchResultWindow::canPrevious()
 */
 void SearchResultWindow::goToNext()
 {
-    d->m_searchResultWidget->goToNext();
+    int index = d->m_widget->currentIndex();
+    if (index != 0)
+        d->m_searchResultWidgets.at(index-1)->goToNext();
 }
 
 /*!
@@ -399,7 +487,9 @@ void SearchResultWindow::goToNext()
 */
 void SearchResultWindow::goToPrev()
 {
-    d->m_searchResultWidget->goToPrevious();
+    int index = d->m_widget->currentIndex();
+    if (index != 0)
+        d->m_searchResultWidgets.at(index-1)->goToPrevious();
 }
 
 /*!
@@ -459,23 +549,6 @@ QString SearchResult::textToReplace() const
     return m_widget->textToReplace();
 }
 
-/*!
-    \fn void SearchResult::setInfo(const QString &label, const QString &toolTip, const QString &term)
-    \brief Set the information about the search that is show in the top-left corner of
-    the search result window.
-
-    The \a label should be a string that shortly describes the
-    search, i.e. search filter and a most relevant search options, followed by a colon ':'.
-    E.g. \code{Project 'myproject':}
-    The search \a term will be shown behind the colon.
-    The \a toolTip should elaborate on the search parameters, like file patterns that are searched and
-    find flags.
-*/
-void SearchResult::setInfo(const QString &label, const QString &toolTip, const QString &term)
-{
-    m_widget->setInfo(label, toolTip, term);
-}
-
 /*!
     \fn void SearchResult::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.
@@ -528,3 +601,5 @@ void SearchResult::setTextToReplace(const QString &textToReplace)
 }
 
 } // namespace Find
+
+#include "searchresultwindow.moc"
diff --git a/src/plugins/find/searchresultwindow.h b/src/plugins/find/searchresultwindow.h
index 24f25eae8eb..0d09575a666 100644
--- a/src/plugins/find/searchresultwindow.h
+++ b/src/plugins/find/searchresultwindow.h
@@ -48,7 +48,7 @@ QT_END_NAMESPACE
 namespace Find {
 namespace Internal {
     class SearchResultTreeView;
-    struct SearchResultWindowPrivate;
+    class SearchResultWindowPrivate;
     class SearchResultWidget;
 }
 class SearchResultWindow;
@@ -98,7 +98,6 @@ public:
     void setUserData(const QVariant &data);
     QVariant userData() const;
     QString textToReplace() const;
-    void setInfo(const QString &label, const QString &toolTip, const QString &term);
 
 public slots:
     void addResult(const QString &fileName, int lineNumber, const QString &lineText,
@@ -156,8 +155,14 @@ public:
     void setTextEditorFont(const QFont &font);
     void openNewSearchPanel();
 
-    // search result object is guaranteed to live till its finishSearch method is called
-    SearchResult *startNewSearch(SearchMode searchOrSearchAndReplace = SearchOnly,
+    // The search result window owns the returned SearchResult
+    // and might delete it any time, even while the search is running
+    // (e.g. when the user clears the search result pane, or if the user opens so many other searches
+    // that this search falls out of the history).
+    SearchResult *startNewSearch(const QString &label,
+                                 const QString &toolTip,
+                                 const QString &searchTerm,
+                                 SearchMode searchOrSearchAndReplace = SearchOnly,
                                  const QString &cfgGroup = QString());
 
 public slots:
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp
index 79a7a70bd2e..716e448bc48 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.cpp
+++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp
@@ -776,8 +776,7 @@ public:
 } // end of anonymous namespace
 
 FindReferences::FindReferences(QObject *parent)
-    : QObject(parent),
-      m_currentSearch(0)
+    : QObject(parent)
 {
     m_watcher.setPendingResultsLimit(1);
     connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
@@ -914,18 +913,20 @@ void FindReferences::displayResults(int first, int last)
     // the first usage is always a dummy to indicate we now start searching
     if (first == 0) {
         Usage dummy = m_watcher.future().resultAt(0);
-        QString replacement = dummy.path;
-        QString symbolName = dummy.lineText;
+        const QString replacement = dummy.path;
+        const QString symbolName = dummy.lineText;
+        const QString label = tr("QML/JS Usages:");
 
         if (replacement.isEmpty()) {
-            m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(Find::SearchResultWindow::SearchOnly);
+            m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
+                        label, QString(), symbolName, Find::SearchResultWindow::SearchOnly);
         } else {
-            m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(Find::SearchResultWindow::SearchAndReplace);
+            m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(
+                        label, QString(), symbolName, Find::SearchResultWindow::SearchAndReplace);
             m_currentSearch->setTextToReplace(replacement);
             connect(m_currentSearch, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
                     SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));
         }
-        m_currentSearch->setInfo(tr("Usages:"), QString(), symbolName);
         connect(m_currentSearch, SIGNAL(activated(Find::SearchResultItem)),
                 this, SLOT(openEditor(Find::SearchResultItem)));
         connect(m_currentSearch, SIGNAL(cancelled()), this, SLOT(cancel()));
@@ -940,6 +941,10 @@ void FindReferences::displayResults(int first, int last)
         ++first;
     }
 
+    if (!m_currentSearch) {
+        m_watcher.cancel();
+        return;
+    }
     for (int index = first; index != last; ++index) {
         Usage result = m_watcher.future().resultAt(index);
         m_currentSearch->addResult(result.path,
@@ -952,7 +957,8 @@ void FindReferences::displayResults(int first, int last)
 
 void FindReferences::searchFinished()
 {
-    m_currentSearch->finishSearch();
+    if (m_currentSearch)
+        m_currentSearch->finishSearch();
     m_currentSearch = 0;
     emit changed();
 }
diff --git a/src/plugins/qmljseditor/qmljsfindreferences.h b/src/plugins/qmljseditor/qmljsfindreferences.h
index db6e548d27c..a8c7de639a7 100644
--- a/src/plugins/qmljseditor/qmljsfindreferences.h
+++ b/src/plugins/qmljseditor/qmljsfindreferences.h
@@ -37,6 +37,7 @@
 #include <QtCore/QObject>
 #include <QtCore/QFuture>
 #include <QtCore/QFutureWatcher>
+#include <QtCore/QPointer>
 #include <utils/filesearch.h>
 #include <qmljs/qmljsdocument.h>
 
@@ -90,7 +91,7 @@ private Q_SLOTS:
     void onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items);
 
 private:
-    Find::SearchResult *m_currentSearch;
+    QPointer<Find::SearchResult> m_currentSearch;
     QFutureWatcher<Usage> m_watcher;
 };
 
diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp
index 105fa8f8f9c..c910c07bd7b 100644
--- a/src/plugins/texteditor/basefilefind.cpp
+++ b/src/plugins/texteditor/basefilefind.cpp
@@ -113,14 +113,10 @@ void BaseFileFind::runNewSearch(const QString &txt, Find::FindFlags findFlags,
         updateComboEntries(m_filterCombo, true);
     m_watcher.setFuture(QFuture<FileSearchResultList>());
     m_currentSearchCount = 0;
-    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(searchMode,
-                                                     searchMode == SearchResultWindow::SearchAndReplace
-                                                     ? QString::fromLatin1("TextEditor")
-                                                     : QString());
+    m_currentSearch = Find::SearchResultWindow::instance()->startNewSearch(label(),
+                           toolTip().arg(Find::IFindFilter::descriptionForFindFlags(findFlags)),
+                           txt, searchMode, QString::fromLatin1("TextEditor"));
     m_currentSearch->setTextToReplace(txt);
-    m_currentSearch->setInfo(label(),
-                             toolTip().arg(Find::IFindFilter::descriptionForFindFlags(findFlags)),
-                             txt);
     QVariantList searchParameters;
     searchParameters << qVariantFromValue(txt) << qVariantFromValue(findFlags);
     m_currentSearch->setUserData(searchParameters);
@@ -169,6 +165,10 @@ void BaseFileFind::doReplace(const QString &text,
 }
 
 void BaseFileFind::displayResult(int index) {
+    if (!m_currentSearch) {
+        m_watcher.cancel();
+        return;
+    }
     Utils::FileSearchResultList results = m_watcher.future().resultAt(index);
     QList<Find::SearchResultItem> items;
     foreach (const Utils::FileSearchResult &result, results) {
@@ -190,7 +190,8 @@ void BaseFileFind::displayResult(int index) {
 
 void BaseFileFind::searchFinished()
 {
-    m_currentSearch->finishSearch();
+    if (m_currentSearch)
+        m_currentSearch->finishSearch();
     m_currentSearch = 0;
     m_isSearching = false;
     m_resultLabel = 0;
diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h
index 6f757b10031..d3505d0da49 100644
--- a/src/plugins/texteditor/basefilefind.h
+++ b/src/plugins/texteditor/basefilefind.h
@@ -78,8 +78,8 @@ public:
 
 protected:
     virtual Utils::FileIterator *files() const = 0;
-    virtual QString label() const = 0; // see Find::SearchResult::setInfo
-    virtual QString toolTip() const = 0; // see Find::SearchResult::setInfo,
+    virtual QString label() const = 0; // see Find::SearchResultWindow::startNewSearch
+    virtual QString toolTip() const = 0; // see Find::SearchResultWindow::startNewSearch,
                                          // add %1 placeholder where the find flags should be put
 
     void writeCommonSettings(QSettings *settings);
@@ -103,7 +103,7 @@ private:
     void runNewSearch(const QString &txt, Find::FindFlags findFlags,
                       Find::SearchResultWindow::SearchMode searchMode);
 
-    Find::SearchResult *m_currentSearch;
+    QPointer<Find::SearchResult> m_currentSearch;
     int m_currentSearchCount;
 
     QFutureWatcher<Utils::FileSearchResultList> m_watcher;
-- 
GitLab