Skip to content

Commit 542ae73

Browse files
committed
added feed filtering dropdown the same way as article filtering dropdown
1 parent 28adf63 commit 542ae73

14 files changed

+348
-117
lines changed

src/librssguard/core/feedsproxymodel.cpp

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ using RootItemPtr = RootItem*;
1919

2020
FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
2121
: QSortFilterProxyModel(parent), m_sourceModel(source_model), m_view(nullptr), m_selectedItem(nullptr),
22-
m_showUnreadOnly(false), m_sortAlphabetically(false) {
22+
m_showUnreadOnly(false), m_sortAlphabetically(false), m_filter(FeedListFilter::NoFiltering) {
2323
setObjectName(QSL("FeedsProxyModel"));
2424

25+
initializeFilters();
26+
2527
setSortRole(Qt::ItemDataRole::EditRole);
2628
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
2729
setRecursiveFilteringEnabled(true);
@@ -397,6 +399,43 @@ bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source
397399
return should_show;
398400
}
399401

402+
void FeedsProxyModel::initializeFilters() {
403+
m_filters[FeedListFilter::ShowEmpty] = [this](const Feed* feed) {
404+
return feed->countOfAllMessages() == 0;
405+
};
406+
407+
m_filters[FeedListFilter::ShowNonEmpty] = [this](const Feed* feed) {
408+
return feed->countOfAllMessages() != 0;
409+
};
410+
411+
m_filters[FeedListFilter::ShowQuiet] = [this](const Feed* feed) {
412+
return feed->isQuiet();
413+
};
414+
415+
m_filters[FeedListFilter::ShowSwitchedOff] = [this](const Feed* feed) {
416+
return feed->isSwitchedOff();
417+
};
418+
419+
m_filters[FeedListFilter::ShowUnread] = [this](const Feed* feed) {
420+
return feed->countOfUnreadMessages() > 0;
421+
};
422+
423+
m_filters[FeedListFilter::ShowWithArticleFilters] = [this](const Feed* feed) {
424+
return !feed->messageFilters().isEmpty();
425+
};
426+
427+
m_filters[FeedListFilter::ShowWithError] = [this](const Feed* feed) {
428+
return feed->status() == Feed::Status::AuthError || feed->status() == Feed::Status::NetworkError ||
429+
feed->status() == Feed::Status::OtherError || feed->status() == Feed::Status::ParsingError;
430+
};
431+
432+
m_filters[FeedListFilter::ShowWithNewArticles] = [this](const Feed* feed) {
433+
return feed->status() == Feed::Status::NewMessages;
434+
};
435+
436+
m_filterKeys = m_filters.keys();
437+
}
438+
400439
bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const {
401440
const QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent);
402441

@@ -406,6 +445,10 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
406445

407446
const RootItem* item = m_sourceModel->itemForIndex(idx);
408447

448+
if (m_selectedItem == item) {
449+
return true;
450+
}
451+
409452
if (item->kind() == RootItem::Kind::Important && !item->getParentServiceRoot()->nodeShowImportant()) {
410453
return false;
411454
}
@@ -428,22 +471,26 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
428471
return true;
429472
}
430473

431-
if (!m_showUnreadOnly) {
432-
// Take only regexp filtering into account.
433-
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
434-
}
435-
else {
436-
// NOTE: If item has < 0 of unread messages it may mean, that the count
437-
// of unread messages is not (yet) known, display that item too.
438-
//
439-
// Also, the actual selected item should not be filtered out too.
440-
// This is primarily to make sure that the selection does not "vanish", this
441-
// particularly manifests itself if user uses "next unread item" action and
442-
// "show unread only" is enabled too and user for example selects last unread
443-
// article in a feed -> then the feed would disappear from list suddenly.
444-
return m_selectedItem == item ||
445-
(item->countOfUnreadMessages() != 0 && QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent));
474+
if (item->kind() == RootItem::Kind::Feed) {
475+
const Feed* feed = item->toFeed();
476+
477+
for (FeedListFilter val : m_filterKeys) {
478+
if (Globals::hasFlag(m_filter, val)) {
479+
// This particular filter is enabled.
480+
if (m_filters[val](feed)) {
481+
// The item matches the feed filter.
482+
// Display it if it matches internal string-based filter too.
483+
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
484+
}
485+
}
486+
}
487+
488+
// The item does not match feed filter.
489+
// Display it only if it is selected.
490+
return m_filter == FeedListFilter::NoFiltering;
446491
}
492+
493+
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
447494
}
448495

449496
bool FeedsProxyModel::sortAlphabetically() const {
@@ -500,3 +547,9 @@ QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes)
500547

501548
return source_indexes;
502549
}
550+
551+
void FeedsProxyModel::setFeedListFilter(FeedListFilter filter) {
552+
m_filter = filter;
553+
554+
invalidateRowsFilter();
555+
}

src/librssguard/core/feedsproxymodel.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,25 @@ class FeedsProxyModel : public QSortFilterProxyModel {
1414
Q_OBJECT
1515

1616
public:
17+
// Enum which describes basic filtering schemes
18+
// for feeds.
19+
enum class FeedListFilter {
20+
NoFiltering = 1,
21+
ShowUnread = 2,
22+
ShowEmpty = 4,
23+
ShowNonEmpty = 8,
24+
ShowWithNewArticles = 16,
25+
ShowWithError = 32,
26+
ShowSwitchedOff = 64,
27+
ShowQuiet = 128,
28+
ShowWithArticleFilters = 256
29+
};
30+
1731
explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = nullptr);
1832
virtual ~FeedsProxyModel();
1933

34+
void setFeedListFilter(FeedListFilter filter);
35+
2036
virtual bool canDropMimeData(const QMimeData* data,
2137
Qt::DropAction action,
2238
int row,
@@ -45,8 +61,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
4561
void setShowUnreadOnly(bool show_unread_only);
4662

4763
const RootItem* selectedItem() const;
48-
4964
void setSelectedItem(const RootItem* selected_item);
65+
5066
void setView(FeedsView* newView);
5167

5268
bool sortAlphabetically() const;
@@ -66,6 +82,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
6682
virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
6783

6884
private:
85+
void initializeFilters();
86+
6987
virtual bool filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const;
7088

7189
// Source model pointer.
@@ -80,6 +98,12 @@ class FeedsProxyModel : public QSortFilterProxyModel {
8098
bool m_showNodeImportant;
8199
QList<RootItem::Kind> m_priorities;
82100
QList<QPair<int, QModelIndex>> m_hiddenIndices;
101+
102+
FeedListFilter m_filter;
103+
QMap<FeedListFilter, std::function<bool(const Feed*)>> m_filters;
104+
QList<FeedListFilter> m_filterKeys;
83105
};
84106

107+
Q_DECLARE_METATYPE(FeedsProxyModel::FeedListFilter)
108+
85109
#endif // FEEDSPROXYMODEL_H

src/librssguard/gui/feedmessageviewer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,10 @@ void FeedMessageViewer::changeMessageFilter(MessagesProxyModel::MessageListFilte
186186
m_messagesView->changeFilter(filter);
187187
}
188188

189+
void FeedMessageViewer::changeFeedFilter(FeedsProxyModel::FeedListFilter filter) {
190+
m_feedsView->changeFilter(filter);
191+
}
192+
189193
void FeedMessageViewer::toggleShowOnlyUnreadFeeds() {
190194
const QAction* origin = qobject_cast<QAction*>(sender());
191195

@@ -288,7 +292,9 @@ void FeedMessageViewer::createConnections() {
288292
&MessagesToolBar::messageHighlighterChanged,
289293
m_messagesView,
290294
&MessagesView::highlightMessages);
295+
291296
connect(m_toolBarMessages, &MessagesToolBar::messageFilterChanged, this, &FeedMessageViewer::changeMessageFilter);
297+
connect(m_toolBarFeeds, &FeedsToolBar::feedFilterChanged, this, &FeedMessageViewer::changeFeedFilter);
292298

293299
connect(m_feedSplitter, &QSplitter::splitterMoved, this, &FeedMessageViewer::onFeedSplitterResized);
294300
connect(m_messageSplitter, &QSplitter::splitterMoved, this, &FeedMessageViewer::onMessageSplitterResized);

src/librssguard/gui/feedmessageviewer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#ifndef FEEDMESSAGEVIEWER_H
44
#define FEEDMESSAGEVIEWER_H
55

6+
#include "core/feedsproxymodel.h"
67
#include "core/messagesmodel.h"
78
#include "core/messagesproxymodel.h"
89
#include "gui/tabcontent.h"
@@ -62,6 +63,8 @@ class RSSGUARD_DLLSPEC FeedMessageViewer : public TabContent {
6263
void switchFeedComponentVisibility();
6364

6465
void changeMessageFilter(MessagesProxyModel::MessageListFilter filter);
66+
void changeFeedFilter(FeedsProxyModel::FeedListFilter filter);
67+
6568
void toggleShowOnlyUnreadFeeds();
6669
void toggleShowFeedTreeBranches();
6770
void toggleItemsAutoExpandingOnSelection();

src/librssguard/gui/feedsview.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,10 @@ void FeedsView::editRecursiveFeeds() {
407407
}
408408
}
409409

410+
void FeedsView::changeFilter(FeedsProxyModel::FeedListFilter filter) {
411+
m_proxyModel->setFeedListFilter(filter);
412+
}
413+
410414
void FeedsView::editSelectedItems() {
411415
editItems(selectedItems());
412416
}
@@ -787,10 +791,10 @@ void FeedsView::filterItems(SearchLineEdit::SearchMode mode,
787791

788792
m_proxyModel->setFilterCaseSensitivity(sensitivity);
789793

790-
FeedsToolBar::SearchFields where_search = FeedsToolBar::SearchFields(custom_criteria);
794+
BaseToolBar::SearchFields where_search = BaseToolBar::SearchFields(custom_criteria);
791795

792-
m_proxyModel->setFilterKeyColumn(where_search == FeedsToolBar::SearchFields::SearchTitleOnly ? FDS_MODEL_TITLE_INDEX
793-
: -1);
796+
m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? FDS_MODEL_TITLE_INDEX
797+
: -1);
794798

795799
if (phrase.isEmpty()) {
796800
loadAllExpandStates();

src/librssguard/gui/feedsview.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
8686
// Switches visibility of the widget.
8787
void switchVisibility();
8888

89+
void changeFilter(FeedsProxyModel::FeedListFilter filter);
90+
8991
void filterItems(SearchLineEdit::SearchMode mode,
9092
Qt::CaseSensitivity sensitivity,
9193
int custom_criteria,

src/librssguard/gui/messagesview.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -871,10 +871,10 @@ void MessagesView::searchMessages(SearchLineEdit::SearchMode mode,
871871

872872
m_proxyModel->setFilterCaseSensitivity(sensitivity);
873873

874-
MessagesToolBar::SearchFields where_search = MessagesToolBar::SearchFields(custom_criteria);
874+
BaseToolBar::SearchFields where_search = BaseToolBar::SearchFields(custom_criteria);
875875

876-
m_proxyModel->setFilterKeyColumn(where_search == MessagesToolBar::SearchFields::SearchTitleOnly ? MSG_DB_TITLE_INDEX
877-
: -1);
876+
m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? MSG_DB_TITLE_INDEX
877+
: -1);
878878

879879
if (selectionModel()->selectedRows().isEmpty()) {
880880
emit currentMessageRemoved(m_sourceModel->loadedItem());

src/librssguard/gui/toolbars/basetoolbar.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
#include "gui/toolbars/basetoolbar.h"
44

5+
#include "3rd-party/boolinq/boolinq.h"
56
#include "definitions/definitions.h"
7+
#include "miscellaneous/settings.h"
68

9+
#include <QFont>
10+
#include <QPainter>
711
#include <QWidgetAction>
812

913
BaseToolBar::BaseToolBar(const QString& title, QWidget* parent) : QToolBar(title, parent) {
@@ -31,3 +35,69 @@ QAction* BaseBar::findMatchingAction(const QString& action, const QList<QAction*
3135

3236
return nullptr;
3337
}
38+
39+
void BaseToolBar::addActionToMenu(QMenu* menu,
40+
const QIcon& icon,
41+
const QString& title,
42+
const QVariant& value,
43+
const QString& name) {
44+
QAction* action = menu->addAction(icon, title);
45+
46+
action->setCheckable(true);
47+
action->setData(value);
48+
action->setObjectName(name);
49+
}
50+
51+
void BaseToolBar::activateAction(const QString& action_name, QWidgetAction* widget_action) {
52+
const int start = action_name.indexOf('[');
53+
const int end = action_name.indexOf(']');
54+
55+
if (start != -1 && end != -1 && end == action_name.length() - 1) {
56+
const QStringList menu_action_names = action_name.chopped(1).right(end - start - 1).split(QL1C(';'));
57+
auto tool_btn = qobject_cast<QToolButton*>(widget_action->defaultWidget());
58+
59+
for (QAction* action : tool_btn->menu()->actions()) {
60+
if (menu_action_names.contains(action->objectName())) {
61+
action->trigger();
62+
}
63+
}
64+
}
65+
}
66+
67+
void BaseToolBar::saveToolButtonSelection(const QString& button_name,
68+
const QString& setting_name,
69+
const QList<QAction*>& actions) const {
70+
QStringList action_names = savedActions();
71+
72+
auto opts_list = boolinq::from(actions)
73+
.select([](const QAction* act) {
74+
return act->objectName();
75+
})
76+
.toStdList();
77+
QStringList opts = FROM_STD_LIST(QStringList, opts_list);
78+
79+
for (QString& action_name : action_names) {
80+
if (action_name.startsWith(button_name)) {
81+
action_name = button_name + QSL("[%1]").arg(opts.join(QL1C(';')));
82+
}
83+
}
84+
85+
qApp->settings()->setValue(GROUP(GUI), setting_name, action_names.join(QSL(",")));
86+
}
87+
88+
void BaseToolBar::drawNumberOfCriterias(QToolButton* btn, int count) {
89+
QPixmap px(128, 128);
90+
px.fill(Qt::GlobalColor::transparent);
91+
92+
QPainter p(&px);
93+
94+
auto fon = p.font();
95+
96+
fon.setPixelSize(40);
97+
p.setFont(fon);
98+
99+
p.drawPixmap(0, 0, 80, 80, btn->defaultAction()->icon().pixmap(128, 128));
100+
p.drawText(65, 65, 50, 50, Qt::AlignmentFlag::AlignCenter, QString::number(count));
101+
102+
btn->setIcon(px);
103+
}

src/librssguard/gui/toolbars/basetoolbar.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
#ifndef TOOLBAR_H
44
#define TOOLBAR_H
55

6+
#include <QMenu>
67
#include <QToolBar>
8+
#include <QToolButton>
9+
#include <QWidgetAction>
710

811
class BaseBar {
912
public:
@@ -41,8 +44,25 @@ class BaseToolBar : public QToolBar, public BaseBar {
4144
Q_OBJECT
4245

4346
public:
47+
enum class SearchFields {
48+
SearchTitleOnly = 1,
49+
SearchAll = 2
50+
};
51+
4452
explicit BaseToolBar(const QString& title, QWidget* parent = nullptr);
4553
virtual ~BaseToolBar();
54+
55+
protected:
56+
void saveToolButtonSelection(const QString& button_name,
57+
const QString& setting_name,
58+
const QList<QAction*>& actions) const;
59+
void activateAction(const QString& action_name, QWidgetAction* widget_action);
60+
void addActionToMenu(QMenu* menu,
61+
const QIcon& icon,
62+
const QString& title,
63+
const QVariant& value,
64+
const QString& name);
65+
void drawNumberOfCriterias(QToolButton* btn, int count);
4666
};
4767

4868
#endif // TOOLBAR_H

0 commit comments

Comments
 (0)