diff --git a/src/librssguard/core/feedsproxymodel.cpp b/src/librssguard/core/feedsproxymodel.cpp index 4ce11595f..788686cde 100644 --- a/src/librssguard/core/feedsproxymodel.cpp +++ b/src/librssguard/core/feedsproxymodel.cpp @@ -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,22 +471,26 @@ bool FeedsProxyModel::filterAcceptsRowInternal(int source_row, const QModelIndex return true; } - if (!m_showUnreadOnly) { - // Take only regexp filtering into account. - 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)); + 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); + } + } + } + + // 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 { @@ -500,3 +547,9 @@ QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList& indexes) return source_indexes; } + +void FeedsProxyModel::setFeedListFilter(FeedListFilter filter) { + m_filter = filter; + + invalidateRowsFilter(); +} diff --git a/src/librssguard/core/feedsproxymodel.h b/src/librssguard/core/feedsproxymodel.h index deac1b8db..d9b018ef7 100644 --- a/src/librssguard/core/feedsproxymodel.h +++ b/src/librssguard/core/feedsproxymodel.h @@ -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 m_priorities; QList> m_hiddenIndices; + + FeedListFilter m_filter; + QMap> m_filters; + QList m_filterKeys; }; +Q_DECLARE_METATYPE(FeedsProxyModel::FeedListFilter) + #endif // FEEDSPROXYMODEL_H diff --git a/src/librssguard/gui/feedmessageviewer.cpp b/src/librssguard/gui/feedmessageviewer.cpp index 323807a38..3a9843ac4 100644 --- a/src/librssguard/gui/feedmessageviewer.cpp +++ b/src/librssguard/gui/feedmessageviewer.cpp @@ -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(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); diff --git a/src/librssguard/gui/feedmessageviewer.h b/src/librssguard/gui/feedmessageviewer.h index e521cf869..83c12b5ac 100644 --- a/src/librssguard/gui/feedmessageviewer.h +++ b/src/librssguard/gui/feedmessageviewer.h @@ -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(); diff --git a/src/librssguard/gui/feedsview.cpp b/src/librssguard/gui/feedsview.cpp index 296b688e0..fa05ed60e 100644 --- a/src/librssguard/gui/feedsview.cpp +++ b/src/librssguard/gui/feedsview.cpp @@ -407,6 +407,10 @@ void FeedsView::editRecursiveFeeds() { } } +void FeedsView::changeFilter(FeedsProxyModel::FeedListFilter filter) { + m_proxyModel->setFeedListFilter(filter); +} + void FeedsView::editSelectedItems() { editItems(selectedItems()); } @@ -787,10 +791,10 @@ 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 - : -1); + m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? FDS_MODEL_TITLE_INDEX + : -1); if (phrase.isEmpty()) { loadAllExpandStates(); diff --git a/src/librssguard/gui/feedsview.h b/src/librssguard/gui/feedsview.h index c91f3bb0f..0146c512a 100644 --- a/src/librssguard/gui/feedsview.h +++ b/src/librssguard/gui/feedsview.h @@ -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, diff --git a/src/librssguard/gui/messagesview.cpp b/src/librssguard/gui/messagesview.cpp index 6bffe750a..cbf8096e3 100644 --- a/src/librssguard/gui/messagesview.cpp +++ b/src/librssguard/gui/messagesview.cpp @@ -871,10 +871,10 @@ 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 - : -1); + m_proxyModel->setFilterKeyColumn(where_search == BaseToolBar::SearchFields::SearchTitleOnly ? MSG_DB_TITLE_INDEX + : -1); if (selectionModel()->selectedRows().isEmpty()) { emit currentMessageRemoved(m_sourceModel->loadedItem()); diff --git a/src/librssguard/gui/toolbars/basetoolbar.cpp b/src/librssguard/gui/toolbars/basetoolbar.cpp index 94eeb46ab..e540f0acb 100644 --- a/src/librssguard/gui/toolbars/basetoolbar.cpp +++ b/src/librssguard/gui/toolbars/basetoolbar.cpp @@ -2,8 +2,12 @@ #include "gui/toolbars/basetoolbar.h" +#include "3rd-party/boolinq/boolinq.h" #include "definitions/definitions.h" +#include "miscellaneous/settings.h" +#include +#include #include BaseToolBar::BaseToolBar(const QString& title, QWidget* parent) : QToolBar(title, parent) { @@ -31,3 +35,69 @@ QAction* BaseBar::findMatchingAction(const QString& action, const QListaddAction(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(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& 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); +} diff --git a/src/librssguard/gui/toolbars/basetoolbar.h b/src/librssguard/gui/toolbars/basetoolbar.h index d7bd7d28e..288dd7565 100644 --- a/src/librssguard/gui/toolbars/basetoolbar.h +++ b/src/librssguard/gui/toolbars/basetoolbar.h @@ -3,7 +3,10 @@ #ifndef TOOLBAR_H #define TOOLBAR_H +#include #include +#include +#include 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& 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 diff --git a/src/librssguard/gui/toolbars/feedstoolbar.cpp b/src/librssguard/gui/toolbars/feedstoolbar.cpp index 7b2c98cc2..4ceef7033 100644 --- a/src/librssguard/gui/toolbars/feedstoolbar.cpp +++ b/src/librssguard/gui/toolbars/feedstoolbar.cpp @@ -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 FeedsToolBar::availableActions() const { QList available_actions = qApp->userActions(); available_actions.append(m_actionSearchMessages); + available_actions.append(m_actionMessageFilter); return available_actions; } @@ -65,6 +70,11 @@ QList 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& actions, bool init } } +inline FeedsProxyModel::FeedListFilter operator|(FeedsProxyModel::FeedListFilter a, FeedsProxyModel::FeedListFilter b) { + return static_cast(static_cast(a) | static_cast(b)); +} + +void FeedsToolBar::handleMessageFilterChange(QAction* action) { + FeedsProxyModel::FeedListFilter task = action->data().value(); + std::list 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(); + } + } + + 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, 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); + }); +} diff --git a/src/librssguard/gui/toolbars/feedstoolbar.h b/src/librssguard/gui/toolbars/feedstoolbar.h index 772f8d606..00162e7dc 100644 --- a/src/librssguard/gui/toolbars/feedstoolbar.h +++ b/src/librssguard/gui/toolbars/feedstoolbar.h @@ -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 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; }; diff --git a/src/librssguard/gui/toolbars/messagestoolbar.cpp b/src/librssguard/gui/toolbars/messagestoolbar.cpp index 075fbda1c..9eda8768a 100644 --- a/src/librssguard/gui/toolbars/messagestoolbar.cpp +++ b/src/librssguard/gui/toolbars/messagestoolbar.cpp @@ -11,10 +11,7 @@ #include #include -#include #include -#include -#include 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, checked_tasks_std)); + saveToolButtonSelection(QSL(HIGHLIGHTER_ACTION_NAME), + GUI::MessagesToolbarDefaultButtons, + FROM_STD_LIST(QList, 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(); std::list 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, checked_tasks_std)); + saveToolButtonSelection(QSL(FILTER_ACTION_NAME), + GUI::MessagesToolbarDefaultButtons, + FROM_STD_LIST(QList, 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& 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(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(','), diff --git a/src/librssguard/gui/toolbars/messagestoolbar.h b/src/librssguard/gui/toolbars/messagestoolbar.h index f567d9e36..12ba4e66b 100644 --- a/src/librssguard/gui/toolbars/messagestoolbar.h +++ b/src/librssguard/gui/toolbars/messagestoolbar.h @@ -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 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& 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; }; diff --git a/src/librssguard/miscellaneous/settings.cpp b/src/librssguard/miscellaneous/settings.cpp index 9a6bc64d5..c493c62de 100644 --- a/src/librssguard/miscellaneous/settings.cpp +++ b/src/librssguard/miscellaneous/settings.cpp @@ -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*)