- Display "Unread" item in RSS Feed list which displays all unread items

This commit is contained in:
Christophe Dumez 2009-08-24 06:28:31 +00:00
parent a0923741a4
commit 480d00d747
8 changed files with 170 additions and 115 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

View file

@ -18,6 +18,7 @@ private:
QHash<QTreeWidgetItem*, RssFile*> mapping;
QHash<QString, QTreeWidgetItem*> feeds_items;
QTreeWidgetItem* current_feed;
QTreeWidgetItem *unread_item;
public:
FeedList(QWidget *parent, RssManager *rssmanager): QTreeWidget(parent), rssmanager(rssmanager) {
@ -28,14 +29,17 @@ public:
QTreeWidgetItem *___qtreewidgetitem = headerItem();
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
}
unread_item = new QTreeWidgetItem(this);
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
itemAdded(unread_item, rssmanager);
setCurrentItem(unread_item);
}
void itemAdded(QTreeWidgetItem *item, RssFile* file) {
mapping[item] = file;
if(file->getType() == RssFile::STREAM) {
feeds_items[file->getID()] = item;
if(topLevelItemCount() == 1)
setCurrentItem(item);
}
}
@ -45,15 +49,19 @@ public:
feeds_items.remove(file->getID());
}
bool hasFeed(QString url) {
bool hasFeed(QString url) const {
return feeds_items.contains(QUrl(url).toString());
}
QList<QTreeWidgetItem*> getAllFeedItems() {
QList<QTreeWidgetItem*> getAllFeedItems() const {
return feeds_items.values();
}
QStringList getItemPath(QTreeWidgetItem* item) {
QTreeWidgetItem* getUnreadItem() const {
return unread_item;
}
QStringList getItemPath(QTreeWidgetItem* item) const {
QStringList path;
if(item) {
if(item->parent())
@ -63,7 +71,7 @@ public:
return path;
}
QList<QTreeWidgetItem*> getAllOpenFolders(QTreeWidgetItem *parent=0) {
QList<QTreeWidgetItem*> getAllOpenFolders(QTreeWidgetItem *parent=0) const {
QList<QTreeWidgetItem*> open_folders;
int nbChildren;
if(parent)
@ -102,19 +110,15 @@ public:
return feeds;
}
RssFile* getRSSItem(QTreeWidgetItem *item) {
RssFile* getRSSItem(QTreeWidgetItem *item) const {
return mapping[item];
}
RssFile* getCurrentRSSItem() {
return mapping[current_feed];
}
RssFile::FileType getItemType(QTreeWidgetItem *item) {
RssFile::FileType getItemType(QTreeWidgetItem *item) const {
return mapping[item]->getType();
}
QString getItemID(QTreeWidgetItem *item) {
QString getItemID(QTreeWidgetItem *item) const {
return mapping[item]->getID();
}
@ -122,6 +126,10 @@ public:
return feeds_items[url];
}
RssStream* getRSSItemFromUrl(QString url) const {
return (RssStream*)getRSSItem(getTreeItemFromUrl(url));
}
QTreeWidgetItem* currentItem() const {
return current_feed;
}
@ -136,7 +144,7 @@ signals:
protected slots:
void updateCurrentFeed(QTreeWidgetItem* new_item) {
if(new_item && getItemType(new_item) == RssFile::STREAM)
if((new_item && getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
current_feed = new_item;
}
@ -146,7 +154,11 @@ protected:
if(item && getItemType(item) != RssFile::FOLDER)
event->ignore();
else {
QTreeWidget::dragMoveEvent(event);
if(selectedItems().contains(unread_item)) {
event->ignore();
} else {
QTreeWidget::dragMoveEvent(event);
}
}
}

View file

@ -73,6 +73,7 @@
<file>Icons/flags/japan.png</file>
<file>Icons/oxygen/view-refresh.png</file>
<file>Icons/oxygen/file.png</file>
<file>Icons/oxygen/mail-folder-inbox.png</file>
<file>Icons/oxygen/time.png</file>
<file>Icons/oxygen/folder-new.png</file>
<file>Icons/oxygen/edit-paste.png</file>

View file

@ -117,6 +117,22 @@ void RssFolder::refresh() {
}
}
QList<RssItem*> RssFolder::getNewsList() const {
QList<RssItem*> news;
foreach(RssFile *child, this->values()) {
news.append(child->getNewsList());
}
return news;
}
QList<RssItem*> RssFolder::getUnreadNewsList() const {
QList<RssItem*> unread_news;
foreach(RssFile *child, this->values()) {
unread_news.append(child->getUnreadNewsList());
}
return unread_news;
}
void RssFolder::refreshStream(QString url) {
qDebug("Refreshing feed: %s", url.toLocal8Bit().data());
Q_ASSERT(this->contains(url));
@ -349,9 +365,9 @@ RssStream::RssStream(RssFolder* parent, RssManager *rssmanager, bittorrent *BTSe
qDebug("Loading %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
foreach(const QVariant &var_it, old_items) {
QHash<QString, QVariant> item = var_it.toHash();
RssItem *rss_item = RssItem::fromHash(item);
RssItem *rss_item = RssItem::fromHash(this, item);
if(rss_item->isValid())
listItem << rss_item;
(*this)[rss_item->getTitle()] = rss_item;
}
}
@ -360,7 +376,7 @@ RssStream::~RssStream(){
if(refreshed) {
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QVariantList old_items;
foreach(RssItem *item, listItem) {
foreach(RssItem *item, this->values()) {
old_items << item->toHash();
}
qDebug("Saving %d old items for feed %s", old_items.size(), getName().toLocal8Bit().data());
@ -387,16 +403,12 @@ void RssStream::refresh() {
// delete all the items saved
void RssStream::removeAllItems() {
qDeleteAll(listItem);
listItem.clear();
qDeleteAll(this->values());
this->clear();
}
bool RssStream::itemAlreadyExists(QString hash) {
RssItem * item;
foreach(item, listItem) {
if(item->getHash() == hash) return true;
}
return false;
bool RssStream::itemAlreadyExists(QString name) {
return this->contains(name);
}
void RssStream::setLoading(bool val) {
@ -464,27 +476,24 @@ void RssStream::setIconPath(QString path) {
iconPath = path;
}
RssItem* RssStream::getItem(unsigned int index) const{
return listItem.at(index);
RssItem* RssStream::getItem(QString name) const{
return this->value(name);
}
unsigned int RssStream::getNbNews() const{
return listItem.size();
return this->size();
}
void RssStream::markAllAsRead() {
RssItem *item;
foreach(item, listItem){
if(!item->isRead())
item->setRead();
foreach(RssItem *item, this->values()){
item->setRead();
}
rssmanager->forwardFeedInfosChanged(url, getName(), 0);
}
unsigned int RssStream::getNbUnRead() const{
unsigned int nbUnread=0;
RssItem *item;
foreach(item, listItem){
foreach(RssItem *item, this->values()) {
if(!item->isRead())
++nbUnread;
}
@ -492,7 +501,16 @@ unsigned int RssStream::getNbUnRead() const{
}
QList<RssItem*> RssStream::getNewsList() const{
return listItem;
return this->values();
}
QList<RssItem*> RssStream::getUnreadNewsList() const {
QList<RssItem*> unread_news;
foreach(RssItem *item, this->values()) {
if(!item->isRead())
unread_news << item;
}
return unread_news;
}
// download the icon from the adress
@ -533,9 +551,9 @@ short RssStream::readDoc(const QDomDocument& doc) {
else if (property.tagName() == "image")
image = property.text();
else if(property.tagName() == "item") {
RssItem * item = new RssItem(property);
if(item->isValid() && !itemAlreadyExists(item->getHash())) {
listItem.append(item);
RssItem * item = new RssItem(this, property);
if(item->isValid() && !itemAlreadyExists(item->getTitle())) {
(*this)[item->getTitle()] = item;
// Check if the item should be automatically downloaded
FeedFilter * matching_filter = FeedFilters::getFeedFilters(url).matches(item->getTitle());
if(matching_filter != 0) {
@ -565,35 +583,21 @@ short RssStream::readDoc(const QDomDocument& doc) {
}
channel = channel.nextSibling().toElement();
}
sortList();
resizeList();
return 0;
}
void RssStream::insertSortElem(QList<RssItem*> &list, RssItem *item) {
int i = 0;
while(i < list.size() && item->getDate() < list.at(i)->getDate()) {
++i;
}
list.insert(i, item);
}
void RssStream::sortList() {
QList<RssItem*> new_list;
RssItem *item;
foreach(item, listItem) {
insertSortElem(new_list, item);
}
listItem = new_list;
}
void RssStream::resizeList() {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
unsigned int max_articles = settings.value(QString::fromUtf8("Preferences/RSS/RSSMaxArticlesPerFeed"), 100).toInt();
int excess = listItem.size() - max_articles;
if(excess <= 0) return;
for(int i=0; i<excess; ++i){
delete listItem.takeLast();
unsigned int nb_articles = this->size();
if(nb_articles > max_articles) {
QList<RssItem*> listItem = sortNewsList(this->values());
int excess = nb_articles - max_articles;
for(int i=0; i<excess; ++i){
RssItem *lastItem = listItem.takeLast();
delete this->take(lastItem->getTitle());
}
}
}

View file

@ -42,7 +42,6 @@
#include <QImage>
#include <QHash>
#include <QDateTime>
#include <QCryptographicHash>
#include "misc.h"
#include "FeedDownloader.h"
@ -94,6 +93,8 @@ public:
virtual RssFolder* getParent() const = 0;
virtual void setParent(RssFolder*) = 0;
virtual void refresh() = 0;
virtual QList<RssItem*> getNewsList() const = 0;
virtual QList<RssItem*> getUnreadNewsList() const = 0;
QStringList getPath() const {
QStringList path;
if(getParent()) {
@ -108,14 +109,13 @@ public:
class RssItem: public QObject {
Q_OBJECT
private:
RssStream* parent;
QString title;
QString torrent_url;
QString news_link;
QString description;
QDateTime date;
QString author;
QString hash;
bool is_valid;
@ -255,7 +255,7 @@ protected:
public:
// public constructor
RssItem(const QDomElement& properties): read(false) {
RssItem(RssStream* parent, const QDomElement& properties): parent(parent), read(false) {
is_valid = false;
torrent_url = QString::null;
news_link = QString::null;
@ -292,18 +292,16 @@ public:
author = property.text();
property = property.nextSibling().toElement();
}
hash = QCryptographicHash::hash(QByteArray(title.toLocal8Bit()), QCryptographicHash::Md5);
}
RssItem(QString _title, QString _torrent_url, QString _news_link, QString _description, QDateTime _date, QString _author, bool _read):
title(_title), torrent_url(_torrent_url), news_link(_news_link), description(_description), date(_date), author(_author), read(_read){
RssItem(RssStream* parent, QString _title, QString _torrent_url, QString _news_link, QString _description, QDateTime _date, QString _author, bool _read):
parent(parent), title(_title), torrent_url(_torrent_url), news_link(_news_link), description(_description), date(_date), author(_author), read(_read){
if(!title.isEmpty() && !torrent_url.isEmpty()) {
is_valid = true;
} else {
std::cerr << "ERROR: an invalid RSS item was saved" << std::endl;
is_valid = false;
}
hash = QCryptographicHash::hash(QByteArray(title.toLocal8Bit()), QCryptographicHash::Md5);
}
~RssItem(){
@ -321,12 +319,16 @@ public:
return item;
}
static RssItem* fromHash(QHash<QString, QVariant> h) {
RssItem * item = new RssItem(h["title"].toString(), h["torrent_url"].toString(), h["news_link"].toString(),
static RssItem* fromHash(RssStream* parent, QHash<QString, QVariant> h) {
RssItem * item = new RssItem(parent, h["title"].toString(), h["torrent_url"].toString(), h["news_link"].toString(),
h["description"].toString(), h["date"].toDateTime(), h["author"].toString(), h["read"].toBool());
return item;
}
RssStream* getParent() const {
return parent;
}
bool isValid() const {
return is_valid;
}
@ -343,10 +345,6 @@ public:
return torrent_url;
}
QString getHash() const {
return hash;
}
QString getLink() const {
return news_link;
}
@ -371,7 +369,7 @@ public:
};
// Rss stream, loaded form an xml file
class RssStream: public RssFile {
class RssStream: public RssFile, public QHash<QString, RssItem*> {
Q_OBJECT
private:
@ -386,7 +384,6 @@ private:
QString alias;
QString filePath;
QString iconPath;
QList<RssItem*> listItem;
bool read;
bool refreshed;
bool downloadFailure;
@ -419,17 +416,16 @@ public:
QString getIconPath() const;
bool hasCustomIcon() const;
void setIconPath(QString path);
RssItem* getItem(unsigned int index) const;
RssItem* getItem(QString name) const;
unsigned int getNbNews() const;
void markAllAsRead();
unsigned int getNbUnRead() const;
QList<RssItem*> getNewsList() const;
QList<RssItem*> getUnreadNewsList() const;
QString getIconUrl();
private:
short readDoc(const QDomDocument& doc);
void insertSortElem(QList<RssItem*> &list, RssItem *item);
void sortList();
void resizeList();
short openRss();
};
@ -460,6 +456,8 @@ public:
QString getName() const;
QString getID() const { return name; }
bool hasChild(QString ID) { return this->contains(ID); }
QList<RssItem*> getNewsList() const;
QList<RssItem*> getUnreadNewsList() const;
public slots:
void refreshAll();
@ -498,4 +496,20 @@ public:
};
static void insertSortElem(QList<RssItem*> &list, RssItem *item) {
int i = 0;
while(i < list.size() && item->getDate() < list.at(i)->getDate()) {
++i;
}
list.insert(i, item);
}
static QList<RssItem*> sortNewsList(QList<RssItem*> news_list) {
QList<RssItem*> new_list;
foreach(RssItem *item, news_list) {
insertSortElem(new_list, item);
}
return new_list;
}
#endif

View file

@ -120,13 +120,29 @@ p, li { white-space: pre-wrap; }
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QListWidget" name="listNews">
<widget class="QTreeWidget" name="listNews">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>1</string>
</property>
</column>
<column>
<property name="text">
<string>Feed URL</string>
</property>
</column>
</widget>
<widget class="QTextBrowser" name="textBrowser"/>
</widget>

View file

@ -55,11 +55,13 @@ void RSSImp::displayRSSListMenu(const QPoint& pos){
myRSSListMenu.addAction(actionMark_items_read);
myRSSListMenu.addSeparator();
if(selectedItems.size() == 1) {
myRSSListMenu.addAction(actionRename);
myRSSListMenu.addAction(actionDelete);
myRSSListMenu.addSeparator();
if(listStreams->getItemType(selectedItems.first()) == RssFile::FOLDER)
myRSSListMenu.addAction(actionNew_folder);
if(listStreams->getRSSItem(selectedItems.first()) != rssmanager) {
myRSSListMenu.addAction(actionRename);
myRSSListMenu.addAction(actionDelete);
myRSSListMenu.addSeparator();
if(listStreams->getItemType(selectedItems.first()) == RssFile::FOLDER)
myRSSListMenu.addAction(actionNew_folder);
}
}
myRSSListMenu.addAction(actionNew_subscription);
if(listStreams->getItemType(selectedItems.first()) == RssFile::STREAM) {
@ -122,8 +124,8 @@ void RSSImp::askNewFolder() {
void RSSImp::displayOverwriteError(QString filename) {
QMessageBox::warning(this, tr("Overwrite attempt"),
tr("You cannot overwrite %1 item.", "You cannot overwrite myFolder item.").arg(filename),
QMessageBox::Ok);
tr("You cannot overwrite %1 item.", "You cannot overwrite myFolder item.").arg(filename),
QMessageBox::Ok);
}
// add a stream by a button
@ -259,18 +261,18 @@ void RSSImp::on_updateAllButton_clicked() {
}
void RSSImp::downloadTorrent() {
QList<QListWidgetItem *> selected_items = listNews->selectedItems();
foreach(const QListWidgetItem* item, selected_items) {
RssItem* news = ((RssStream*)listStreams->getCurrentRSSItem())->getItem(listNews->row(item));
QList<QTreeWidgetItem *> selected_items = listNews->selectedItems();
foreach(const QTreeWidgetItem* item, selected_items) {
RssItem* news = listStreams->getRSSItemFromUrl(item->text(1))->getItem(item->text(0));
BTSession->downloadFromUrl(news->getTorrentUrl());
}
}
// open the url of the news in a browser
void RSSImp::openNewsUrl() {
QList<QListWidgetItem *> selected_items = listNews->selectedItems();
foreach(const QListWidgetItem* item, selected_items) {
RssItem* news = ((RssStream*)listStreams->getCurrentRSSItem())->getItem(listNews->row(item));
QList<QTreeWidgetItem *> selected_items = listNews->selectedItems();
foreach(const QTreeWidgetItem* item, selected_items) {
RssItem* news = listStreams->getRSSItemFromUrl(item->text(1))->getItem(item->text(0));
QString link = news->getLink();
if(!link.isEmpty())
QDesktopServices::openUrl(QUrl(link));
@ -382,27 +384,29 @@ void RSSImp::refreshNewsList(QTreeWidgetItem* item) {
listNews->clear();
return;
}
if(listStreams->getItemType(item) != RssFile::STREAM) {
listNews->clear();
return;
}
RssStream *stream = (RssStream*)listStreams->getRSSItem(item);
RssFile *rss_item = listStreams->getRSSItem(item);
qDebug("Getting the list of news");
QList<RssItem*> news = stream->getNewsList();
QList<RssItem*> news;
if(rss_item == rssmanager)
news = sortNewsList(rss_item->getUnreadNewsList());
else
news = sortNewsList(rss_item->getNewsList());
// Clear the list first
textBrowser->clear();
listNews->clear();
qDebug("Got the list of news");
foreach(RssItem* article, news){
QListWidgetItem* it = new QListWidgetItem(article->getTitle(), listNews);
QTreeWidgetItem* it = new QTreeWidgetItem(listNews);
it->setText(0, article->getTitle());
it->setText(1, article->getParent()->getUrl());
if(article->isRead()){
it->setData(Qt::ForegroundRole, QVariant(QColor("grey")));
it->setData(Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere.png")));
it->setData(0, Qt::ForegroundRole, QVariant(QColor("grey")));
it->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere.png")));
}else{
it->setData(Qt::ForegroundRole, QVariant(QColor("blue")));
it->setData(Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere2.png")));
it->setData(0, Qt::ForegroundRole, QVariant(QColor("blue")));
it->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere2.png")));
}
}
qDebug("Added all news to the GUI");
@ -410,10 +414,10 @@ void RSSImp::refreshNewsList(QTreeWidgetItem* item) {
}
// display a news
void RSSImp::refreshTextBrowser(QListWidgetItem *item) {
void RSSImp::refreshTextBrowser(QTreeWidgetItem *item) {
if(!item) return;
RssStream *stream = (RssStream*)listStreams->getCurrentRSSItem();
RssItem* article = stream->getItem(listNews->row(item));
RssStream *stream = listStreams->getRSSItemFromUrl(item->text(1));
RssItem* article = stream->getItem(item->text(0));
QString html;
html += "<div style='border: 2px solid red; margin-left: 5px; margin-right: 5px; margin-bottom: 5px;'>";
html += "<div style='background-color: #678db2; font-weight: bold; color: #fff;'>"+article->getTitle() + "</div>";
@ -427,10 +431,11 @@ void RSSImp::refreshTextBrowser(QListWidgetItem *item) {
html += "<divstyle='margin-left: 5px; margin-right: 5px;'>"+article->getDescription()+"</div>";
textBrowser->setHtml(html);
article->setRead();
item->setData(Qt::ForegroundRole, QVariant(QColor("grey")));
item->setData(Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere.png")));
item->setData(0, Qt::ForegroundRole, QVariant(QColor("grey")));
item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/sphere.png")));
// Decrement feed nb unread news
updateItemInfos(listStreams->currentFeed());
updateItemInfos(listStreams->getUnreadItem());
updateItemInfos(listStreams->getTreeItemFromUrl(item->text(1)));
}
void RSSImp::saveSlidersPosition() {
@ -493,8 +498,11 @@ RSSImp::RSSImp(bittorrent *BTSession) : QWidget(), BTSession(BTSession){
listStreams = new FeedList(splitter_h, rssmanager);
splitter_h->insertWidget(0, listStreams);
listNews->hideColumn(1);
fillFeedsList();
refreshNewsList(listStreams->currentItem());
loadFoldersOpenState();
connect(rssmanager, SIGNAL(feedInfosChanged(QString, QString, unsigned int)), this, SLOT(updateFeedInfos(QString, QString, unsigned int)));
connect(rssmanager, SIGNAL(feedIconChanged(QString, QString)), this, SLOT(updateFeedIcon(QString, QString)));
@ -520,8 +528,8 @@ RSSImp::RSSImp(bittorrent *BTSession) : QWidget(), BTSession(BTSession){
connect(listStreams, SIGNAL(foldersAltered(QList<QTreeWidgetItem*>)), this, SLOT(updateItemsInfos(QList<QTreeWidgetItem*>)));
connect(listStreams, SIGNAL(overwriteAttempt(QString)), this, SLOT(displayOverwriteError(QString)));
connect(listNews, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(refreshTextBrowser(QListWidgetItem *)));
connect(listNews, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(downloadTorrent()));
connect(listNews, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(refreshTextBrowser(QTreeWidgetItem *)));
connect(listNews, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(downloadTorrent()));
// Refresh all feeds
rssmanager->refreshAll();

View file

@ -60,7 +60,7 @@ protected slots:
void refreshSelectedItems();
void copySelectedFeedsURL();
void refreshNewsList(QTreeWidgetItem* item);
void refreshTextBrowser(QListWidgetItem *);
void refreshTextBrowser(QTreeWidgetItem *);
void updateFeedIcon(QString url, QString icon_path);
void updateFeedInfos(QString url, QString aliasOrUrl, unsigned int nbUnread);
void updateItemsInfos(QList<QTreeWidgetItem*> items);