diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG
index 06b011888..7e359ca4e 100644
--- a/resources/text/CHANGELOG
+++ b/resources/text/CHANGELOG
@@ -1,7 +1,29 @@
+
2.5.0
Added:
+ - Implemented ability to display ONLY FEEDS WITH SOME UNREAD MESSAGES. This behavior can be controlled by action in menu 'Feeds -> Show only unread feeds/categories' (issue #52). Behavior is this:
+
+ - User selects some feeds with unread messages, reads them all.
+ - User switches to another feed.
+ - Previously selected feed (now has no unread messages) is now hidden.
+
+ Note that this feature also works when you e.g. mark message(s) as read/unread or when you restore some messages from recycle bin. Also when feeds are updated, then filter is invalidated.
+
- Password in feeds & proxy & MySQL are now saved in encrypted form. This means that all passwords from RSS Guard older than 2.5.0 are lost!!! Set your passwords again. Used encryption scheme is meant to only make sure that passwords are not stored in DB/settings in plain form. It is not meant to protect your passwords in any broader way. Attacker can exploit your passwords if he really wants.
- Fancy & modern popup notifications (turned on by default).
- Enhanced information in download manager.
diff --git a/src/core/feedsmodelrootitem.h b/src/core/feedsmodelrootitem.h
index 8edd8d0ce..959c6e572 100755
--- a/src/core/feedsmodelrootitem.h
+++ b/src/core/feedsmodelrootitem.h
@@ -94,6 +94,10 @@ class FeedsModelRootItem {
// Checks whether THIS object is child (direct or indirect)
// of the given root.
bool isChildOf(FeedsModelRootItem *root) {
+ if (root == NULL) {
+ return false;
+ }
+
FeedsModelRootItem *this_item = this;
while (this_item->kind() != FeedsModelRootItem::RootItem) {
@@ -108,6 +112,15 @@ class FeedsModelRootItem {
return false;
}
+ bool isParentOf(FeedsModelRootItem *child) {
+ if (child == NULL) {
+ return false;
+ }
+ else {
+ return child->isChildOf(this);
+ }
+ }
+
// Removes all children from this item.
// NOTE: Children are NOT freed from the memory.
inline void clearChildren() {
diff --git a/src/core/feedsproxymodel.cpp b/src/core/feedsproxymodel.cpp
index 3a3eaa0c0..1c7442117 100755
--- a/src/core/feedsproxymodel.cpp
+++ b/src/core/feedsproxymodel.cpp
@@ -25,7 +25,7 @@
FeedsProxyModel::FeedsProxyModel(QObject *parent)
- : QSortFilterProxyModel(parent) {
+ : QSortFilterProxyModel(parent), m_selectedItem(NULL), m_showUnreadOnly(false) {
m_sourceModel = new FeedsModel(this);
setObjectName(QSL("FeedsProxyModel"));
@@ -34,7 +34,7 @@ FeedsProxyModel::FeedsProxyModel(QObject *parent)
setFilterCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(-1);
setFilterRole(Qt::EditRole);
- setDynamicSortFilter(true);
+ setDynamicSortFilter(false);
setSourceModel(m_sourceModel);
}
@@ -180,7 +180,53 @@ bool FeedsProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right
}
bool FeedsProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const {
- return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+ if (!m_showUnreadOnly) {
+ return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
+ }
+
+ // TODO: učechrat
+ QModelIndex idx = m_sourceModel->index(source_row, 0, source_parent);
+
+ if (!idx.isValid()) {
+ return false;
+ }
+
+ FeedsModelRootItem *item = m_sourceModel->itemForIndex(idx);
+
+ if (item->kind() == FeedsModelRootItem::RecycleBin) {
+ // Recycle bin is always displayed.
+ return true;
+ }
+
+ /*
+ if (m_selectedItem == NULL) {
+ return item->countOfUnreadMessages() > 0;
+ }
+ */
+
+ if (item->isParentOf(m_selectedItem)/* || item->isChildOf(m_selectedItem)*/ || m_selectedItem == item) {
+ // Currently selected item and all its parents and children must be displayed.
+ return true;
+ }
+ else {
+ return item->countOfUnreadMessages() > 0;
+ }
+}
+
+FeedsModelRootItem *FeedsProxyModel::selectedItem() const {
+ return m_selectedItem;
+}
+
+void FeedsProxyModel::setSelectedItem(FeedsModelRootItem *selectedItem) {
+ m_selectedItem = selectedItem;
+}
+
+bool FeedsProxyModel::showUnreadOnly() const {
+ return m_showUnreadOnly;
+}
+
+void FeedsProxyModel::setShowUnreadOnly(bool showUnreadOnly) {
+ m_showUnreadOnly = showUnreadOnly;
}
QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList &indexes) {
diff --git a/src/core/feedsproxymodel.h b/src/core/feedsproxymodel.h
index 428712bd8..84b8d4a5d 100755
--- a/src/core/feedsproxymodel.h
+++ b/src/core/feedsproxymodel.h
@@ -18,6 +18,8 @@
#ifndef FEEDSPROXYMODEL_H
#define FEEDSPROXYMODEL_H
+#include "feedsmodelrootitem.h"
+
#include
@@ -41,6 +43,12 @@ class FeedsProxyModel : public QSortFilterProxyModel {
// Maps list of indexes.
QModelIndexList mapListToSource(const QModelIndexList &indexes);
+ bool showUnreadOnly() const;
+ void setShowUnreadOnly(bool showUnreadOnly);
+
+ FeedsModelRootItem *selectedItem() const;
+ void setSelectedItem(FeedsModelRootItem *selectedItem);
+
public slots:
void invalidateFilter();
@@ -52,6 +60,9 @@ class FeedsProxyModel : public QSortFilterProxyModel {
private:
// Source model pointer.
FeedsModel *m_sourceModel;
+
+ FeedsModelRootItem *m_selectedItem;
+ bool m_showUnreadOnly;
};
#endif // FEEDSPROXYMODEL_H
diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp
index 9df1aa4b7..85f842baf 100755
--- a/src/gui/dialogs/formmain.cpp
+++ b/src/gui/dialogs/formmain.cpp
@@ -276,6 +276,7 @@ void FormMain::setupIcons() {
m_ui->m_actionSelectPreviousFeedCategory->setIcon(icon_theme_factory->fromTheme(QSL("go-up")));
m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-down")));
m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-up")));
+ m_ui->m_actionShowOnlyUnreadFeeds->setIcon(icon_theme_factory->fromTheme(QSL("mail-mark-unread")));
// Setup icons for underlying components: opened web browsers...
foreach (WebBrowser *browser, WebBrowser::runningWebBrowsers()) {
diff --git a/src/gui/dialogs/formmain.ui b/src/gui/dialogs/formmain.ui
index 93016d524..d7393efcc 100755
--- a/src/gui/dialogs/formmain.ui
+++ b/src/gui/dialogs/formmain.ui
@@ -144,6 +144,8 @@
+
+
@@ -649,6 +651,17 @@
Ctrl+Shift+Del
+
+
+ true
+
+
+ Show only unread feeds/categories
+
+
+ Ctrl+Shift+U
+
+
diff --git a/src/gui/feedmessageviewer.cpp b/src/gui/feedmessageviewer.cpp
index f9731854b..2da799db3 100755
--- a/src/gui/feedmessageviewer.cpp
+++ b/src/gui/feedmessageviewer.cpp
@@ -276,6 +276,10 @@ void FeedMessageViewer::switchFeedComponentVisibility() {
m_feedsWidget->setVisible(!m_feedsWidget->isVisible());
}
+void FeedMessageViewer::toggleShowOnlyUnreadFeeds() {
+ m_feedsView->invalidateReadFeedsFilter(true, qobject_cast(sender())->isChecked());
+}
+
void FeedMessageViewer::updateMessageButtonsAvailability() {
bool one_message_selected = m_messagesView->selectionModel()->selectedRows().size() == 1;
bool atleast_one_message_selected = !m_messagesView->selectionModel()->selectedRows().isEmpty();
@@ -423,6 +427,8 @@ void FeedMessageViewer::createConnections() {
SIGNAL(triggered()), m_messagesView, SLOT(selectPreviousItem()));
connect(form_main->m_ui->m_actionSwitchMessageListOrientation, SIGNAL(triggered()),
this, SLOT(switchMessageSplitterOrientation()));
+ connect(form_main->m_ui->m_actionShowOnlyUnreadFeeds, SIGNAL(toggled(bool)),
+ this, SLOT(toggleShowOnlyUnreadFeeds()));
}
void FeedMessageViewer::initialize() {
diff --git a/src/gui/feedmessageviewer.h b/src/gui/feedmessageviewer.h
index 67369f24d..8f6067eaf 100644
--- a/src/gui/feedmessageviewer.h
+++ b/src/gui/feedmessageviewer.h
@@ -118,6 +118,8 @@ class FeedMessageViewer : public TabContent {
// toolbar.
void switchFeedComponentVisibility();
+ void toggleShowOnlyUnreadFeeds();
+
void updateMessageButtonsAvailability();
void updateFeedButtonsAvailability();
diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp
index d12a5de85..581c7ef66 100755
--- a/src/gui/feedsview.cpp
+++ b/src/gui/feedsview.cpp
@@ -165,6 +165,14 @@ void FeedsView::loadExpandedStates() {
}
}
+void FeedsView::invalidateReadFeedsFilter(bool set_new_value, bool show_unread_only) {
+ if (set_new_value) {
+ m_proxyModel->setShowUnreadOnly(show_unread_only);
+ }
+
+ QTimer::singleShot(0, m_proxyModel, SLOT(invalidateFilter()));
+}
+
void FeedsView::updateAllFeeds() {
emit feedsUpdateRequested(allFeeds());
}
@@ -329,6 +337,9 @@ void FeedsView::receiveMessageCountsChange(FeedsSelection::SelectionMode mode,
else {
updateCountsOfSelectedFeeds(total_msg_count_changed);
}
+
+ // TODO: učechrat
+ invalidateReadFeedsFilter();
}
void FeedsView::editSelectedItem() {
@@ -510,6 +521,9 @@ void FeedsView::updateCountsOfParticularFeed(FeedsModelFeed *feed, bool update_t
m_sourceModel->reloadChangedLayout(QModelIndexList() << index);
}
+ // TODO: učechrat
+ invalidateReadFeedsFilter();
+
notifyWithCounts();
}
@@ -609,9 +623,14 @@ void FeedsView::setupAppearance() {
}
void FeedsView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) {
+ // TODO: učechrat
+ FeedsModelRootItem *selected_item = selectedItem();
+ m_proxyModel->setSelectedItem(selected_item);
+
QTreeView::selectionChanged(selected, deselected);
- emit feedsSelected(FeedsSelection(selectedItem()));
+ emit feedsSelected(FeedsSelection(selected_item));
+ invalidateReadFeedsFilter();
}
void FeedsView::keyPressEvent(QKeyEvent *event) {
diff --git a/src/gui/feedsview.h b/src/gui/feedsview.h
index 9faa48cce..dcb2f579c 100755
--- a/src/gui/feedsview.h
+++ b/src/gui/feedsview.h
@@ -75,6 +75,8 @@ class FeedsView : public QTreeView {
void loadExpandedStates();
public slots:
+ void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
+
// Feed updating.
void updateAllFeeds();
void updateAllFeedsOnStartup();
diff --git a/src/miscellaneous/settings.h b/src/miscellaneous/settings.h
index ad3d2adbc..8dc658462 100755
--- a/src/miscellaneous/settings.h
+++ b/src/miscellaneous/settings.h
@@ -34,6 +34,7 @@
#define DVALUE(x) const x
#define NON_CONST_DVALUE(x) x
#define SETTING(x) x, x##Def
+#define DEFAULT_VALUE(x) x##Def
#define GROUP(x) x::ID
// Feeds.