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