diff --git a/src/ui/form/prog/programswindow.cpp b/src/ui/form/prog/programswindow.cpp
index 617cbec4..0e1d5f75 100644
--- a/src/ui/form/prog/programswindow.cpp
+++ b/src/ui/form/prog/programswindow.cpp
@@ -142,6 +142,9 @@ void ProgramsWindow::retranslateUi()
m_btBlockApp->setText(tr("Block"));
m_btRemoveApp->setText(tr("Remove"));
m_editSearch->setPlaceholderText(tr("Search"));
+ m_btFilter->setToolTip(tr("Filters"));
+ m_btClearFilter->setToolTip(tr("Clear Filters"));
+ m_cbFilterWildcard->setText(tr("Wildcard Paths"));
m_btGroups->setText(tr("Groups"));
m_btServices->setText(tr("Services"));
@@ -208,6 +211,10 @@ QLayout *ProgramsWindow::setupHeader()
// Search edit line
setupEditSearch();
+ // Filter button
+ setupFilter();
+ setupClearFilter();
+
// Groups button
m_btGroups = ControlUtil::createFlatToolButton(":/icons/application_double.png");
@@ -232,6 +239,8 @@ QLayout *ProgramsWindow::setupHeader()
layout->addWidget(m_btRemoveApp);
layout->addWidget(ControlUtil::createVSeparator());
layout->addWidget(m_editSearch);
+ layout->addWidget(m_btFilter);
+ layout->addWidget(m_btClearFilter);
layout->addStretch();
layout->addWidget(m_btGroups);
layout->addWidget(m_btServices);
@@ -310,6 +319,48 @@ void ProgramsWindow::setupEditSearch()
connect(this, &ProgramsWindow::aboutToShow, m_editSearch, qOverload<>(&QWidget::setFocus));
}
+void ProgramsWindow::setupFilter()
+{
+ m_cbFilterWildcard = new QCheckBox();
+ m_cbFilterWildcard->setIcon(IconCache::icon(":/icons/coding.png"));
+
+ m_cbFilterWildcard->setTristate(true);
+ m_cbFilterWildcard->setCheckState(Qt::PartiallyChecked);
+
+ connect(m_cbFilterWildcard, &QCheckBox::clicked, this, [&] {
+ appListModel()->setFilterValue(
+ AppListModel::FilterWildcard, m_cbFilterWildcard->checkState());
+ });
+
+ auto layout = ControlUtil::createVLayoutByWidgets({ m_cbFilterWildcard });
+
+ auto menu = ControlUtil::createMenuByLayout(layout, this);
+
+ m_btFilter = ControlUtil::createButton(":/icons/filter.png");
+ m_btFilter->setMenu(menu);
+}
+
+void ProgramsWindow::setupClearFilter()
+{
+ m_btClearFilter = new QPushButton(
+ GuiUtil::overlayIcon(":/icons/filter.png", ":/icons/cross.png"), QString());
+
+ connect(m_btClearFilter, &QPushButton::clicked, this, [&] {
+ appListModel()->clearFilters();
+
+ m_cbFilterWildcard->setCheckState(Qt::PartiallyChecked);
+ });
+
+ const auto refreshClearFilter = [&] {
+ const auto isEmpty = (appListModel()->filters() == AppListModel::FilterNone);
+ m_btClearFilter->setVisible(!isEmpty);
+ };
+
+ refreshClearFilter();
+
+ connect(appListModel(), &AppListModel::filtersChanged, this, refreshClearFilter);
+}
+
void ProgramsWindow::setupTableApps()
{
m_appListView = new TableView();
diff --git a/src/ui/form/prog/programswindow.h b/src/ui/form/prog/programswindow.h
index 44137568..617294ca 100644
--- a/src/ui/form/prog/programswindow.h
+++ b/src/ui/form/prog/programswindow.h
@@ -64,6 +64,8 @@ private:
QLayout *setupHeader();
void setupEditMenu();
void setupEditSearch();
+ void setupFilter();
+ void setupClearFilter();
void setupTableApps();
void setupTableAppsHeader();
void setupAppInfoRow();
@@ -106,6 +108,9 @@ private:
QToolButton *m_btBlockApp = nullptr;
QToolButton *m_btRemoveApp = nullptr;
QLineEdit *m_editSearch = nullptr;
+ QPushButton *m_btFilter = nullptr;
+ QPushButton *m_btClearFilter = nullptr;
+ QCheckBox *m_cbFilterWildcard = nullptr;
QToolButton *m_btGroups = nullptr;
QToolButton *m_btServices = nullptr;
QPushButton *m_btMenu = nullptr;
diff --git a/src/ui/fort_icons.qrc b/src/ui/fort_icons.qrc
index de7fcc35..abc6db21 100644
--- a/src/ui/fort_icons.qrc
+++ b/src/ui/fort_icons.qrc
@@ -27,6 +27,7 @@
icons/disk.png
icons/download.png
icons/error.png
+ icons/filter.png
icons/folder.png
icons/fort-96.png
icons/fort.png
diff --git a/src/ui/icons/filter.png b/src/ui/icons/filter.png
new file mode 100644
index 00000000..58efdba6
Binary files /dev/null and b/src/ui/icons/filter.png differ
diff --git a/src/ui/model/applistmodel.cpp b/src/ui/model/applistmodel.cpp
index 2a3509a9..d6db82a8 100644
--- a/src/ui/model/applistmodel.cpp
+++ b/src/ui/model/applistmodel.cpp
@@ -267,6 +267,41 @@ inline QVariant dataDisplayRow(const AppRow &appRow, int column, int role)
AppListModel::AppListModel(QObject *parent) : FtsTableSqlModel(parent) { }
+void AppListModel::setFilters(FilterFlags v)
+{
+ if (m_filters == v)
+ return;
+
+ m_filters = v;
+ emit filtersChanged();
+
+ resetLater();
+}
+
+void AppListModel::setFilter(FilterFlag v, bool on)
+{
+ auto filters = m_filters;
+ filters.setFlag(v, on);
+
+ setFilters(filters);
+}
+
+void AppListModel::setFilterValue(FilterFlag v, Qt::CheckState checkState)
+{
+ m_filterValues.setFlag(v, (checkState == Qt::Checked));
+
+ setFilter(v, (checkState != Qt::PartiallyChecked));
+
+ resetLater();
+}
+
+void AppListModel::clearFilters()
+{
+ m_filterValues = FilterNone;
+
+ setFilters(FilterNone);
+}
+
ConfManager *AppListModel::confManager() const
{
return IoC();
@@ -528,6 +563,24 @@ QString AppListModel::sqlBase() const
" LEFT JOIN rule r ON r.rule_id = t.rule_id";
}
+QString AppListModel::sqlWhere() const
+{
+ QString sql = FtsTableSqlModel::sqlWhere();
+
+ if (filters() != FilterNone) {
+ if (sql.isEmpty()) {
+ sql = " WHERE ";
+ }
+
+ if (filters().testFlag(FilterWildcard)) {
+ sql += QString("t.is_wildcard = %1")
+ .arg(filterValues().testFlag(FilterWildcard) ? "1" : "0");
+ }
+ }
+
+ return sql;
+}
+
QString AppListModel::sqlWhereFts() const
{
return " WHERE t.app_id IN ( SELECT rowid FROM app_fts(:match) )";
diff --git a/src/ui/model/applistmodel.h b/src/ui/model/applistmodel.h
index 9f5df120..79b03688 100644
--- a/src/ui/model/applistmodel.h
+++ b/src/ui/model/applistmodel.h
@@ -22,8 +22,24 @@ class AppListModel : public FtsTableSqlModel
Q_OBJECT
public:
+ enum FilterFlag {
+ FilterNone = 0,
+ FilterWildcard = (1 << 0),
+ };
+ Q_ENUM(FilterFlag)
+ Q_DECLARE_FLAGS(FilterFlags, FilterFlag)
+
explicit AppListModel(QObject *parent = nullptr);
+ FilterFlags filters() const { return m_filters; }
+ void setFilters(FilterFlags v);
+ void setFilter(FilterFlag v, bool on = true);
+
+ FilterFlags filterValues() const { return m_filterValues; }
+ void setFilterValue(FilterFlag v, Qt::CheckState checkState);
+
+ void clearFilters();
+
ConfManager *confManager() const;
ConfAppManager *confAppManager() const;
AppInfoCache *appInfoCache() const;
@@ -41,11 +57,15 @@ public:
AppRow appRowById(qint64 appId) const;
AppRow appRowByPath(const QString &appPath) const;
+signals:
+ void filtersChanged();
+
protected:
bool updateTableRow(const QVariantHash &vars, int row) const override;
TableRow &tableRow() const override { return m_appRow; }
QString sqlBase() const override;
+ QString sqlWhere() const override;
QString sqlWhereFts() const override;
QString sqlOrderColumn() const override;
@@ -60,7 +80,12 @@ private:
bool updateAppRow(const QString &sql, const QVariantHash &vars, AppRow &appRow) const;
private:
+ FilterFlags m_filters = FilterNone;
+ FilterFlags m_filterValues = FilterNone;
+
mutable AppRow m_appRow;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(AppListModel::FilterFlags)
+
#endif // APPLISTMODEL_H