forked from qt-creator/qt-creator
Core: Add infrastructure to do additional filtering on search results
... and make use of it to let users filter C++ "find references" results by access type. Fixes: QTCREATORBUG-19373 Change-Id: Ib5cadde1cfd235026d8e69da51daa6374808d3f3 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -27,13 +27,68 @@
|
|||||||
#include "searchresulttreeitems.h"
|
#include "searchresulttreeitems.h"
|
||||||
#include "searchresulttreeitemroles.h"
|
#include "searchresulttreeitemroles.h"
|
||||||
|
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
using namespace Core;
|
namespace Core {
|
||||||
using namespace Core::Internal;
|
namespace Internal {
|
||||||
|
|
||||||
|
class SearchResultTreeModel : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
SearchResultTreeModel(QObject *parent = nullptr);
|
||||||
|
~SearchResultTreeModel() override;
|
||||||
|
|
||||||
|
void setShowReplaceUI(bool show);
|
||||||
|
void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
|
||||||
|
|
||||||
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
|
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QModelIndex parent(const QModelIndex &child) const override;
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||||
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
|
||||||
|
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
|
||||||
|
QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
|
||||||
|
|
||||||
|
QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
||||||
|
|
||||||
|
static SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void jumpToSearchResult(const QString &fileName, int lineNumber,
|
||||||
|
int searchTermStart, int searchTermLength);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QModelIndex index(SearchResultTreeItem *item) const;
|
||||||
|
void addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
||||||
|
QSet<SearchResultTreeItem *> addPath(const QStringList &path);
|
||||||
|
QVariant data(const SearchResultTreeItem *row, int role) const;
|
||||||
|
bool setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall = true);
|
||||||
|
QModelIndex nextIndex(const QModelIndex &idx, bool *wrapped = nullptr) const;
|
||||||
|
QModelIndex prevIndex(const QModelIndex &idx, bool *wrapped = nullptr) const;
|
||||||
|
|
||||||
|
SearchResultTreeItem *m_rootItem;
|
||||||
|
SearchResultTreeItem *m_currentParent;
|
||||||
|
SearchResultColors m_colors;
|
||||||
|
QModelIndex m_currentIndex;
|
||||||
|
QStringList m_currentPath; // the path that belongs to the current parent
|
||||||
|
QFont m_textEditorFont;
|
||||||
|
bool m_showReplaceUI;
|
||||||
|
bool m_editorFontIsUsed;
|
||||||
|
};
|
||||||
|
|
||||||
SearchResultTreeModel::SearchResultTreeModel(QObject *parent)
|
SearchResultTreeModel::SearchResultTreeModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
@@ -425,8 +480,6 @@ void SearchResultTreeModel::clear()
|
|||||||
|
|
||||||
QModelIndex SearchResultTreeModel::nextIndex(const QModelIndex &idx, bool *wrapped) const
|
QModelIndex SearchResultTreeModel::nextIndex(const QModelIndex &idx, bool *wrapped) const
|
||||||
{
|
{
|
||||||
if (wrapped)
|
|
||||||
*wrapped = false;
|
|
||||||
// pathological
|
// pathological
|
||||||
if (!idx.isValid())
|
if (!idx.isValid())
|
||||||
return index(0, 0);
|
return index(0, 0);
|
||||||
@@ -468,8 +521,6 @@ QModelIndex SearchResultTreeModel::next(const QModelIndex &idx, bool includeGene
|
|||||||
|
|
||||||
QModelIndex SearchResultTreeModel::prevIndex(const QModelIndex &idx, bool *wrapped) const
|
QModelIndex SearchResultTreeModel::prevIndex(const QModelIndex &idx, bool *wrapped) const
|
||||||
{
|
{
|
||||||
if (wrapped)
|
|
||||||
*wrapped = false;
|
|
||||||
QModelIndex current = idx;
|
QModelIndex current = idx;
|
||||||
bool checkForChildren = true;
|
bool checkForChildren = true;
|
||||||
if (current.isValid()) {
|
if (current.isValid()) {
|
||||||
@@ -502,3 +553,106 @@ QModelIndex SearchResultTreeModel::prev(const QModelIndex &idx, bool includeGene
|
|||||||
} while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated());
|
} while (value != idx && !includeGenerated && treeItemAtIndex(value)->isGenerated());
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SearchResultFilterModel::SearchResultFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||||
|
{
|
||||||
|
setSourceModel(new SearchResultTreeModel(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultFilterModel::setFilter(SearchResultFilter *filter)
|
||||||
|
{
|
||||||
|
if (m_filter)
|
||||||
|
m_filter->disconnect(this);
|
||||||
|
m_filter = filter;
|
||||||
|
if (m_filter) {
|
||||||
|
connect(m_filter, &SearchResultFilter::filterChanged,
|
||||||
|
this, [this] {
|
||||||
|
invalidateFilter();
|
||||||
|
emit filterInvalidated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
invalidateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultFilterModel::setShowReplaceUI(bool show)
|
||||||
|
{
|
||||||
|
sourceModel()->setShowReplaceUI(show);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultFilterModel::setTextEditorFont(const QFont &font, const SearchResultColors &colors)
|
||||||
|
{
|
||||||
|
sourceModel()->setTextEditorFont(font, colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QModelIndex> SearchResultFilterModel::addResults(const QList<SearchResultItem> &items,
|
||||||
|
SearchResult::AddMode mode)
|
||||||
|
{
|
||||||
|
QList<QModelIndex> sourceIndexes = sourceModel()->addResults(items, mode);
|
||||||
|
sourceIndexes = Utils::filtered(sourceIndexes, [this](const QModelIndex &idx) {
|
||||||
|
return filterAcceptsRow(idx.row(), idx.parent());
|
||||||
|
});
|
||||||
|
return Utils::transform(sourceIndexes,
|
||||||
|
[this](const QModelIndex &idx) { return mapFromSource(idx); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultFilterModel::clear()
|
||||||
|
{
|
||||||
|
sourceModel()->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SearchResultFilterModel::nextOrPrev(const QModelIndex &idx, bool *wrapped,
|
||||||
|
const std::function<QModelIndex (const QModelIndex &)> &func) const
|
||||||
|
{
|
||||||
|
if (wrapped)
|
||||||
|
*wrapped = false;
|
||||||
|
const QModelIndex sourceIndex = mapToSource(idx);
|
||||||
|
QModelIndex nextOrPrevSourceIndex = func(sourceIndex);
|
||||||
|
while (nextOrPrevSourceIndex != sourceIndex
|
||||||
|
&& !filterAcceptsRow(nextOrPrevSourceIndex.row(), nextOrPrevSourceIndex.parent())) {
|
||||||
|
nextOrPrevSourceIndex = func(nextOrPrevSourceIndex);
|
||||||
|
}
|
||||||
|
return mapFromSource(nextOrPrevSourceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SearchResultFilterModel::next(const QModelIndex &idx, bool includeGenerated,
|
||||||
|
bool *wrapped) const
|
||||||
|
{
|
||||||
|
return nextOrPrev(idx, wrapped, [this, includeGenerated, wrapped](const QModelIndex &index) {
|
||||||
|
return sourceModel()->next(index, includeGenerated, wrapped); });
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex SearchResultFilterModel::prev(const QModelIndex &idx, bool includeGenerated,
|
||||||
|
bool *wrapped) const
|
||||||
|
{
|
||||||
|
return nextOrPrev(idx, wrapped, [this, includeGenerated, wrapped](const QModelIndex &index) {
|
||||||
|
return sourceModel()->prev(index, includeGenerated, wrapped); });
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SearchResultFilterModel::filterAcceptsRow(int source_row,
|
||||||
|
const QModelIndex &source_parent) const
|
||||||
|
{
|
||||||
|
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
|
||||||
|
const SearchResultTreeItem * const item = SearchResultTreeModel::treeItemAtIndex(idx);
|
||||||
|
if (!item)
|
||||||
|
return false;
|
||||||
|
if (!m_filter)
|
||||||
|
return true;
|
||||||
|
if (item->item.userData.isValid())
|
||||||
|
return m_filter->matches(item->item);
|
||||||
|
const int childCount = sourceModel()->rowCount(idx);
|
||||||
|
for (int i = 0; i < childCount; ++i) {
|
||||||
|
if (filterAcceptsRow(i, idx))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResultTreeModel *SearchResultFilterModel::sourceModel() const
|
||||||
|
{
|
||||||
|
return static_cast<SearchResultTreeModel *>(QSortFilterProxyModel::sourceModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Core
|
||||||
|
|
||||||
|
#include <searchresulttreemodel.moc>
|
||||||
|
@@ -28,64 +28,43 @@
|
|||||||
#include "searchresultwindow.h"
|
#include "searchresultwindow.h"
|
||||||
#include "searchresultcolor.h"
|
#include "searchresultcolor.h"
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class SearchResultTreeItem;
|
class SearchResultTreeModel;
|
||||||
|
|
||||||
class SearchResultTreeModel : public QAbstractItemModel
|
class SearchResultFilterModel : public QSortFilterProxyModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SearchResultTreeModel(QObject *parent = nullptr);
|
SearchResultFilterModel(QObject *parent = nullptr);
|
||||||
~SearchResultTreeModel() override;
|
|
||||||
|
|
||||||
|
void setFilter(SearchResultFilter *filter);
|
||||||
void setShowReplaceUI(bool show);
|
void setShowReplaceUI(bool show);
|
||||||
void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
|
void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
|
||||||
|
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
QModelIndex parent(const QModelIndex &child) const override;
|
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
|
||||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
|
||||||
|
|
||||||
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
|
|
||||||
QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false, bool *wrapped = nullptr) const;
|
|
||||||
|
|
||||||
QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
QList<QModelIndex> addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
||||||
|
void clear();
|
||||||
|
QModelIndex next(const QModelIndex &idx, bool includeGenerated = false,
|
||||||
|
bool *wrapped = nullptr) const;
|
||||||
|
QModelIndex prev(const QModelIndex &idx, bool includeGenerated = false,
|
||||||
|
bool *wrapped = nullptr) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void jumpToSearchResult(const QString &fileName, int lineNumber,
|
void filterInvalidated();
|
||||||
int searchTermStart, int searchTermLength);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QModelIndex index(SearchResultTreeItem *item) const;
|
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||||
void addResultsToCurrentParent(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
|
||||||
QSet<SearchResultTreeItem *> addPath(const QStringList &path);
|
|
||||||
QVariant data(const SearchResultTreeItem *row, int role) const;
|
|
||||||
bool setCheckState(const QModelIndex &idx, Qt::CheckState checkState, bool firstCall = true);
|
|
||||||
QModelIndex nextIndex(const QModelIndex &idx, bool *wrapped = nullptr) const;
|
|
||||||
QModelIndex prevIndex(const QModelIndex &idx, bool *wrapped = nullptr) const;
|
|
||||||
static SearchResultTreeItem *treeItemAtIndex(const QModelIndex &idx);
|
|
||||||
|
|
||||||
SearchResultTreeItem *m_rootItem;
|
QModelIndex nextOrPrev(const QModelIndex &idx, bool *wrapped,
|
||||||
SearchResultTreeItem *m_currentParent;
|
const std::function<QModelIndex(const QModelIndex &)> &func) const;
|
||||||
SearchResultColors m_colors;
|
SearchResultTreeModel *sourceModel() const;
|
||||||
QModelIndex m_currentIndex;
|
|
||||||
QStringList m_currentPath; // the path that belongs to the current parent
|
SearchResultFilter *m_filter = nullptr;
|
||||||
QFont m_textEditorFont;
|
|
||||||
bool m_showReplaceUI;
|
|
||||||
bool m_editorFontIsUsed;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -28,18 +28,39 @@
|
|||||||
#include "searchresulttreemodel.h"
|
#include "searchresulttreemodel.h"
|
||||||
#include "searchresulttreeitemdelegate.h"
|
#include "searchresulttreeitemdelegate.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class FilterWidget : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FilterWidget(QWidget *parent, QWidget *content) : QWidget(parent, Qt::Popup)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
const auto layout = new QVBoxLayout(this);
|
||||||
|
layout->setContentsMargins(2, 2, 2, 2);
|
||||||
|
layout->setSpacing(2);
|
||||||
|
layout->addWidget(content);
|
||||||
|
setLayout(layout);
|
||||||
|
move(parent->mapToGlobal(QPoint(0, -sizeHint().height())));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SearchResultTreeView::SearchResultTreeView(QWidget *parent)
|
SearchResultTreeView::SearchResultTreeView(QWidget *parent)
|
||||||
: Utils::TreeView(parent)
|
: Utils::TreeView(parent)
|
||||||
, m_model(new SearchResultTreeModel(this))
|
, m_model(new SearchResultFilterModel(this))
|
||||||
, m_autoExpandResults(false)
|
, m_autoExpandResults(false)
|
||||||
{
|
{
|
||||||
setModel(m_model);
|
setModel(m_model);
|
||||||
|
connect(m_model, &SearchResultFilterModel::filterInvalidated,
|
||||||
|
this, &SearchResultTreeView::filterInvalidated);
|
||||||
|
|
||||||
setItemDelegate(new SearchResultTreeItemDelegate(8, this));
|
setItemDelegate(new SearchResultTreeItemDelegate(8, this));
|
||||||
setIndentation(14);
|
setIndentation(14);
|
||||||
setUniformRowHeights(true);
|
setUniformRowHeights(true);
|
||||||
@@ -80,6 +101,27 @@ void SearchResultTreeView::addResults(const QList<SearchResultItem> &items, Sear
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchResultTreeView::setFilter(SearchResultFilter *filter)
|
||||||
|
{
|
||||||
|
m_filter = filter;
|
||||||
|
if (m_filter)
|
||||||
|
m_filter->setParent(this);
|
||||||
|
m_model->setFilter(filter);
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SearchResultTreeView::hasFilter() const
|
||||||
|
{
|
||||||
|
return m_filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultTreeView::showFilterWidget(QWidget *parent)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(hasFilter(), return);
|
||||||
|
const auto optionsWidget = new FilterWidget(parent, m_filter->createWidget());
|
||||||
|
optionsWidget->show();
|
||||||
|
}
|
||||||
|
|
||||||
void SearchResultTreeView::keyPressEvent(QKeyEvent *event)
|
void SearchResultTreeView::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
if ((event->key() == Qt::Key_Return
|
if ((event->key() == Qt::Key_Return
|
||||||
@@ -118,7 +160,7 @@ void SearchResultTreeView::setTabWidth(int tabWidth)
|
|||||||
doItemsLayout();
|
doItemsLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchResultTreeModel *SearchResultTreeView::model() const
|
SearchResultFilterModel *SearchResultTreeView::model() const
|
||||||
{
|
{
|
||||||
return m_model;
|
return m_model;
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ class SearchResultColor;
|
|||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class SearchResultTreeModel;
|
class SearchResultFilterModel;
|
||||||
|
|
||||||
class SearchResultTreeView : public Utils::TreeView
|
class SearchResultTreeView : public Utils::TreeView
|
||||||
{
|
{
|
||||||
@@ -47,21 +47,27 @@ public:
|
|||||||
void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
|
void setTextEditorFont(const QFont &font, const SearchResultColors &colors);
|
||||||
void setTabWidth(int tabWidth);
|
void setTabWidth(int tabWidth);
|
||||||
|
|
||||||
SearchResultTreeModel *model() const;
|
SearchResultFilterModel *model() const;
|
||||||
void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
||||||
|
void setFilter(SearchResultFilter *filter);
|
||||||
|
bool hasFilter() const;
|
||||||
|
void showFilterWidget(QWidget *parent);
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
bool event(QEvent *e) override;
|
bool event(QEvent *e) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void jumpToSearchResult(const SearchResultItem &item);
|
void jumpToSearchResult(const SearchResultItem &item);
|
||||||
|
void filterInvalidated();
|
||||||
|
void filterChanged();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void clear();
|
void clear();
|
||||||
void emitJumpToSearchResult(const QModelIndex &index);
|
void emitJumpToSearchResult(const QModelIndex &index);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SearchResultTreeModel *m_model;
|
SearchResultFilterModel *m_model;
|
||||||
|
SearchResultFilter *m_filter = nullptr;
|
||||||
bool m_autoExpandResults;
|
bool m_autoExpandResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -134,6 +134,10 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
|
|||||||
m_searchResultTreeView = new SearchResultTreeView(this);
|
m_searchResultTreeView = new SearchResultTreeView(this);
|
||||||
m_searchResultTreeView->setFrameStyle(QFrame::NoFrame);
|
m_searchResultTreeView->setFrameStyle(QFrame::NoFrame);
|
||||||
m_searchResultTreeView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
m_searchResultTreeView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
|
connect(m_searchResultTreeView, &SearchResultTreeView::filterInvalidated,
|
||||||
|
this, &SearchResultWidget::filterInvalidated);
|
||||||
|
connect(m_searchResultTreeView, &SearchResultTreeView::filterChanged,
|
||||||
|
this, &SearchResultWidget::filterChanged);
|
||||||
auto agg = new Aggregation::Aggregate;
|
auto agg = new Aggregation::Aggregate;
|
||||||
agg->add(m_searchResultTreeView);
|
agg->add(m_searchResultTreeView);
|
||||||
agg->add(new ItemViewFind(m_searchResultTreeView,
|
agg->add(new ItemViewFind(m_searchResultTreeView,
|
||||||
@@ -443,6 +447,21 @@ void SearchResultWidget::setSearchAgainEnabled(bool enabled)
|
|||||||
m_searchAgainButton->setEnabled(enabled);
|
m_searchAgainButton->setEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchResultWidget::setFilter(SearchResultFilter *filter)
|
||||||
|
{
|
||||||
|
m_searchResultTreeView->setFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SearchResultWidget::hasFilter() const
|
||||||
|
{
|
||||||
|
return m_searchResultTreeView->hasFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultWidget::showFilterWidget(QWidget *parent)
|
||||||
|
{
|
||||||
|
m_searchResultTreeView->showFilterWidget(parent);
|
||||||
|
}
|
||||||
|
|
||||||
void SearchResultWidget::setReplaceEnabled(bool enabled)
|
void SearchResultWidget::setReplaceEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
m_replaceButton->setEnabled(enabled);
|
m_replaceButton->setEnabled(enabled);
|
||||||
@@ -513,7 +532,7 @@ void SearchResultWidget::searchAgain()
|
|||||||
QList<SearchResultItem> SearchResultWidget::checkedItems() const
|
QList<SearchResultItem> SearchResultWidget::checkedItems() const
|
||||||
{
|
{
|
||||||
QList<SearchResultItem> result;
|
QList<SearchResultItem> result;
|
||||||
SearchResultTreeModel *model = m_searchResultTreeView->model();
|
SearchResultFilterModel *model = m_searchResultTreeView->model();
|
||||||
const int fileCount = model->rowCount();
|
const int fileCount = model->rowCount();
|
||||||
for (int i = 0; i < fileCount; ++i) {
|
for (int i = 0; i < fileCount; ++i) {
|
||||||
QModelIndex fileIndex = model->index(i, 0);
|
QModelIndex fileIndex = model->index(i, 0);
|
||||||
|
@@ -90,7 +90,9 @@ public:
|
|||||||
|
|
||||||
void setSearchAgainSupported(bool supported);
|
void setSearchAgainSupported(bool supported);
|
||||||
void setSearchAgainEnabled(bool enabled);
|
void setSearchAgainEnabled(bool enabled);
|
||||||
|
void setFilter(SearchResultFilter *filter);
|
||||||
|
bool hasFilter() const;
|
||||||
|
void showFilterWidget(QWidget *parent);
|
||||||
void setReplaceEnabled(bool enabled);
|
void setReplaceEnabled(bool enabled);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@@ -107,6 +109,8 @@ signals:
|
|||||||
void restarted();
|
void restarted();
|
||||||
void visibilityChanged(bool visible);
|
void visibilityChanged(bool visible);
|
||||||
void requestPopup(bool focus);
|
void requestPopup(bool focus);
|
||||||
|
void filterInvalidated();
|
||||||
|
void filterChanged();
|
||||||
|
|
||||||
void navigateStateChanged();
|
void navigateStateChanged();
|
||||||
|
|
||||||
|
@@ -110,10 +110,12 @@ namespace Internal {
|
|||||||
void moveWidgetToTop();
|
void moveWidgetToTop();
|
||||||
void popupRequested(bool focus);
|
void popupRequested(bool focus);
|
||||||
void handleExpandCollapseToolButton(bool checked);
|
void handleExpandCollapseToolButton(bool checked);
|
||||||
|
void updateFilterButton();
|
||||||
|
|
||||||
SearchResultWindow *q;
|
SearchResultWindow *q;
|
||||||
QList<Internal::SearchResultWidget *> m_searchResultWidgets;
|
QList<Internal::SearchResultWidget *> m_searchResultWidgets;
|
||||||
QToolButton *m_expandCollapseButton;
|
QToolButton *m_expandCollapseButton;
|
||||||
|
QToolButton *m_filterButton;
|
||||||
QToolButton *m_newSearchButton;
|
QToolButton *m_newSearchButton;
|
||||||
QAction *m_expandCollapseAction;
|
QAction *m_expandCollapseAction;
|
||||||
static const bool m_initiallyExpand;
|
static const bool m_initiallyExpand;
|
||||||
@@ -168,6 +170,11 @@ namespace Internal {
|
|||||||
cmd->setAttribute(Command::CA_UpdateText);
|
cmd->setAttribute(Command::CA_UpdateText);
|
||||||
m_expandCollapseButton->setDefaultAction(cmd->action());
|
m_expandCollapseButton->setDefaultAction(cmd->action());
|
||||||
|
|
||||||
|
m_filterButton = new QToolButton(m_widget);
|
||||||
|
m_filterButton->setText(tr("Filter Results"));
|
||||||
|
m_filterButton->setIcon(Utils::Icons::FILTER.icon());
|
||||||
|
m_filterButton->setEnabled(false);
|
||||||
|
|
||||||
QAction *newSearchAction = new QAction(tr("New Search"), this);
|
QAction *newSearchAction = new QAction(tr("New Search"), this);
|
||||||
newSearchAction->setIcon(Utils::Icons::NEWSEARCH_TOOLBAR.icon());
|
newSearchAction->setIcon(Utils::Icons::NEWSEARCH_TOOLBAR.icon());
|
||||||
cmd = ActionManager::command(Constants::ADVANCED_FIND);
|
cmd = ActionManager::command(Constants::ADVANCED_FIND);
|
||||||
@@ -178,6 +185,13 @@ namespace Internal {
|
|||||||
connect(m_expandCollapseAction, &QAction::toggled,
|
connect(m_expandCollapseAction, &QAction::toggled,
|
||||||
this, &SearchResultWindowPrivate::handleExpandCollapseToolButton);
|
this, &SearchResultWindowPrivate::handleExpandCollapseToolButton);
|
||||||
|
|
||||||
|
connect(m_filterButton, &QToolButton::clicked, this, [this] {
|
||||||
|
if (!isSearchVisible())
|
||||||
|
return;
|
||||||
|
m_searchResultWidgets.at(visibleSearchIndex())->showFilterWidget(m_filterButton);
|
||||||
|
});
|
||||||
|
connect(m_widget, &QStackedWidget::currentChanged,
|
||||||
|
this, &SearchResultWindowPrivate::updateFilterButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchResultWindowPrivate::setCurrentIndex(int index, bool focus)
|
void SearchResultWindowPrivate::setCurrentIndex(int index, bool focus)
|
||||||
@@ -445,7 +459,7 @@ QWidget *SearchResultWindow::outputWidget(QWidget *)
|
|||||||
*/
|
*/
|
||||||
QList<QWidget*> SearchResultWindow::toolBarWidgets() const
|
QList<QWidget*> SearchResultWindow::toolBarWidgets() const
|
||||||
{
|
{
|
||||||
return {d->m_expandCollapseButton, d->m_newSearchButton, d->m_spacer,
|
return {d->m_expandCollapseButton, d->m_filterButton, d->m_newSearchButton, d->m_spacer,
|
||||||
d->m_historyLabel, d->m_spacer2, d->m_recentSearchesBox};
|
d->m_historyLabel, d->m_spacer2, d->m_recentSearchesBox};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,6 +510,12 @@ SearchResult *SearchResultWindow::startNewSearch(const QString &label,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto widget = new SearchResultWidget;
|
auto widget = new SearchResultWidget;
|
||||||
|
connect(widget, &SearchResultWidget::filterInvalidated, this, [this, widget] {
|
||||||
|
if (widget == d->m_searchResultWidgets.at(d->visibleSearchIndex()))
|
||||||
|
d->handleExpandCollapseToolButton(d->m_expandCollapseButton->isChecked());
|
||||||
|
});
|
||||||
|
connect(widget, &SearchResultWidget::filterChanged,
|
||||||
|
d, &SearchResultWindowPrivate::updateFilterButton);
|
||||||
d->m_searchResultWidgets.prepend(widget);
|
d->m_searchResultWidgets.prepend(widget);
|
||||||
d->m_widget->insertWidget(1, widget);
|
d->m_widget->insertWidget(1, widget);
|
||||||
connect(widget, &SearchResultWidget::navigateStateChanged,
|
connect(widget, &SearchResultWidget::navigateStateChanged,
|
||||||
@@ -615,6 +635,12 @@ void SearchResultWindowPrivate::handleExpandCollapseToolButton(bool checked)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchResultWindowPrivate::updateFilterButton()
|
||||||
|
{
|
||||||
|
m_filterButton->setEnabled(isSearchVisible()
|
||||||
|
&& m_searchResultWidgets.at(visibleSearchIndex())->hasFilter());
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
@@ -833,6 +859,11 @@ void SearchResult::addResults(const QList<SearchResultItem> &items, AddMode mode
|
|||||||
emit countChanged(m_widget->count());
|
emit countChanged(m_widget->count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchResult::setFilter(SearchResultFilter *filter)
|
||||||
|
{
|
||||||
|
m_widget->setFilter(filter);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Notifies the \uicontrol {Search Results} output pane that the current search
|
Notifies the \uicontrol {Search Results} output pane that the current search
|
||||||
has been \a canceled, and the UI should reflect that.
|
has been \a canceled, and the UI should reflect that.
|
||||||
|
@@ -45,6 +45,18 @@ namespace Internal {
|
|||||||
}
|
}
|
||||||
class SearchResultWindow;
|
class SearchResultWindow;
|
||||||
|
|
||||||
|
class CORE_EXPORT SearchResultFilter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual QWidget *createWidget() = 0;
|
||||||
|
virtual bool matches(const SearchResultItem &item) const = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void filterChanged();
|
||||||
|
};
|
||||||
|
|
||||||
class CORE_EXPORT SearchResult : public QObject
|
class CORE_EXPORT SearchResult : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -77,6 +89,7 @@ public slots:
|
|||||||
const QVariant &userData = QVariant(),
|
const QVariant &userData = QVariant(),
|
||||||
SearchResultColor::Style style = SearchResultColor::Style::Default);
|
SearchResultColor::Style style = SearchResultColor::Style::Default);
|
||||||
void addResults(const QList<SearchResultItem> &items, AddMode mode);
|
void addResults(const QList<SearchResultItem> &items, AddMode mode);
|
||||||
|
void setFilter(SearchResultFilter *filter); // Takes ownership
|
||||||
void finishSearch(bool canceled);
|
void finishSearch(bool canceled);
|
||||||
void setTextToReplace(const QString &textToReplace);
|
void setTextToReplace(const QString &textToReplace);
|
||||||
void restart();
|
void restart();
|
||||||
|
@@ -49,6 +49,7 @@
|
|||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -169,6 +170,58 @@ static QList<QByteArray> fullIdForSymbol(CPlusPlus::Symbol *symbol)
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
class Filter : public Core::SearchResultFilter
|
||||||
|
{
|
||||||
|
QWidget *createWidget() override
|
||||||
|
{
|
||||||
|
const auto widget = new QWidget;
|
||||||
|
const auto layout = new QVBoxLayout(widget);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
const auto readsCheckBox = new QCheckBox(tr("Reads"));
|
||||||
|
readsCheckBox->setChecked(m_showReads);
|
||||||
|
const auto writesCheckBox = new QCheckBox(tr("Writes"));
|
||||||
|
writesCheckBox->setChecked(m_showWrites);
|
||||||
|
const auto otherCheckBox = new QCheckBox(tr("Other"));
|
||||||
|
otherCheckBox->setChecked(m_showOther);
|
||||||
|
layout->addWidget(readsCheckBox);
|
||||||
|
layout->addWidget(writesCheckBox);
|
||||||
|
layout->addWidget(otherCheckBox);
|
||||||
|
connect(readsCheckBox, &QCheckBox::toggled,
|
||||||
|
this, [this](bool checked) { setValue(m_showReads, checked); });
|
||||||
|
connect(writesCheckBox, &QCheckBox::toggled,
|
||||||
|
this, [this](bool checked) { setValue(m_showWrites, checked); });
|
||||||
|
connect(otherCheckBox, &QCheckBox::toggled,
|
||||||
|
this, [this](bool checked) { setValue(m_showOther, checked); });
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool matches(const SearchResultItem &item) const override
|
||||||
|
{
|
||||||
|
switch (static_cast<CPlusPlus::Usage::Type>(item.userData.toInt())) {
|
||||||
|
case CPlusPlus::Usage::Type::Read:
|
||||||
|
return m_showReads;
|
||||||
|
case CPlusPlus::Usage::Type::Write:
|
||||||
|
case CPlusPlus::Usage::Type::WritableRef:
|
||||||
|
case CPlusPlus::Usage::Type::Initialization:
|
||||||
|
return m_showWrites;
|
||||||
|
case CPlusPlus::Usage::Type::Declaration:
|
||||||
|
case CPlusPlus::Usage::Type::Other:
|
||||||
|
return m_showOther;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setValue(bool &member, bool value)
|
||||||
|
{
|
||||||
|
member = value;
|
||||||
|
emit filterChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool m_showReads = true;
|
||||||
|
bool m_showWrites = true;
|
||||||
|
bool m_showOther = true;
|
||||||
|
};
|
||||||
|
|
||||||
class ProcessFile
|
class ProcessFile
|
||||||
{
|
{
|
||||||
const WorkingCopy workingCopy;
|
const WorkingCopy workingCopy;
|
||||||
@@ -339,6 +392,7 @@ void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
|
|||||||
SearchResultWindow::PreserveCaseDisabled,
|
SearchResultWindow::PreserveCaseDisabled,
|
||||||
QLatin1String("CppEditor"));
|
QLatin1String("CppEditor"));
|
||||||
search->setTextToReplace(replacement);
|
search->setTextToReplace(replacement);
|
||||||
|
search->setFilter(new Filter);
|
||||||
auto renameFilesCheckBox = new QCheckBox();
|
auto renameFilesCheckBox = new QCheckBox();
|
||||||
renameFilesCheckBox->setVisible(false);
|
renameFilesCheckBox->setVisible(false);
|
||||||
search->setAdditionalReplaceWidget(renameFilesCheckBox);
|
search->setAdditionalReplaceWidget(renameFilesCheckBox);
|
||||||
@@ -570,7 +624,8 @@ static void displayResults(SearchResult *search, QFutureWatcher<CPlusPlus::Usage
|
|||||||
for (int index = first; index != last; ++index) {
|
for (int index = first; index != last; ++index) {
|
||||||
const CPlusPlus::Usage result = watcher->future().resultAt(index);
|
const CPlusPlus::Usage result = watcher->future().resultAt(index);
|
||||||
search->addResult(result.path.toString(), result.line, result.lineText,
|
search->addResult(result.path.toString(), result.line, result.lineText,
|
||||||
result.col, result.len, {}, colorStyleForUsageType(result.type));
|
result.col, result.len, int(result.type),
|
||||||
|
colorStyleForUsageType(result.type));
|
||||||
|
|
||||||
if (parameters.prettySymbolName.isEmpty())
|
if (parameters.prettySymbolName.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
Reference in New Issue
Block a user