added feed filtering dropdown the same way as article filtering dropdown
This commit is contained in:
parent
28adf6379c
commit
542ae73b2b
14 changed files with 348 additions and 117 deletions
|
@ -19,9 +19,11 @@ using RootItemPtr = RootItem*;
|
|||
|
||||
FeedsProxyModel::FeedsProxyModel(FeedsModel* source_model, QObject* parent)
|
||||
: QSortFilterProxyModel(parent), m_sourceModel(source_model), m_view(nullptr), m_selectedItem(nullptr),
|
||||
m_showUnreadOnly(false), m_sortAlphabetically(false) {
|
||||
m_showUnreadOnly(false), m_sortAlphabetically(false), m_filter(FeedListFilter::NoFiltering) {
|
||||
setObjectName(QSL("FeedsProxyModel"));
|
||||
|
||||
initializeFilters();
|
||||
|
||||
setSortRole(Qt::ItemDataRole::EditRole);
|
||||
setSortCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
|
||||
setRecursiveFilteringEnabled(true);
|
||||
|
@ -397,6 +399,43 @@ bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source
|
|||
return should_show;
|
||||
}
|
||||
|
||||
void FeedsProxyModel::initializeFilters() {
|
||||
m_filters[FeedListFilter::ShowEmpty] = [this](const Feed* feed) {
|
||||
return feed->countOfAllMessages() == 0;
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowNonEmpty] = [this](const Feed* feed) {
|
||||
return feed->countOfAllMessages() != 0;
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowQuiet] = [this](const Feed* feed) {
|
||||
return feed->isQuiet();
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowSwitchedOff] = [this](const Feed* feed) {
|
||||
return feed->isSwitchedOff();
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowUnread] = [this](const Feed* feed) {
|
||||
return feed->countOfUnreadMessages() > 0;
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowWithArticleFilters] = [this](const Feed* feed) {
|
||||
return !feed->messageFilters().isEmpty();
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowWithError] = [this](const Feed* feed) {
|
||||
return feed->status() == Feed::Status::AuthError || feed->status() == Feed::Status::NetworkError ||
|
||||
feed->status() == Feed::Status::OtherError || feed->status() == Feed::Status::ParsingError;
|
||||
};
|
||||
|
||||
m_filters[FeedListFilter::ShowWithNewArticles] = [this](const Feed* feed) {
|
||||
return feed->status() == Feed::Status::NewMessages;
|
||||
};
|
||||
|
||||
m_filterKeys = m_filters.keys();
|
||||
}
|
||||
|
||||
bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const {
|
||||
const QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent);
|
||||
|
||||
|
@ -406,6 +445,10 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
|
|||
|
||||
const RootItem* item = m_sourceModel->itemForIndex(idx);
|
||||
|
||||
if (m_selectedItem == item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item->kind() == RootItem::Kind::Important && !item->getParentServiceRoot()->nodeShowImportant()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -428,24 +471,28 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex
|
|||
return true;
|
||||
}
|
||||
|
||||
if (!m_showUnreadOnly) {
|
||||
// Take only regexp filtering into account.
|
||||
if (item->kind() == RootItem::Kind::Feed) {
|
||||
const Feed* feed = item->toFeed();
|
||||
|
||||
for (FeedListFilter val : m_filterKeys) {
|
||||
if (Globals::hasFlag(m_filter, val)) {
|
||||
// This particular filter is enabled.
|
||||
if (m_filters[val](feed)) {
|
||||
// The item matches the feed filter.
|
||||
// Display it if it matches internal string-based filter too.
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
else {
|
||||
// NOTE: If item has < 0 of unread messages it may mean, that the count
|
||||
// of unread messages is not (yet) known, display that item too.
|
||||
//
|
||||
// Also, the actual selected item should not be filtered out too.
|
||||
// This is primarily to make sure that the selection does not "vanish", this
|
||||
// particularly manifests itself if user uses "next unread item" action and
|
||||
// "show unread only" is enabled too and user for example selects last unread
|
||||
// article in a feed -> then the feed would disappear from list suddenly.
|
||||
return m_selectedItem == item ||
|
||||
(item->countOfUnreadMessages() != 0 && QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent));
|
||||
}
|
||||
}
|
||||
|
||||
// The item does not match feed filter.
|
||||
// Display it only if it is selected.
|
||||
return m_filter == FeedListFilter::NoFiltering;
|
||||
}
|
||||
|
||||
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
||||
}
|
||||
|
||||
bool FeedsProxyModel::sortAlphabetically() const {
|
||||
return m_sortAlphabetically;
|
||||
}
|
||||
|
@ -500,3 +547,9 @@ QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes)
|
|||
|
||||
return source_indexes;
|
||||
}
|
||||
|
||||
void FeedsProxyModel::setFeedListFilter(FeedListFilter filter) {
|
||||
m_filter = filter;
|
||||
|
||||
invalidateRowsFilter();
|
||||
}
|
||||
|
|
|
@ -14,9 +14,25 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Enum which describes basic filtering schemes
|
||||
// for feeds.
|
||||
enum class FeedListFilter {
|
||||
NoFiltering = 1,
|
||||
ShowUnread = 2,
|
||||
ShowEmpty = 4,
|
||||
ShowNonEmpty = 8,
|
||||
ShowWithNewArticles = 16,
|
||||
ShowWithError = 32,
|
||||
ShowSwitchedOff = 64,
|
||||
ShowQuiet = 128,
|
||||
ShowWithArticleFilters = 256
|
||||
};
|
||||
|
||||
explicit FeedsProxyModel(FeedsModel* source_model, QObject* parent = nullptr);
|
||||
virtual ~FeedsProxyModel();
|
||||
|
||||
void setFeedListFilter(FeedListFilter filter);
|
||||
|
||||
virtual bool canDropMimeData(const QMimeData* data,
|
||||
Qt::DropAction action,
|
||||
int row,
|
||||
|
@ -45,8 +61,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||
void setShowUnreadOnly(bool show_unread_only);
|
||||
|
||||
const RootItem* selectedItem() const;
|
||||
|
||||
void setSelectedItem(const RootItem* selected_item);
|
||||
|
||||
void setView(FeedsView* newView);
|
||||
|
||||
bool sortAlphabetically() const;
|
||||
|
@ -66,6 +82,8 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||
virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const;
|
||||
|
||||
private:
|
||||
void initializeFilters();
|
||||
|
||||
virtual bool filterAcceptsRowInternal(int source_row, const QModelIndex& source_parent) const;
|
||||
|
||||
// Source model pointer.
|
||||
|
@ -80,6 +98,12 @@ class FeedsProxyModel : public QSortFilterProxyModel {
|
|||
bool m_showNodeImportant;
|
||||
QList<RootItem::Kind> m_priorities;
|
||||
QList<QPair<int, QModelIndex>> m_hiddenIndices;
|
||||
|
||||
FeedListFilter m_filter;
|
||||
QMap<FeedListFilter, std::function<bool(const Feed*)>> m_filters;
|
||||
QList<FeedListFilter> m_filterKeys;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FeedsProxyModel::FeedListFilter)
|
||||
|
||||
#endif // FEEDSPROXYMODEL_H
|
||||
|
|
|
@ -186,6 +186,10 @@ void FeedMessageViewer::changeMessageFilter(MessagesProxyModel::MessageListFilte
|
|||
m_messagesView->changeFilter(filter);
|
||||
}
|
||||
|
||||
void FeedMessageViewer::changeFeedFilter(FeedsProxyModel::FeedListFilter filter) {
|
||||
m_feedsView->changeFilter(filter);
|
||||
}
|
||||
|
||||
void FeedMessageViewer::toggleShowOnlyUnreadFeeds() {
|
||||
const QAction* origin = qobject_cast<QAction*>(sender());
|
||||
|
||||
|
@ -288,7 +292,9 @@ void FeedMessageViewer::createConnections() {
|
|||
&MessagesToolBar::messageHighlighterChanged,
|
||||
m_messagesView,
|
||||
&MessagesView::highlightMessages);
|
||||
|
||||
connect(m_toolBarMessages, &MessagesToolBar::messageFilterChanged, this, &FeedMessageViewer::changeMessageFilter);
|
||||
connect(m_toolBarFeeds, &FeedsToolBar::feedFilterChanged, this, &FeedMessageViewer::changeFeedFilter);
|
||||
|
||||
connect(m_feedSplitter, &QSplitter::splitterMoved, this, &FeedMessageViewer::onFeedSplitterResized);
|
||||
connect(m_messageSplitter, &QSplitter::splitterMoved, this, &FeedMessageViewer::onMessageSplitterResized);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#ifndef FEEDMESSAGEVIEWER_H
|
||||
#define FEEDMESSAGEVIEWER_H
|
||||
|
||||
#include "core/feedsproxymodel.h"
|
||||
#include "core/messagesmodel.h"
|
||||
#include "core/messagesproxymodel.h"
|
||||
#include "gui/tabcontent.h"
|
||||
|
@ -62,6 +63,8 @@ class RSSGUARD_DLLSPEC FeedMessageViewer : public TabContent {
|
|||
void switchFeedComponentVisibility();
|
||||
|
||||
void changeMessageFilter(MessagesProxyModel::MessageListFilter filter);
|
||||
void changeFeedFilter(FeedsProxyModel::FeedListFilter filter);
|
||||
|
||||
void toggleShowOnlyUnreadFeeds();
|
||||
void toggleShowFeedTreeBranches();
|
||||
void toggleItemsAutoExpandingOnSelection();
|
||||
|
|
|
@ -407,6 +407,10 @@ void FeedsView::editRecursiveFeeds() {
|
|||
}
|
||||
}
|
||||
|
||||
void FeedsView::changeFilter(FeedsProxyModel::FeedListFilter filter) {
|
||||
m_proxyModel->setFeedListFilter(filter);
|
||||
}
|
||||
|
||||
void FeedsView::editSelectedItems() {
|
||||
editItems(selectedItems());
|
||||
}
|
||||
|
@ -787,9 +791,9 @@ void FeedsView::filterItems(SearchLineEdit::SearchMode mode,
|
|||
|
||||
m_proxyModel->setFilterCaseSensitivity(sensitivity);
|
||||
|
||||
FeedsToolBar::SearchFields where_search = FeedsToolBar::SearchFields(custom_criteria);
|
||||
BaseToolBar::SearchFields where_search = BaseToolBar::SearchFields(custom_criteria);
|
||||
|
||||
m_proxyModel->setFilterKeyColumn(where_search == FeedsToolBar::SearchFields::SearchTitleOnly ? FDS_MODEL_TITLE_INDEX
|
||||
m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? FDS_MODEL_TITLE_INDEX
|
||||
: -1);
|
||||
|
||||
if (phrase.isEmpty()) {
|
||||
|
|
|
@ -86,6 +86,8 @@ class RSSGUARD_DLLSPEC FeedsView : public BaseTreeView {
|
|||
// Switches visibility of the widget.
|
||||
void switchVisibility();
|
||||
|
||||
void changeFilter(FeedsProxyModel::FeedListFilter filter);
|
||||
|
||||
void filterItems(SearchLineEdit::SearchMode mode,
|
||||
Qt::CaseSensitivity sensitivity,
|
||||
int custom_criteria,
|
||||
|
|
|
@ -871,9 +871,9 @@ void MessagesView::searchMessages(SearchLineEdit::SearchMode mode,
|
|||
|
||||
m_proxyModel->setFilterCaseSensitivity(sensitivity);
|
||||
|
||||
MessagesToolBar::SearchFields where_search = MessagesToolBar::SearchFields(custom_criteria);
|
||||
BaseToolBar::SearchFields where_search = BaseToolBar::SearchFields(custom_criteria);
|
||||
|
||||
m_proxyModel->setFilterKeyColumn(where_search == MessagesToolBar::SearchFields::SearchTitleOnly ? MSG_DB_TITLE_INDEX
|
||||
m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? MSG_DB_TITLE_INDEX
|
||||
: -1);
|
||||
|
||||
if (selectionModel()->selectedRows().isEmpty()) {
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
|
||||
#include "gui/toolbars/basetoolbar.h"
|
||||
|
||||
#include "3rd-party/boolinq/boolinq.h"
|
||||
#include "definitions/definitions.h"
|
||||
#include "miscellaneous/settings.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QPainter>
|
||||
#include <QWidgetAction>
|
||||
|
||||
BaseToolBar::BaseToolBar(const QString& title, QWidget* parent) : QToolBar(title, parent) {
|
||||
|
@ -31,3 +35,69 @@ QAction* BaseBar::findMatchingAction(const QString& action, const QList<QAction*
|
|||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BaseToolBar::addActionToMenu(QMenu* menu,
|
||||
const QIcon& icon,
|
||||
const QString& title,
|
||||
const QVariant& value,
|
||||
const QString& name) {
|
||||
QAction* action = menu->addAction(icon, title);
|
||||
|
||||
action->setCheckable(true);
|
||||
action->setData(value);
|
||||
action->setObjectName(name);
|
||||
}
|
||||
|
||||
void BaseToolBar::activateAction(const QString& action_name, QWidgetAction* widget_action) {
|
||||
const int start = action_name.indexOf('[');
|
||||
const int end = action_name.indexOf(']');
|
||||
|
||||
if (start != -1 && end != -1 && end == action_name.length() - 1) {
|
||||
const QStringList menu_action_names = action_name.chopped(1).right(end - start - 1).split(QL1C(';'));
|
||||
auto tool_btn = qobject_cast<QToolButton*>(widget_action->defaultWidget());
|
||||
|
||||
for (QAction* action : tool_btn->menu()->actions()) {
|
||||
if (menu_action_names.contains(action->objectName())) {
|
||||
action->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseToolBar::saveToolButtonSelection(const QString& button_name,
|
||||
const QString& setting_name,
|
||||
const QList<QAction*>& actions) const {
|
||||
QStringList action_names = savedActions();
|
||||
|
||||
auto opts_list = boolinq::from(actions)
|
||||
.select([](const QAction* act) {
|
||||
return act->objectName();
|
||||
})
|
||||
.toStdList();
|
||||
QStringList opts = FROM_STD_LIST(QStringList, opts_list);
|
||||
|
||||
for (QString& action_name : action_names) {
|
||||
if (action_name.startsWith(button_name)) {
|
||||
action_name = button_name + QSL("[%1]").arg(opts.join(QL1C(';')));
|
||||
}
|
||||
}
|
||||
|
||||
qApp->settings()->setValue(GROUP(GUI), setting_name, action_names.join(QSL(",")));
|
||||
}
|
||||
|
||||
void BaseToolBar::drawNumberOfCriterias(QToolButton* btn, int count) {
|
||||
QPixmap px(128, 128);
|
||||
px.fill(Qt::GlobalColor::transparent);
|
||||
|
||||
QPainter p(&px);
|
||||
|
||||
auto fon = p.font();
|
||||
|
||||
fon.setPixelSize(40);
|
||||
p.setFont(fon);
|
||||
|
||||
p.drawPixmap(0, 0, 80, 80, btn->defaultAction()->icon().pixmap(128, 128));
|
||||
p.drawText(65, 65, 50, 50, Qt::AlignmentFlag::AlignCenter, QString::number(count));
|
||||
|
||||
btn->setIcon(px);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
#ifndef TOOLBAR_H
|
||||
#define TOOLBAR_H
|
||||
|
||||
#include <QMenu>
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
#include <QWidgetAction>
|
||||
|
||||
class BaseBar {
|
||||
public:
|
||||
|
@ -41,8 +44,25 @@ class BaseToolBar : public QToolBar, public BaseBar {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class SearchFields {
|
||||
SearchTitleOnly = 1,
|
||||
SearchAll = 2
|
||||
};
|
||||
|
||||
explicit BaseToolBar(const QString& title, QWidget* parent = nullptr);
|
||||
virtual ~BaseToolBar();
|
||||
|
||||
protected:
|
||||
void saveToolButtonSelection(const QString& button_name,
|
||||
const QString& setting_name,
|
||||
const QList<QAction*>& actions) const;
|
||||
void activateAction(const QString& action_name, QWidgetAction* widget_action);
|
||||
void addActionToMenu(QMenu* menu,
|
||||
const QIcon& icon,
|
||||
const QString& title,
|
||||
const QVariant& value,
|
||||
const QString& name);
|
||||
void drawNumberOfCriterias(QToolButton* btn, int count);
|
||||
};
|
||||
|
||||
#endif // TOOLBAR_H
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
#include "gui/toolbars/feedstoolbar.h"
|
||||
|
||||
#include "3rd-party/boolinq/boolinq.h"
|
||||
#include "core/feedsproxymodel.h"
|
||||
#include "gui/reusable/nonclosablemenu.h"
|
||||
#include "miscellaneous/application.h"
|
||||
#include "miscellaneous/iconfactory.h"
|
||||
#include "miscellaneous/settings.h"
|
||||
|
@ -15,6 +18,7 @@ FeedsToolBar::FeedsToolBar(const QString& title, QWidget* parent) : BaseToolBar(
|
|||
margins.setRight(margins.right() + FILTER_RIGHT_MARGIN);
|
||||
setContentsMargins(margins);
|
||||
|
||||
initializeFilter();
|
||||
initializeSearchBox();
|
||||
}
|
||||
|
||||
|
@ -22,6 +26,7 @@ QList<QAction*> FeedsToolBar::availableActions() const {
|
|||
QList<QAction*> available_actions = qApp->userActions();
|
||||
|
||||
available_actions.append(m_actionSearchMessages);
|
||||
available_actions.append(m_actionMessageFilter);
|
||||
|
||||
return available_actions;
|
||||
}
|
||||
|
@ -65,6 +70,11 @@ QList<QAction*> FeedsToolBar::convertActions(const QStringList& actions) {
|
|||
// Add search box.
|
||||
spec_actions.append(m_actionSearchMessages);
|
||||
}
|
||||
else if (action_name.startsWith(QSL(FILTER_ACTION_NAME))) {
|
||||
// Add filter button.
|
||||
spec_actions.append(m_actionMessageFilter);
|
||||
activateAction(action_name, m_actionMessageFilter);
|
||||
}
|
||||
else if (action_name == QSL(SPACER_ACTION_NAME)) {
|
||||
// Add new spacer.
|
||||
auto* spacer = new QWidget(this);
|
||||
|
@ -93,6 +103,53 @@ void FeedsToolBar::loadSpecificActions(const QList<QAction*>& actions, bool init
|
|||
}
|
||||
}
|
||||
|
||||
inline FeedsProxyModel::FeedListFilter operator|(FeedsProxyModel::FeedListFilter a, FeedsProxyModel::FeedListFilter b) {
|
||||
return static_cast<FeedsProxyModel::FeedListFilter>(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
void FeedsToolBar::handleMessageFilterChange(QAction* action) {
|
||||
FeedsProxyModel::FeedListFilter task = action->data().value<FeedsProxyModel::FeedListFilter>();
|
||||
std::list<QAction*> checked_tasks_std = boolinq::from(m_menuMessageFilter->actions())
|
||||
.where([](QAction* act) {
|
||||
return act->isChecked();
|
||||
})
|
||||
.toStdList();
|
||||
|
||||
if (task == FeedsProxyModel::FeedListFilter::NoFiltering || checked_tasks_std.empty()) {
|
||||
task = FeedsProxyModel::FeedListFilter::NoFiltering;
|
||||
|
||||
checked_tasks_std.clear();
|
||||
|
||||
// Uncheck everything.
|
||||
m_menuMessageFilter->blockSignals(true);
|
||||
|
||||
for (QAction* tsk : m_menuMessageFilter->actions()) {
|
||||
tsk->setChecked(false);
|
||||
}
|
||||
|
||||
m_menuMessageFilter->blockSignals(false);
|
||||
}
|
||||
else {
|
||||
task = FeedsProxyModel::FeedListFilter(0);
|
||||
|
||||
for (QAction* tsk : checked_tasks_std) {
|
||||
task = task | tsk->data().value<FeedsProxyModel::FeedListFilter>();
|
||||
}
|
||||
}
|
||||
|
||||
m_btnMessageFilter->setDefaultAction(checked_tasks_std.empty() ? m_menuMessageFilter->actions().constFirst()
|
||||
: checked_tasks_std.front());
|
||||
|
||||
if (checked_tasks_std.size() > 1) {
|
||||
drawNumberOfCriterias(m_btnMessageFilter, int(checked_tasks_std.size()));
|
||||
}
|
||||
|
||||
saveToolButtonSelection(QSL(FILTER_ACTION_NAME),
|
||||
GUI::FeedsToolbarActions,
|
||||
FROM_STD_LIST(QList<QAction*>, checked_tasks_std));
|
||||
emit feedFilterChanged(task);
|
||||
}
|
||||
|
||||
QStringList FeedsToolBar::defaultActions() const {
|
||||
return QString(GUI::FeedsToolbarActionsDef)
|
||||
.split(',',
|
||||
|
@ -139,3 +196,66 @@ void FeedsToolBar::initializeSearchBox() {
|
|||
SearchLineEdit* FeedsToolBar::searchBox() const {
|
||||
return m_txtSearchMessages;
|
||||
}
|
||||
|
||||
void FeedsToolBar::initializeFilter() {
|
||||
m_menuMessageFilter = new NonClosableMenu(tr("Menu for filtering feeds"), this);
|
||||
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("No extra filtering"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::NoFiltering),
|
||||
QSL("no_filtering"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show unread feeds"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowUnread),
|
||||
QSL("show_unread"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show non-empty feeds"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowNonEmpty),
|
||||
QSL("non_empty"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show feeds with new articles"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowWithNewArticles),
|
||||
QSL("new_articles"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show feeds with error"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowWithError),
|
||||
QSL("with_error"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show switched off feeds"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowSwitchedOff),
|
||||
QSL("switched_off"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show quiet feeds"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowQuiet),
|
||||
QSL("quiet"));
|
||||
addActionToMenu(m_menuMessageFilter,
|
||||
qApp->icons()->fromTheme(QSL("mail-mark-read")),
|
||||
tr("Show feeds with article filters"),
|
||||
QVariant::fromValue(FeedsProxyModel::FeedListFilter::ShowWithArticleFilters),
|
||||
QSL("with_filters"));
|
||||
|
||||
m_btnMessageFilter = new QToolButton(this);
|
||||
m_btnMessageFilter->setToolTip(tr("Display all feeds"));
|
||||
m_btnMessageFilter->setMenu(m_menuMessageFilter);
|
||||
m_btnMessageFilter->setPopupMode(QToolButton::ToolButtonPopupMode::InstantPopup);
|
||||
m_btnMessageFilter->setIcon(qApp->icons()->fromTheme(QSL("mail-mark-read")));
|
||||
m_btnMessageFilter->setDefaultAction(m_menuMessageFilter->actions().constFirst());
|
||||
|
||||
m_actionMessageFilter = new QWidgetAction(this);
|
||||
m_actionMessageFilter->setDefaultWidget(m_btnMessageFilter);
|
||||
m_actionMessageFilter->setIcon(m_btnMessageFilter->icon());
|
||||
m_actionMessageFilter->setProperty("type", FILTER_ACTION_NAME);
|
||||
m_actionMessageFilter->setProperty("name", tr("Feed list filter"));
|
||||
|
||||
connect(m_menuMessageFilter, &QMenu::triggered, this, &FeedsToolBar::handleMessageFilterChange);
|
||||
connect(this, &FeedsToolBar::toolButtonStyleChanged, this, [=](Qt::ToolButtonStyle style) {
|
||||
m_btnMessageFilter->setToolButtonStyle(style);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#ifndef FEEDSTOOLBAR_H
|
||||
#define FEEDSTOOLBAR_H
|
||||
|
||||
#include "core/feedsproxymodel.h"
|
||||
#include "gui/reusable/searchlineedit.h"
|
||||
#include "gui/toolbars/basetoolbar.h"
|
||||
|
||||
|
@ -12,11 +13,6 @@ class FeedsToolBar : public BaseToolBar {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class SearchFields {
|
||||
SearchTitleOnly = 1,
|
||||
SearchAll = 2
|
||||
};
|
||||
|
||||
explicit FeedsToolBar(const QString& title, QWidget* parent = nullptr);
|
||||
|
||||
virtual QList<QAction*> availableActions() const;
|
||||
|
@ -30,15 +26,24 @@ class FeedsToolBar : public BaseToolBar {
|
|||
SearchLineEdit* searchBox() const;
|
||||
|
||||
signals:
|
||||
void feedFilterChanged(FeedsProxyModel::FeedListFilter filter);
|
||||
void searchCriteriaChanged(SearchLineEdit::SearchMode mode,
|
||||
Qt::CaseSensitivity sensitivity,
|
||||
int custom_criteria,
|
||||
const QString& phrase);
|
||||
|
||||
private slots:
|
||||
void handleMessageFilterChange(QAction* action);
|
||||
|
||||
private:
|
||||
void initializeFilter();
|
||||
void initializeSearchBox();
|
||||
|
||||
private:
|
||||
QWidgetAction* m_actionMessageFilter;
|
||||
QToolButton* m_btnMessageFilter;
|
||||
QMenu* m_menuMessageFilter;
|
||||
|
||||
SearchLineEdit* m_txtSearchMessages;
|
||||
QWidgetAction* m_actionSearchMessages;
|
||||
};
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
#include <chrono>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QWidgetAction>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
@ -155,27 +152,12 @@ void MessagesToolBar::handleMessageHighlighterChange(QAction* action) {
|
|||
drawNumberOfCriterias(m_btnMessageHighlighter, int(checked_tasks_std.size()));
|
||||
}
|
||||
|
||||
saveToolButtonSelection(QSL(HIGHLIGHTER_ACTION_NAME), FROM_STD_LIST(QList<QAction*>, checked_tasks_std));
|
||||
saveToolButtonSelection(QSL(HIGHLIGHTER_ACTION_NAME),
|
||||
GUI::MessagesToolbarDefaultButtons,
|
||||
FROM_STD_LIST(QList<QAction*>, checked_tasks_std));
|
||||
emit messageHighlighterChanged(task);
|
||||
}
|
||||
|
||||
void MessagesToolBar::drawNumberOfCriterias(QToolButton* btn, int count) {
|
||||
QPixmap px(128, 128);
|
||||
px.fill(Qt::GlobalColor::transparent);
|
||||
|
||||
QPainter p(&px);
|
||||
|
||||
auto fon = p.font();
|
||||
|
||||
fon.setPixelSize(40);
|
||||
p.setFont(fon);
|
||||
|
||||
p.drawPixmap(0, 0, 80, 80, btn->defaultAction()->icon().pixmap(128, 128));
|
||||
p.drawText(65, 65, 50, 50, Qt::AlignmentFlag::AlignCenter, QString::number(count));
|
||||
|
||||
btn->setIcon(px);
|
||||
}
|
||||
|
||||
void MessagesToolBar::handleMessageFilterChange(QAction* action) {
|
||||
MessagesProxyModel::MessageListFilter task = action->data().value<MessagesProxyModel::MessageListFilter>();
|
||||
std::list<QAction*> checked_tasks_std = boolinq::from(m_menuMessageFilter->actions())
|
||||
|
@ -185,6 +167,8 @@ void MessagesToolBar::handleMessageFilterChange(QAction* action) {
|
|||
.toStdList();
|
||||
|
||||
if (task == MessagesProxyModel::MessageListFilter::NoFiltering || checked_tasks_std.empty()) {
|
||||
task = MessagesProxyModel::MessageListFilter::NoFiltering;
|
||||
|
||||
checked_tasks_std.clear();
|
||||
|
||||
// Uncheck everything.
|
||||
|
@ -211,7 +195,9 @@ void MessagesToolBar::handleMessageFilterChange(QAction* action) {
|
|||
drawNumberOfCriterias(m_btnMessageFilter, int(checked_tasks_std.size()));
|
||||
}
|
||||
|
||||
saveToolButtonSelection(QSL(FILTER_ACTION_NAME), FROM_STD_LIST(QList<QAction*>, checked_tasks_std));
|
||||
saveToolButtonSelection(QSL(FILTER_ACTION_NAME),
|
||||
GUI::MessagesToolbarDefaultButtons,
|
||||
FROM_STD_LIST(QList<QAction*>, checked_tasks_std));
|
||||
emit messageFilterChanged(task);
|
||||
}
|
||||
|
||||
|
@ -235,18 +221,6 @@ void MessagesToolBar::initializeSearchBox() {
|
|||
connect(m_txtSearchMessages, &SearchLineEdit::searchCriteriaChanged, this, &MessagesToolBar::searchCriteriaChanged);
|
||||
}
|
||||
|
||||
void MessagesToolBar::addActionToMenu(QMenu* menu,
|
||||
const QIcon& icon,
|
||||
const QString& title,
|
||||
const QVariant& value,
|
||||
const QString& name) {
|
||||
QAction* action = menu->addAction(icon, title);
|
||||
|
||||
action->setCheckable(true);
|
||||
action->setData(value);
|
||||
action->setObjectName(name);
|
||||
}
|
||||
|
||||
void MessagesToolBar::initializeHighlighter() {
|
||||
m_menuMessageHighlighter = new NonClosableMenu(tr("Menu for highlighting articles"), this);
|
||||
|
||||
|
@ -357,53 +331,16 @@ void MessagesToolBar::initializeHighlighter() {
|
|||
|
||||
connect(m_menuMessageHighlighter, &QMenu::triggered, this, &MessagesToolBar::handleMessageHighlighterChange);
|
||||
connect(m_menuMessageFilter, &QMenu::triggered, this, &MessagesToolBar::handleMessageFilterChange);
|
||||
|
||||
connect(this, &MessagesToolBar::toolButtonStyleChanged, this, [=](Qt::ToolButtonStyle style) {
|
||||
m_btnMessageHighlighter->setToolButtonStyle(style);
|
||||
m_btnMessageFilter->setToolButtonStyle(style);
|
||||
});
|
||||
}
|
||||
|
||||
void MessagesToolBar::saveToolButtonSelection(const QString& button_name, const QList<QAction*>& actions) const {
|
||||
QStringList action_names = savedActions();
|
||||
|
||||
auto opts_list = boolinq::from(actions)
|
||||
.select([](const QAction* act) {
|
||||
return act->objectName();
|
||||
})
|
||||
.toStdList();
|
||||
QStringList opts = FROM_STD_LIST(QStringList, opts_list);
|
||||
|
||||
for (QString& action_name : action_names) {
|
||||
if (action_name.startsWith(button_name)) {
|
||||
action_name = button_name + QSL("[%1]").arg(opts.join(QL1C(';')));
|
||||
}
|
||||
}
|
||||
|
||||
qApp->settings()->setValue(GROUP(GUI), GUI::MessagesToolbarDefaultButtons, action_names.join(QSL(",")));
|
||||
}
|
||||
|
||||
SearchLineEdit* MessagesToolBar::searchBox() const {
|
||||
return m_txtSearchMessages;
|
||||
}
|
||||
|
||||
void MessagesToolBar::activateAction(const QString& action_name, QWidgetAction* widget_action) {
|
||||
const int start = action_name.indexOf('[');
|
||||
const int end = action_name.indexOf(']');
|
||||
|
||||
if (start != -1 && end != -1 && end == action_name.length() - 1) {
|
||||
const QStringList menu_action_names = action_name.chopped(1).right(end - start - 1).split(QL1C(';'));
|
||||
auto tool_btn = qobject_cast<QToolButton*>(widget_action->defaultWidget());
|
||||
|
||||
for (QAction* action : tool_btn->menu()->actions()) {
|
||||
if (menu_action_names.contains(action->objectName())) {
|
||||
// tool_btn->setDefaultAction(action);
|
||||
action->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList MessagesToolBar::defaultActions() const {
|
||||
return QString(GUI::MessagesToolbarDefaultButtonsDef)
|
||||
.split(QL1C(','),
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include "gui/reusable/searchlineedit.h"
|
||||
#include "gui/toolbars/basetoolbar.h"
|
||||
|
||||
class QWidgetAction;
|
||||
class QToolButton;
|
||||
class QMenu;
|
||||
class QTimer;
|
||||
|
||||
|
@ -17,11 +15,6 @@ class MessagesToolBar : public BaseToolBar {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class SearchFields {
|
||||
SearchTitleOnly = 1,
|
||||
SearchAll = 2
|
||||
};
|
||||
|
||||
explicit MessagesToolBar(const QString& title, QWidget* parent = nullptr);
|
||||
|
||||
virtual QList<QAction*> availableActions() const;
|
||||
|
@ -48,23 +41,17 @@ class MessagesToolBar : public BaseToolBar {
|
|||
|
||||
private:
|
||||
void initializeSearchBox();
|
||||
void addActionToMenu(QMenu* menu,
|
||||
const QIcon& icon,
|
||||
const QString& title,
|
||||
const QVariant& value,
|
||||
const QString& name);
|
||||
void initializeHighlighter();
|
||||
void activateAction(const QString& action_name, QWidgetAction* widget_action);
|
||||
void saveToolButtonSelection(const QString& button_name, const QList<QAction*>& actions) const;
|
||||
void drawNumberOfCriterias(QToolButton* btn, int count);
|
||||
|
||||
private:
|
||||
QWidgetAction* m_actionMessageHighlighter;
|
||||
QWidgetAction* m_actionMessageFilter;
|
||||
QToolButton* m_btnMessageHighlighter;
|
||||
QToolButton* m_btnMessageFilter;
|
||||
QMenu* m_menuMessageHighlighter;
|
||||
|
||||
QWidgetAction* m_actionMessageFilter;
|
||||
QToolButton* m_btnMessageFilter;
|
||||
QMenu* m_menuMessageFilter;
|
||||
|
||||
QWidgetAction* m_actionSearchMessages;
|
||||
SearchLineEdit* m_txtSearchMessages;
|
||||
};
|
||||
|
|
|
@ -299,7 +299,7 @@ DVALUE(int) GUI::HeightRowFeedsDef = -1;
|
|||
DKEY GUI::FeedsToolbarActions = "feeds_toolbar";
|
||||
DVALUE(char*)
|
||||
GUI::FeedsToolbarActionsDef = "m_actionUpdateAllItems,m_actionStopRunningItemsUpdate,m_actionPauseFeedFetching,m_"
|
||||
"actionMarkAllItemsRead,spacer,search";
|
||||
"actionMarkAllItemsRead,filter,spacer,search";
|
||||
|
||||
DKEY GUI::StatusbarActions = "status_bar";
|
||||
DVALUE(char*)
|
||||
|
|
Loading…
Add table
Reference in a new issue