Commit 91239baa authored by Eike Ziller's avatar Eike Ziller

Locator filters: Fix various thread-safety issues

Introduces a "prepareSearch" method for locator filters that is called on
the UI thread prior to the threaded matching.
Fix various small thread-safety issues in the various filters.

Change-Id: If5ae7d205e126d367420936a93f8d9a84496edb8
Reviewed-by: default avatarDaniel Teske <daniel.teske@digia.com>
parent d6c73653
...@@ -46,7 +46,6 @@ BaseFileFilter::BaseFileFilter() ...@@ -46,7 +46,6 @@ BaseFileFilter::BaseFileFilter()
QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &origEntry) QList<LocatorFilterEntry> BaseFileFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &origEntry)
{ {
updateFiles();
QList<LocatorFilterEntry> betterEntries; QList<LocatorFilterEntry> betterEntries;
QList<LocatorFilterEntry> goodEntries; QList<LocatorFilterEntry> goodEntries;
QString needle = trimWildcards(QDir::fromNativeSeparators(origEntry)); QString needle = trimWildcards(QDir::fromNativeSeparators(origEntry));
...@@ -120,7 +119,3 @@ void BaseFileFilter::generateFileNames() ...@@ -120,7 +119,3 @@ void BaseFileFilter::generateFileNames()
} }
m_forceNewSearchList = true; m_forceNewSearchList = true;
} }
void BaseFileFilter::updateFiles()
{
}
...@@ -46,10 +46,10 @@ public: ...@@ -46,10 +46,10 @@ public:
void accept(Core::LocatorFilterEntry selection) const; void accept(Core::LocatorFilterEntry selection) const;
protected: protected:
// runs in non-UI thread /* Generates the file names from the list of file paths in m_files. */
virtual void updateFiles();
void generateFileNames(); void generateFileNames();
/* Subclasses should update the file list latest in their prepareSearch method. */
inline QStringList &files() { return m_files; } inline QStringList &files() { return m_files; }
inline const QStringList &files() const { return m_files; } inline const QStringList &files() const { return m_files; }
......
...@@ -63,6 +63,18 @@ FileSystemFilter::FileSystemFilter(LocatorWidget *locatorWidget) ...@@ -63,6 +63,18 @@ FileSystemFilter::FileSystemFilter(LocatorWidget *locatorWidget)
setIncludedByDefault(false); setIncludedByDefault(false);
} }
void FileSystemFilter::prepareSearch(const QString &entry)
{
Q_UNUSED(entry)
IDocument *document= EditorManager::currentDocument();
if (document && !document->filePath().isEmpty()) {
QFileInfo info(document->filePath());
m_currentDocumentDirectory = info.absolutePath() + QLatin1Char('/');
} else {
m_currentDocumentDirectory.clear();
}
}
QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
{ {
QList<LocatorFilterEntry> goodEntries; QList<LocatorFilterEntry> goodEntries;
...@@ -72,15 +84,10 @@ QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<Core::Lo ...@@ -72,15 +84,10 @@ QList<LocatorFilterEntry> FileSystemFilter::matchesFor(QFutureInterface<Core::Lo
QString directory = entryInfo.path(); QString directory = entryInfo.path();
QString filePath = entryInfo.filePath(); QString filePath = entryInfo.filePath();
if (entryInfo.isRelative()) { if (entryInfo.isRelative()) {
if (filePath.startsWith(QLatin1String("~/"))) { if (filePath.startsWith(QLatin1String("~/")))
directory.replace(0, 1, QDir::homePath()); directory.replace(0, 1, QDir::homePath());
} else { else if (!m_currentDocumentDirectory.isEmpty())
IDocument *document= EditorManager::currentDocument(); directory.prepend(m_currentDocumentDirectory);
if (document && !document->filePath().isEmpty()) {
QFileInfo info(document->filePath());
directory.prepend(info.absolutePath() + QLatin1Char('/'));
}
}
} }
QDir dirInfo(directory); QDir dirInfo(directory);
QDir::Filters dirFilter = QDir::Dirs|QDir::Drives|QDir::NoDot; QDir::Filters dirFilter = QDir::Dirs|QDir::Drives|QDir::NoDot;
......
...@@ -49,6 +49,7 @@ class FileSystemFilter : public Core::ILocatorFilter ...@@ -49,6 +49,7 @@ class FileSystemFilter : public Core::ILocatorFilter
public: public:
explicit FileSystemFilter(LocatorWidget *locatorWidget); explicit FileSystemFilter(LocatorWidget *locatorWidget);
void prepareSearch(const QString &entry);
QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry); QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
void accept(Core::LocatorFilterEntry selection) const; void accept(Core::LocatorFilterEntry selection) const;
QByteArray saveState() const; QByteArray saveState() const;
...@@ -59,6 +60,7 @@ public: ...@@ -59,6 +60,7 @@ public:
private: private:
LocatorWidget *m_locatorWidget; LocatorWidget *m_locatorWidget;
bool m_includeHidden; bool m_includeHidden;
QString m_currentDocumentDirectory;
}; };
} // namespace Internal } // namespace Internal
......
...@@ -53,6 +53,11 @@ QString ILocatorFilter::shortcutString() const ...@@ -53,6 +53,11 @@ QString ILocatorFilter::shortcutString() const
return m_shortcut; return m_shortcut;
} }
void ILocatorFilter::prepareSearch(const QString &entry)
{
Q_UNUSED(entry)
}
void ILocatorFilter::setShortcutString(const QString &shortcut) void ILocatorFilter::setShortcutString(const QString &shortcut)
{ {
m_shortcut = shortcut; m_shortcut = shortcut;
......
...@@ -100,7 +100,13 @@ public: ...@@ -100,7 +100,13 @@ public:
/* String to type to use this filter exclusively. */ /* String to type to use this filter exclusively. */
QString shortcutString() const; QString shortcutString() const;
/* List of matches for the given user entry. */ /* Called on the main thread before matchesFor is called in a separate thread.
Can be used to perform actions that need to be done in the main thread before actually
running the search. */
virtual void prepareSearch(const QString &entry);
/* List of matches for the given user entry. This runs in a separate thread, but only
a single thread at a time. */
virtual QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) = 0; virtual QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) = 0;
/* User has selected the given entry that belongs to this filter. */ /* User has selected the given entry that belongs to this filter. */
......
...@@ -57,9 +57,6 @@ public: ...@@ -57,9 +57,6 @@ public:
} }
void refresh(QFutureInterface<void> &) {} void refresh(QFutureInterface<void> &) {}
protected:
void updateFiles() {}
}; };
inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); } inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); }
......
...@@ -52,11 +52,12 @@ LocatorFiltersFilter::LocatorFiltersFilter(Locator *plugin, ...@@ -52,11 +52,12 @@ LocatorFiltersFilter::LocatorFiltersFilter(Locator *plugin,
setConfigurable(false); setConfigurable(false);
} }
QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) void LocatorFiltersFilter::prepareSearch(const QString &entry)
{ {
QList<LocatorFilterEntry> entries; m_filterShortcutStrings.clear();
m_filterDisplayNames.clear();
if (!entry.isEmpty()) if (!entry.isEmpty())
return entries; return;
QMap<QString, ILocatorFilter *> uniqueFilters; QMap<QString, ILocatorFilter *> uniqueFilters;
foreach (ILocatorFilter *filter, m_plugin->filters()) { foreach (ILocatorFilter *filter, m_plugin->filters()) {
...@@ -65,27 +66,36 @@ QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<Core ...@@ -65,27 +66,36 @@ QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<Core
} }
foreach (ILocatorFilter *filter, uniqueFilters) { foreach (ILocatorFilter *filter, uniqueFilters) {
if (future.isCanceled())
break;
if (!filter->shortcutString().isEmpty() && !filter->isHidden() && filter->isEnabled()) { if (!filter->shortcutString().isEmpty() && !filter->isHidden() && filter->isEnabled()) {
LocatorFilterEntry filterEntry(this, m_filterShortcutStrings.append(filter->shortcutString());
filter->shortcutString(), m_filterDisplayNames.append(filter->displayName());
QVariant::fromValue(filter),
m_icon);
filterEntry.extraInfo = filter->displayName();
entries.append(filterEntry);
} }
} }
}
QList<LocatorFilterEntry> LocatorFiltersFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry)
{
Q_UNUSED(entry) // search is already done in the GUI thread in prepareSearch
QList<LocatorFilterEntry> entries;
for (int i = 0; i < m_filterShortcutStrings.size(); ++i) {
if (future.isCanceled())
break;
LocatorFilterEntry filterEntry(this,
m_filterShortcutStrings.at(i),
m_filterShortcutStrings.at(i),
m_icon);
filterEntry.extraInfo = m_filterDisplayNames.at(i);
entries.append(filterEntry);
}
return entries; return entries;
} }
void LocatorFiltersFilter::accept(LocatorFilterEntry selection) const void LocatorFiltersFilter::accept(LocatorFilterEntry selection) const
{ {
ILocatorFilter *filter = selection.internalData.value<ILocatorFilter *>(); const QString shortcutString = selection.internalData.toString();
if (filter) if (!shortcutString.isEmpty())
m_locatorWidget->show(filter->shortcutString() + QLatin1Char(' '), m_locatorWidget->show(shortcutString + QLatin1Char(' '),
filter->shortcutString().length() + 1); shortcutString.length() + 1);
} }
void LocatorFiltersFilter::refresh(QFutureInterface<void> &future) void LocatorFiltersFilter::refresh(QFutureInterface<void> &future)
......
...@@ -53,6 +53,7 @@ public: ...@@ -53,6 +53,7 @@ public:
LocatorWidget *locatorWidget); LocatorWidget *locatorWidget);
// ILocatorFilter // ILocatorFilter
void prepareSearch(const QString &entry);
QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry); QList<LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
void accept(LocatorFilterEntry selection) const; void accept(LocatorFilterEntry selection) const;
void refresh(QFutureInterface<void> &future); void refresh(QFutureInterface<void> &future);
...@@ -60,6 +61,8 @@ public: ...@@ -60,6 +61,8 @@ public:
private: private:
Locator *m_plugin; Locator *m_plugin;
LocatorWidget *m_locatorWidget; LocatorWidget *m_locatorWidget;
QStringList m_filterShortcutStrings;
QStringList m_filterDisplayNames;
QIcon m_icon; QIcon m_icon;
}; };
......
...@@ -491,6 +491,9 @@ void LocatorWidget::updateCompletionList(const QString &text) ...@@ -491,6 +491,9 @@ void LocatorWidget::updateCompletionList(const QString &text)
QString searchText; QString searchText;
const QList<ILocatorFilter *> filters = filtersFor(text, searchText); const QList<ILocatorFilter *> filters = filtersFor(text, searchText);
foreach (ILocatorFilter *filter, filters)
filter->prepareSearch(searchText);
QFuture<LocatorFilterEntry> future = QtConcurrent::run(runSearch, filters, searchText); QFuture<LocatorFilterEntry> future = QtConcurrent::run(runSearch, filters, searchText);
m_entriesWatcher->setFuture(future); m_entriesWatcher->setFuture(future);
} }
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <QFileInfo> #include <QFileInfo>
#include <QMutexLocker>
using namespace Core; using namespace Core;
using namespace Core; using namespace Core;
...@@ -67,7 +68,7 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Core: ...@@ -67,7 +68,7 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Core:
if (!regexp.isValid()) if (!regexp.isValid())
return goodEntries; return goodEntries;
const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry); const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry);
foreach (const DocumentModel::Entry &editorEntry, m_editors) { foreach (const DocumentModel::Entry &editorEntry, editors()) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
QString fileName = editorEntry.fileName(); QString fileName = editorEntry.fileName();
...@@ -90,6 +91,7 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Core: ...@@ -90,6 +91,7 @@ QList<LocatorFilterEntry> OpenDocumentsFilter::matchesFor(QFutureInterface<Core:
void OpenDocumentsFilter::refreshInternally() void OpenDocumentsFilter::refreshInternally()
{ {
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
m_editors.clear(); m_editors.clear();
foreach (DocumentModel::Entry *e, DocumentModel::entries()) { foreach (DocumentModel::Entry *e, DocumentModel::entries()) {
DocumentModel::Entry entry; DocumentModel::Entry entry;
...@@ -101,6 +103,12 @@ void OpenDocumentsFilter::refreshInternally() ...@@ -101,6 +103,12 @@ void OpenDocumentsFilter::refreshInternally()
} }
} }
QList<DocumentModel::Entry> OpenDocumentsFilter::editors() const
{
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
return m_editors;
}
void OpenDocumentsFilter::refresh(QFutureInterface<void> &future) void OpenDocumentsFilter::refresh(QFutureInterface<void> &future)
{ {
Q_UNUSED(future) Q_UNUSED(future)
......
...@@ -34,9 +34,10 @@ ...@@ -34,9 +34,10 @@
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <QString>
#include <QList>
#include <QFutureInterface> #include <QFutureInterface>
#include <QList>
#include <QMutex>
#include <QString>
namespace Core { namespace Core {
namespace Internal { namespace Internal {
...@@ -55,6 +56,9 @@ public slots: ...@@ -55,6 +56,9 @@ public slots:
void refreshInternally(); void refreshInternally();
private: private:
QList<Core::DocumentModel::Entry> editors() const;
mutable QMutex m_mutex;
QList<Core::DocumentModel::Entry> m_editors; QList<Core::DocumentModel::Entry> m_editors;
}; };
......
...@@ -59,16 +59,19 @@ HelpIndexFilter::~HelpIndexFilter() ...@@ -59,16 +59,19 @@ HelpIndexFilter::~HelpIndexFilter()
{ {
} }
QList<LocatorFilterEntry> HelpIndexFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry) void HelpIndexFilter::prepareSearch(const QString &entry)
{ {
QStringList keywords;
if (entry.length() < 2) if (entry.length() < 2)
keywords = Core::HelpManager::findKeywords(entry, caseSensitivity(entry), 200); m_keywords = Core::HelpManager::findKeywords(entry, caseSensitivity(entry), 200);
else else
keywords = Core::HelpManager::findKeywords(entry, caseSensitivity(entry)); m_keywords = Core::HelpManager::findKeywords(entry, caseSensitivity(entry));
}
QList<LocatorFilterEntry> HelpIndexFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, const QString &entry)
{
Q_UNUSED(entry) // search is already done in the GUI thread in prepareSearch
QList<LocatorFilterEntry> entries; QList<LocatorFilterEntry> entries;
foreach (const QString &keyword, keywords) { foreach (const QString &keyword, m_keywords) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
entries.append(LocatorFilterEntry(this, keyword, QVariant(), m_icon)); entries.append(LocatorFilterEntry(this, keyword, QVariant(), m_icon));
......
...@@ -46,6 +46,7 @@ public: ...@@ -46,6 +46,7 @@ public:
~HelpIndexFilter(); ~HelpIndexFilter();
// ILocatorFilter // ILocatorFilter
void prepareSearch(const QString &entry);
QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry); QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry);
void accept(Core::LocatorFilterEntry selection) const; void accept(Core::LocatorFilterEntry selection) const;
void refresh(QFutureInterface<void> &future); void refresh(QFutureInterface<void> &future);
...@@ -55,6 +56,7 @@ signals: ...@@ -55,6 +56,7 @@ signals:
private: private:
QIcon m_icon; QIcon m_icon;
QStringList m_keywords;
}; };
} // namespace Internal } // namespace Internal
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "remotehelpfilter.h" #include "remotehelpfilter.h"
#include <QMutexLocker>
#include <QUrl> #include <QUrl>
namespace Help { namespace Help {
...@@ -99,7 +100,7 @@ RemoteHelpFilter::~RemoteHelpFilter() ...@@ -99,7 +100,7 @@ RemoteHelpFilter::~RemoteHelpFilter()
QList<Core::LocatorFilterEntry> RemoteHelpFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &pattern) QList<Core::LocatorFilterEntry> RemoteHelpFilter::matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, const QString &pattern)
{ {
QList<Core::LocatorFilterEntry> entries; QList<Core::LocatorFilterEntry> entries;
foreach (const QString &url, m_remoteUrls) { foreach (const QString &url, remoteUrls()) {
if (future.isCanceled()) if (future.isCanceled())
break; break;
...@@ -156,6 +157,7 @@ bool RemoteHelpFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) ...@@ -156,6 +157,7 @@ bool RemoteHelpFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
Q_UNUSED(needsRefresh) Q_UNUSED(needsRefresh)
RemoteFilterOptions optionsDialog(this, parent); RemoteFilterOptions optionsDialog(this, parent);
if (optionsDialog.exec() == QDialog::Accepted) { if (optionsDialog.exec() == QDialog::Accepted) {
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
m_remoteUrls.clear(); m_remoteUrls.clear();
setIncludedByDefault(!optionsDialog.m_ui.limitCheck->isChecked()); setIncludedByDefault(!optionsDialog.m_ui.limitCheck->isChecked());
setShortcutString(optionsDialog.m_ui.shortcutEdit->text().trimmed()); setShortcutString(optionsDialog.m_ui.shortcutEdit->text().trimmed());
...@@ -166,5 +168,11 @@ bool RemoteHelpFilter::openConfigDialog(QWidget *parent, bool &needsRefresh) ...@@ -166,5 +168,11 @@ bool RemoteHelpFilter::openConfigDialog(QWidget *parent, bool &needsRefresh)
return true; return true;
} }
QStringList RemoteHelpFilter::remoteUrls() const
{
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
return m_remoteUrls;
}
} // namespace Internal } // namespace Internal
} // namespace Help } // namespace Help
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <coreplugin/locator/ilocatorfilter.h> #include <coreplugin/locator/ilocatorfilter.h>
#include <QIcon> #include <QIcon>
#include <QMutex>
namespace Help { namespace Help {
namespace Internal { namespace Internal {
...@@ -54,7 +55,7 @@ public: ...@@ -54,7 +55,7 @@ public:
bool restoreState(const QByteArray &state); bool restoreState(const QByteArray &state);
bool openConfigDialog(QWidget *parent, bool &needsRefresh); bool openConfigDialog(QWidget *parent, bool &needsRefresh);
QStringList remoteUrls() const { return m_remoteUrls; } QStringList remoteUrls() const;
signals: signals:
void linkActivated(const QUrl &url) const; void linkActivated(const QUrl &url) const;
...@@ -62,6 +63,7 @@ signals: ...@@ -62,6 +63,7 @@ signals:
private: private:
QIcon m_icon; QIcon m_icon;
QStringList m_remoteUrls; QStringList m_remoteUrls;
mutable QMutex m_mutex;
}; };
class RemoteFilterOptions : public QDialog class RemoteFilterOptions : public QDialog
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <QMutexLocker>
using namespace Core; using namespace Core;
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal; using namespace ProjectExplorer::Internal;
...@@ -52,11 +54,14 @@ AllProjectsFilter::AllProjectsFilter() : m_filesUpToDate(false) ...@@ -52,11 +54,14 @@ AllProjectsFilter::AllProjectsFilter() : m_filesUpToDate(false)
void AllProjectsFilter::markFilesAsOutOfDate() void AllProjectsFilter::markFilesAsOutOfDate()
{ {
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
m_filesUpToDate = false; m_filesUpToDate = false;
} }
void AllProjectsFilter::updateFilesImpl() void AllProjectsFilter::prepareSearch(const QString &entry)
{ {
Q_UNUSED(entry)
QMutexLocker lock(&m_mutex); Q_UNUSED(lock)
if (m_filesUpToDate) if (m_filesUpToDate)
return; return;
files().clear(); files().clear();
...@@ -67,13 +72,8 @@ void AllProjectsFilter::updateFilesImpl() ...@@ -67,13 +72,8 @@ void AllProjectsFilter::updateFilesImpl()
m_filesUpToDate = true; m_filesUpToDate = true;
} }
void AllProjectsFilter::updateFiles()
{
QMetaObject::invokeMethod(this, "updateFilesImpl", Qt::BlockingQueuedConnection);
}
void AllProjectsFilter::refresh(QFutureInterface<void> &future) void AllProjectsFilter::refresh(QFutureInterface<void> &future)
{ {
Q_UNUSED(future) Q_UNUSED(future)
QMetaObject::invokeMethod(this, "markFilesAsOutOfDate", Qt::BlockingQueuedConnection); markFilesAsOutOfDate();
} }
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <coreplugin/locator/basefilefilter.h> #include <coreplugin/locator/basefilefilter.h>
#include <QMutex>
#include <QFutureInterface> #include <QFutureInterface>
namespace ProjectExplorer { namespace ProjectExplorer {
...@@ -44,16 +45,14 @@ class AllProjectsFilter : public Core::BaseFileFilter ...@@ -44,16 +45,14 @@ class AllProjectsFilter : public Core::BaseFileFilter
public: public:
AllProjectsFilter(); AllProjectsFilter();
void refresh(QFutureInterface<void> &future);