Experimentally fixed #52.

This commit is contained in:
Martin Rotter 2015-07-16 09:18:07 +02:00
parent 41c239ff03
commit 1e12713a1a
11 changed files with 140 additions and 4 deletions

View File

@ -1,7 +1,29 @@
<body> <body>
<style>
body {
font-size: 150%;
}
ul {
list-style-type: square;
}
ul > li {
margin-top: 3px;
margin-bottom: 3px;
}
</style>
<center><h2>2.5.0</h2></center> <center><h2>2.5.0</h2></center>
Added: Added:
<ul> <ul>
<li>Implemented ability to display <b>ONLY FEEDS WITH SOME UNREAD MESSAGES</b>. This behavior can be controlled by action in menu 'Feeds -> Show only unread feeds/categories' (issue #52). Behavior is this:
<ol>
<li>User selects some feeds with unread messages, reads them all.</li>
<li>User switches to another feed.</li>
<li>Previously selected feed (now has no unread messages) is now hidden.</li>
</ol>
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.
</li>
<li><b>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.</b> 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.</li> <li><b>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.</b> 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.</li>
<li><b>Fancy & modern popup notifications</b> (turned on by default).</li> <li><b>Fancy & modern popup notifications</b> (turned on by default).</li>
<li>Enhanced information in download manager.</li> <li>Enhanced information in download manager.</li>

View File

@ -94,6 +94,10 @@ class FeedsModelRootItem {
// Checks whether THIS object is child (direct or indirect) // Checks whether THIS object is child (direct or indirect)
// of the given root. // of the given root.
bool isChildOf(FeedsModelRootItem *root) { bool isChildOf(FeedsModelRootItem *root) {
if (root == NULL) {
return false;
}
FeedsModelRootItem *this_item = this; FeedsModelRootItem *this_item = this;
while (this_item->kind() != FeedsModelRootItem::RootItem) { while (this_item->kind() != FeedsModelRootItem::RootItem) {
@ -108,6 +112,15 @@ class FeedsModelRootItem {
return false; return false;
} }
bool isParentOf(FeedsModelRootItem *child) {
if (child == NULL) {
return false;
}
else {
return child->isChildOf(this);
}
}
// Removes all children from this item. // Removes all children from this item.
// NOTE: Children are NOT freed from the memory. // NOTE: Children are NOT freed from the memory.
inline void clearChildren() { inline void clearChildren() {

View File

@ -25,7 +25,7 @@
FeedsProxyModel::FeedsProxyModel(QObject *parent) FeedsProxyModel::FeedsProxyModel(QObject *parent)
: QSortFilterProxyModel(parent) { : QSortFilterProxyModel(parent), m_selectedItem(NULL), m_showUnreadOnly(false) {
m_sourceModel = new FeedsModel(this); m_sourceModel = new FeedsModel(this);
setObjectName(QSL("FeedsProxyModel")); setObjectName(QSL("FeedsProxyModel"));
@ -34,7 +34,7 @@ FeedsProxyModel::FeedsProxyModel(QObject *parent)
setFilterCaseSensitivity(Qt::CaseInsensitive); setFilterCaseSensitivity(Qt::CaseInsensitive);
setFilterKeyColumn(-1); setFilterKeyColumn(-1);
setFilterRole(Qt::EditRole); setFilterRole(Qt::EditRole);
setDynamicSortFilter(true); setDynamicSortFilter(false);
setSourceModel(m_sourceModel); 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 { 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) { QModelIndexList FeedsProxyModel::mapListToSource(const QModelIndexList &indexes) {

View File

@ -18,6 +18,8 @@
#ifndef FEEDSPROXYMODEL_H #ifndef FEEDSPROXYMODEL_H
#define FEEDSPROXYMODEL_H #define FEEDSPROXYMODEL_H
#include "feedsmodelrootitem.h"
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
@ -41,6 +43,12 @@ class FeedsProxyModel : public QSortFilterProxyModel {
// Maps list of indexes. // Maps list of indexes.
QModelIndexList mapListToSource(const QModelIndexList &indexes); QModelIndexList mapListToSource(const QModelIndexList &indexes);
bool showUnreadOnly() const;
void setShowUnreadOnly(bool showUnreadOnly);
FeedsModelRootItem *selectedItem() const;
void setSelectedItem(FeedsModelRootItem *selectedItem);
public slots: public slots:
void invalidateFilter(); void invalidateFilter();
@ -52,6 +60,9 @@ class FeedsProxyModel : public QSortFilterProxyModel {
private: private:
// Source model pointer. // Source model pointer.
FeedsModel *m_sourceModel; FeedsModel *m_sourceModel;
FeedsModelRootItem *m_selectedItem;
bool m_showUnreadOnly;
}; };
#endif // FEEDSPROXYMODEL_H #endif // FEEDSPROXYMODEL_H

View File

@ -276,6 +276,7 @@ void FormMain::setupIcons() {
m_ui->m_actionSelectPreviousFeedCategory->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); 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_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-down")));
m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme(QSL("go-up"))); 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... // Setup icons for underlying components: opened web browsers...
foreach (WebBrowser *browser, WebBrowser::runningWebBrowsers()) { foreach (WebBrowser *browser, WebBrowser::runningWebBrowsers()) {

View File

@ -144,6 +144,8 @@
<addaction name="m_actionEditSelectedFeedCategory"/> <addaction name="m_actionEditSelectedFeedCategory"/>
<addaction name="m_actionDeleteSelectedFeedCategory"/> <addaction name="m_actionDeleteSelectedFeedCategory"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="m_actionShowOnlyUnreadFeeds"/>
<addaction name="separator"/>
<addaction name="m_actionSelectNextFeedCategory"/> <addaction name="m_actionSelectNextFeedCategory"/>
<addaction name="m_actionSelectPreviousFeedCategory"/> <addaction name="m_actionSelectPreviousFeedCategory"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -649,6 +651,17 @@
<string notr="true">Ctrl+Shift+Del</string> <string notr="true">Ctrl+Shift+Del</string>
</property> </property>
</action> </action>
<action name="m_actionShowOnlyUnreadFeeds">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show only unread feeds/categories</string>
</property>
<property name="shortcut">
<string>Ctrl+Shift+U</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -276,6 +276,10 @@ void FeedMessageViewer::switchFeedComponentVisibility() {
m_feedsWidget->setVisible(!m_feedsWidget->isVisible()); m_feedsWidget->setVisible(!m_feedsWidget->isVisible());
} }
void FeedMessageViewer::toggleShowOnlyUnreadFeeds() {
m_feedsView->invalidateReadFeedsFilter(true, qobject_cast<QAction*>(sender())->isChecked());
}
void FeedMessageViewer::updateMessageButtonsAvailability() { void FeedMessageViewer::updateMessageButtonsAvailability() {
bool one_message_selected = m_messagesView->selectionModel()->selectedRows().size() == 1; bool one_message_selected = m_messagesView->selectionModel()->selectedRows().size() == 1;
bool atleast_one_message_selected = !m_messagesView->selectionModel()->selectedRows().isEmpty(); bool atleast_one_message_selected = !m_messagesView->selectionModel()->selectedRows().isEmpty();
@ -423,6 +427,8 @@ void FeedMessageViewer::createConnections() {
SIGNAL(triggered()), m_messagesView, SLOT(selectPreviousItem())); SIGNAL(triggered()), m_messagesView, SLOT(selectPreviousItem()));
connect(form_main->m_ui->m_actionSwitchMessageListOrientation, SIGNAL(triggered()), connect(form_main->m_ui->m_actionSwitchMessageListOrientation, SIGNAL(triggered()),
this, SLOT(switchMessageSplitterOrientation())); this, SLOT(switchMessageSplitterOrientation()));
connect(form_main->m_ui->m_actionShowOnlyUnreadFeeds, SIGNAL(toggled(bool)),
this, SLOT(toggleShowOnlyUnreadFeeds()));
} }
void FeedMessageViewer::initialize() { void FeedMessageViewer::initialize() {

View File

@ -118,6 +118,8 @@ class FeedMessageViewer : public TabContent {
// toolbar. // toolbar.
void switchFeedComponentVisibility(); void switchFeedComponentVisibility();
void toggleShowOnlyUnreadFeeds();
void updateMessageButtonsAvailability(); void updateMessageButtonsAvailability();
void updateFeedButtonsAvailability(); void updateFeedButtonsAvailability();

View File

@ -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() { void FeedsView::updateAllFeeds() {
emit feedsUpdateRequested(allFeeds()); emit feedsUpdateRequested(allFeeds());
} }
@ -329,6 +337,9 @@ void FeedsView::receiveMessageCountsChange(FeedsSelection::SelectionMode mode,
else { else {
updateCountsOfSelectedFeeds(total_msg_count_changed); updateCountsOfSelectedFeeds(total_msg_count_changed);
} }
// TODO: učechrat
invalidateReadFeedsFilter();
} }
void FeedsView::editSelectedItem() { void FeedsView::editSelectedItem() {
@ -510,6 +521,9 @@ void FeedsView::updateCountsOfParticularFeed(FeedsModelFeed *feed, bool update_t
m_sourceModel->reloadChangedLayout(QModelIndexList() << index); m_sourceModel->reloadChangedLayout(QModelIndexList() << index);
} }
// TODO: učechrat
invalidateReadFeedsFilter();
notifyWithCounts(); notifyWithCounts();
} }
@ -609,9 +623,14 @@ void FeedsView::setupAppearance() {
} }
void FeedsView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { 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); QTreeView::selectionChanged(selected, deselected);
emit feedsSelected(FeedsSelection(selectedItem())); emit feedsSelected(FeedsSelection(selected_item));
invalidateReadFeedsFilter();
} }
void FeedsView::keyPressEvent(QKeyEvent *event) { void FeedsView::keyPressEvent(QKeyEvent *event) {

View File

@ -75,6 +75,8 @@ class FeedsView : public QTreeView {
void loadExpandedStates(); void loadExpandedStates();
public slots: public slots:
void invalidateReadFeedsFilter(bool set_new_value = false, bool show_unread_only = false);
// Feed updating. // Feed updating.
void updateAllFeeds(); void updateAllFeeds();
void updateAllFeedsOnStartup(); void updateAllFeedsOnStartup();

View File

@ -34,6 +34,7 @@
#define DVALUE(x) const x #define DVALUE(x) const x
#define NON_CONST_DVALUE(x) x #define NON_CONST_DVALUE(x) x
#define SETTING(x) x, x##Def #define SETTING(x) x, x##Def
#define DEFAULT_VALUE(x) x##Def
#define GROUP(x) x::ID #define GROUP(x) x::ID
// Feeds. // Feeds.